diff --git a/src/presentation/cli/bags.rs b/src/presentation/cli/bags.rs index 3656a1c..cc8cc91 100644 --- a/src/presentation/cli/bags.rs +++ b/src/presentation/cli/bags.rs @@ -1,7 +1,7 @@ use anyhow::Result; use clap::Args; -use serde_json::json; +use super::macros::{define_delete_command, define_get_command}; use super::print_json; use crate::domain::ids::{BagId, RoastId}; use crate::infrastructure::client::BrewlogClient; @@ -42,16 +42,7 @@ pub async fn list_bags(client: &BrewlogClient, command: ListBagsCommand) -> Resu print_json(&bags) } -#[derive(Debug, Args)] -pub struct GetBagCommand { - #[arg(long)] - pub id: i64, -} - -pub async fn get_bag(client: &BrewlogClient, command: GetBagCommand) -> Result<()> { - let bag = client.bags().get(BagId::new(command.id)).await?; - print_json(&bag) -} +define_get_command!(GetBagCommand, get_bag, BagId, bags); #[derive(Debug, Args)] pub struct UpdateBagCommand { @@ -83,19 +74,4 @@ pub async fn update_bag(client: &BrewlogClient, command: UpdateBagCommand) -> Re print_json(&bag) } -#[derive(Debug, Args)] -pub struct DeleteBagCommand { - #[arg(long)] - pub id: i64, -} - -pub async fn delete_bag(client: &BrewlogClient, command: DeleteBagCommand) -> Result<()> { - let id = BagId::new(command.id); - client.bags().delete(id).await?; - let response = json!({ - "status": "deleted", - "resource": "bag", - "id": id.into_inner(), - }); - print_json(&response) -} +define_delete_command!(DeleteBagCommand, delete_bag, BagId, bags, "bag"); diff --git a/src/presentation/cli/macros.rs b/src/presentation/cli/macros.rs new file mode 100644 index 0000000..70d6257 --- /dev/null +++ b/src/presentation/cli/macros.rs @@ -0,0 +1,65 @@ +/// Macro to define a "get" command struct and handler function. +/// +/// Generates a command struct with an `id: i64` field and an async function +/// that fetches the entity by ID and prints it as JSON. +/// +/// # Example +/// ```ignore +/// define_get_command!(GetRoasterCommand, get_roaster, RoasterId, roasters); +/// ``` +macro_rules! define_get_command { + ($cmd_name:ident, $fn_name:ident, $id_type:ty, $client_method:ident) => { + #[derive(Debug, clap::Args)] + pub struct $cmd_name { + #[arg(long)] + pub id: i64, + } + + pub async fn $fn_name( + client: &crate::infrastructure::client::BrewlogClient, + command: $cmd_name, + ) -> anyhow::Result<()> { + let entity = client + .$client_method() + .get(<$id_type>::new(command.id)) + .await?; + super::print_json(&entity) + } + }; +} + +/// Macro to define a "delete" command struct and handler function. +/// +/// Generates a command struct with an `id: i64` field and an async function +/// that deletes the entity by ID and prints a confirmation JSON response. +/// +/// # Example +/// ```ignore +/// define_delete_command!(DeleteRoasterCommand, delete_roaster, RoasterId, roasters, "roaster"); +/// ``` +macro_rules! define_delete_command { + ($cmd_name:ident, $fn_name:ident, $id_type:ty, $client_method:ident, $resource_name:literal) => { + #[derive(Debug, clap::Args)] + pub struct $cmd_name { + #[arg(long)] + pub id: i64, + } + + pub async fn $fn_name( + client: &crate::infrastructure::client::BrewlogClient, + command: $cmd_name, + ) -> anyhow::Result<()> { + let id = <$id_type>::new(command.id); + client.$client_method().delete(id).await?; + let response = serde_json::json!({ + "status": "deleted", + "resource": $resource_name, + "id": id.into_inner(), + }); + super::print_json(&response) + } + }; +} + +pub(super) use define_delete_command; +pub(super) use define_get_command; diff --git a/src/presentation/cli/mod.rs b/src/presentation/cli/mod.rs index 405866d..c57e237 100644 --- a/src/presentation/cli/mod.rs +++ b/src/presentation/cli/mod.rs @@ -1,3 +1,4 @@ +mod macros; pub mod bags; pub mod roasters; pub mod roasts; diff --git a/src/presentation/cli/roasters.rs b/src/presentation/cli/roasters.rs index 3d4ab8e..5fdb512 100644 --- a/src/presentation/cli/roasters.rs +++ b/src/presentation/cli/roasters.rs @@ -1,7 +1,7 @@ use anyhow::Result; use clap::Args; -use serde_json::json; +use super::macros::{define_delete_command, define_get_command}; use super::print_json; use crate::domain::ids::RoasterId; use crate::domain::roasters::{NewRoaster, UpdateRoaster}; @@ -39,16 +39,7 @@ pub async fn list_roasters(client: &BrewlogClient) -> Result<()> { print_json(&roasters) } -#[derive(Debug, Args)] -pub struct GetRoasterCommand { - #[arg(long)] - pub id: i64, -} - -pub async fn get_roaster(client: &BrewlogClient, command: GetRoasterCommand) -> Result<()> { - let roaster = client.roasters().get(RoasterId::new(command.id)).await?; - print_json(&roaster) -} +define_get_command!(GetRoasterCommand, get_roaster, RoasterId, roasters); #[derive(Debug, Args)] pub struct UpdateRoasterCommand { @@ -82,19 +73,4 @@ pub async fn update_roaster(client: &BrewlogClient, command: UpdateRoasterComman print_json(&roaster) } -#[derive(Debug, Args)] -pub struct DeleteRoasterCommand { - #[arg(long)] - pub id: i64, -} - -pub async fn delete_roaster(client: &BrewlogClient, command: DeleteRoasterCommand) -> Result<()> { - let id = RoasterId::new(command.id); - client.roasters().delete(id).await?; - let response = json!({ - "status": "deleted", - "resource": "roaster", - "id": id.into_inner(), - }); - print_json(&response) -} +define_delete_command!(DeleteRoasterCommand, delete_roaster, RoasterId, roasters, "roaster"); diff --git a/src/presentation/cli/roasts.rs b/src/presentation/cli/roasts.rs index 7d3574b..2912e6c 100644 --- a/src/presentation/cli/roasts.rs +++ b/src/presentation/cli/roasts.rs @@ -1,7 +1,7 @@ use anyhow::Result; use clap::Args; -use serde_json::json; +use super::macros::{define_delete_command, define_get_command}; use super::print_json; use crate::domain::ids::{RoastId, RoasterId}; use crate::domain::roasts::NewRoast; @@ -54,30 +54,5 @@ pub async fn list_roasts(client: &BrewlogClient, command: ListRoastsCommand) -> print_json(&roasts) } -#[derive(Debug, Args)] -pub struct GetRoastCommand { - #[arg(long)] - pub id: i64, -} - -pub async fn get_roast(client: &BrewlogClient, command: GetRoastCommand) -> Result<()> { - let roast = client.roasts().get(RoastId::new(command.id)).await?; - print_json(&roast) -} - -#[derive(Debug, Args)] -pub struct DeleteRoastCommand { - #[arg(long)] - pub id: i64, -} - -pub async fn delete_roast(client: &BrewlogClient, command: DeleteRoastCommand) -> Result<()> { - let id = RoastId::new(command.id); - client.roasts().delete(id).await?; - let response = json!({ - "status": "deleted", - "resource": "roast", - "id": id.into_inner(), - }); - print_json(&response) -} +define_get_command!(GetRoastCommand, get_roast, RoastId, roasts); +define_delete_command!(DeleteRoastCommand, delete_roast, RoastId, roasts, "roast");