use crate::commands::errors::UserError;
use crate::{Entity, SystemName, cli_utils, http_utils};
use handled::Handle;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
fn parse_id_or_exit_generic<T, E>(id_str: &str, id_type_name: &str) -> T
where
T: FromStr<Err = E>,
E: Handle<UserError> + std::fmt::Display,
{
id_str.parse().unwrap_or_else(|e: E| {
if let Some(user_error) = e.handle() {
if let Some(ref hint) = user_error.usage_hint {
cli_utils::exit_with_usage_error(&user_error.message, hint);
} else {
cli_utils::exit_with_error(&user_error.message);
}
} else {
cli_utils::exit_with_error(&format!("Invalid {}: {}", id_type_name, e));
}
})
}
pub fn parse_entity_id_or_exit(entity_id_str: &str) -> Entity {
parse_id_or_exit_generic(entity_id_str, "entity ID")
}
pub fn parse_system_name_or_exit(name_str: &str) -> SystemName {
name_str.parse().unwrap_or_else(|e| {
cli_utils::exit_with_error(&format!("{}", e));
})
}
pub fn require_args_or_exit(args: &[String], required_count: usize, command: &str, usage: &str) {
if args.len() < required_count {
cli_utils::exit_with_usage_error(
&format!("{} command requires more arguments", command),
usage,
);
}
}
pub fn validate_args_count_or_exit(
args: &[String],
min_count: usize,
max_count: usize,
command: &str,
usage: &str,
) {
if args.len() < min_count {
cli_utils::exit_with_usage_error(
&format!("{} command requires more arguments", command),
usage,
);
}
if args.len() > max_count {
cli_utils::exit_with_usage_error(
&format!("{} command has too many arguments", command),
usage,
);
}
}
macro_rules! dispatch_command {
($command_name:expr, $usage:expr, $args:expr, $client:expr, $output_format:expr, {
$($subcommand:expr => $handler:expr),* $(,)?
}) => {
if $args.is_empty() {
crate::cli_utils::exit_with_usage_error(
&format!("{} command requires a subcommand", $command_name),
$usage,
);
}
match $args[0].as_str() {
$(
$subcommand => $handler($args, $client, $output_format).await,
)*
_ => {
let available_subcommands = vec![$($subcommand),*];
crate::cli_utils::exit_with_error(&format!(
"Unknown {} subcommand '{}'. Available subcommands: {}",
$command_name,
$args[0],
available_subcommands.join(", ")
));
}
}
};
}
pub(crate) use dispatch_command;
pub struct HttpOperations;
impl HttpOperations {
pub async fn post<Req, Resp>(
client: &http_utils::StigmergyClient,
path: &str,
request: &Req,
context: &str,
) -> Resp
where
Req: Serialize,
Resp: for<'de> Deserialize<'de>,
{
http_utils::execute_or_exit(
|| client.post::<Req, Resp>(path, request),
&format!("Failed to {}", context),
)
.await
}
pub async fn get<Resp>(client: &http_utils::StigmergyClient, path: &str, context: &str) -> Resp
where
Resp: for<'de> Deserialize<'de>,
{
http_utils::execute_or_exit(
|| client.get::<Resp>(path),
&format!("Failed to {}", context),
)
.await
}
pub async fn put<Req, Resp>(
client: &http_utils::StigmergyClient,
path: &str,
request: &Req,
context: &str,
) -> Resp
where
Req: Serialize,
Resp: for<'de> Deserialize<'de>,
{
http_utils::execute_or_exit(
|| client.put::<Req, Resp>(path, request),
&format!("Failed to {}", context),
)
.await
}
pub async fn delete(client: &http_utils::StigmergyClient, path: &str, context: &str) {
http_utils::execute_or_exit(|| client.delete(path), &format!("Failed to {}", context))
.await;
}
}
pub struct ApiUrlBuilder;
impl ApiUrlBuilder {
const API_V1_PREFIX: &'static str = "/api/v1";
pub fn entity(id: Option<&str>) -> String {
match id {
Some(id) => format!("{}/entity/{}", Self::API_V1_PREFIX, id),
None => format!("{}/entity", Self::API_V1_PREFIX),
}
}
pub fn system(id: Option<&str>) -> String {
match id {
Some(id) => format!("{}/system/{}", Self::API_V1_PREFIX, id),
None => format!("{}/system", Self::API_V1_PREFIX),
}
}
pub fn component_definition(id: Option<&str>) -> String {
match id {
Some(id) => format!("{}/componentdefinition/{}", Self::API_V1_PREFIX, id),
None => format!("{}/componentdefinition", Self::API_V1_PREFIX),
}
}
pub fn component(entity_id: Option<&str>) -> String {
match entity_id {
Some(id) => format!("{}/entity/{}/component", Self::API_V1_PREFIX, id),
None => format!("{}/component", Self::API_V1_PREFIX),
}
}
pub fn system_from_markdown() -> String {
format!("{}/system/from-markdown", Self::API_V1_PREFIX)
}
pub fn build_url(base_url: &str, path: &str) -> String {
format!("{}{}", base_url, path)
}
}