boomack-cli 0.5.0

CLI client for Boomack
use std::process::exit;

use clap::{Args, Subcommand};
use serde::Serialize;
use boomack::client::slots::{
    clear_slot_request,
    SlotExportParameters,
    export_slot_request,
};
use super::{CliConfig, parse_location, run_request};

#[derive(Args, Serialize)]
pub struct SlotCommandArguments {
    #[clap(subcommand)]
    command: SlotSubCommands,
}

#[derive(Subcommand, Serialize)]
pub enum SlotSubCommands {
    #[clap(about = "Clear the slot")]
    Clear(SlotIdArguments),

    #[clap(about = "Export slot content to a path on the server")]
    Export(SlotExportArguments),
}

#[derive(Args, Serialize)]
pub struct SlotIdArguments {
    #[clap(value_name = "TARGET",
        help = "The panel and slot ID, e. g. 'panel-1/main-slot'")]
    target: Option<String>,
}

impl<'l> SlotIdArguments {
    pub fn parse_target(&'l self) -> (Option<&'l str>, &'l str) {
        let (panel_id, slot_id) = parse_location(self.target.as_deref());
        if matches!(slot_id, None) {
            eprintln!("Target location is required");
            exit(1)
        }
        (panel_id, slot_id.unwrap())
    }
}

#[derive(Args, Serialize)]
pub struct SlotExportArguments {
    #[clap(value_name = "TARGET",
        help = "The panel and slot ID, e. g. 'panel-1/main-slot'")]
    target: Option<String>,

    #[clap(short = 'p', long = "path", value_name = "SERVER_PATH",
        help = "A relative path in the filesystem of the server to export to")]
    path: Option<String>,

    #[clap(short = 'n', long = "name", value_name = "NAME",
        help = "A name for the exported HTML file; without extension")]
    filename: Option<String>,

    #[clap(short = 't', long = "theme", value_name = "THEME",
        help = "A theme to use during the export")]
    theme: Option<String>,

    #[clap(short = 'z', long = "zoom", value_name = "ZOOM",
        help = "A CSS zoom level for the HTML")]
    zoom: Option<f32>,

    #[clap(long = "no-tools",
        help = "Remove all tools from the slot's toolbar")]
    no_tools: bool,

    #[clap(long = "no-toolbar",
        help = "Remove toolbar from the slot")]
    no_toolbar: bool,
}

impl<'l> SlotExportArguments {
    pub fn parse_target(&'l self) -> (Option<&'l str>, &'l str) {
        let (panel_id, slot_id) = parse_location(self.target.as_deref());
        if matches!(slot_id, None) {
            eprintln!("Target location is required");
            exit(1)
        }
        (panel_id, slot_id.unwrap())
    }
    pub fn to_parameters(&'l self) -> SlotExportParameters<'l> {
        SlotExportParameters {
            path: self.path.as_deref(),
            name: self.filename.as_deref(),
            theme: self.theme.as_deref(),
            zoom: self.zoom,
            tools: Some(!self.no_tools),
            toolbar: Some(!self.no_toolbar),
        }
    }
}

pub fn dispatch_slot_command(cfg: &CliConfig, args: &SlotCommandArguments) -> i32 {
    let request = match &args.command {
        SlotSubCommands::Export(args) => {
            let (panel_id, slot_id) = args.parse_target();
            export_slot_request(panel_id, slot_id, args.to_parameters())
        },
        SlotSubCommands::Clear(args) => {
            let (panel_id, slot_id) = args.parse_target();
            clear_slot_request(panel_id, slot_id)
        },
    };
    run_request(cfg, request)
}