feat(cli): add Gear CLI commands

Implement complete CLI interface for managing gear via brewlog CLI.

Commands (presentation/cli/gear.rs):
- add-gear: Create gear with --category, --make, --model, --notes flags
- list-gear: List all gear with optional --category filter
- get-gear: Retrieve single gear by --id
- update-gear: Update gear fields (make, model, notes)
- delete-gear: Delete gear by --id

Uses define_get_command! and define_delete_command! macros for get/delete
operations to reduce boilerplate.

Command Registration:
- Register gear module in presentation/cli/mod.rs
- Add commands to Commands enum: AddGear, ListGear, GetGear, UpdateGear, DeleteGear
- Wire command execution in main.rs

All commands output JSON and use BrewlogClient for HTTP API calls.

Examples:
  brewlog add-gear --category grinder --make Baratza --model Encore
  brewlog list-gear --category brewer
  brewlog update-gear --id 1 --notes "Updated notes"
This commit is contained in:
Jon Seager 2026-02-02 15:24:34 +00:00
parent 1ef447157e
commit 1f5561d7ab
No known key found for this signature in database
3 changed files with 91 additions and 1 deletions

View file

@ -1,7 +1,7 @@
use anyhow::Result; use anyhow::Result;
use brewlog::application::{ServerConfig, serve}; use brewlog::application::{ServerConfig, serve};
use brewlog::infrastructure::client::BrewlogClient; use brewlog::infrastructure::client::BrewlogClient;
use brewlog::presentation::cli::{Cli, Commands, ServeCommand, bags, roasters, roasts, tokens}; use brewlog::presentation::cli::{Cli, Commands, ServeCommand, bags, gear, roasters, roasts, tokens};
use clap::Parser; use clap::Parser;
use tracing::{Subscriber, subscriber::set_global_default}; use tracing::{Subscriber, subscriber::set_global_default};
@ -47,6 +47,13 @@ async fn main() -> Result<()> {
Commands::UpdateBag(cmd) => bags::update_bag(&client, cmd).await, Commands::UpdateBag(cmd) => bags::update_bag(&client, cmd).await,
Commands::DeleteBag(cmd) => bags::delete_bag(&client, cmd).await, Commands::DeleteBag(cmd) => bags::delete_bag(&client, cmd).await,
// Gear
Commands::AddGear(cmd) => gear::add_gear(&client, cmd).await,
Commands::ListGear(cmd) => gear::list_gear(&client, cmd).await,
Commands::GetGear(cmd) => gear::get_gear(&client, cmd).await,
Commands::UpdateGear(cmd) => gear::update_gear(&client, cmd).await,
Commands::DeleteGear(cmd) => gear::delete_gear(&client, cmd).await,
Commands::Serve(_) => unreachable!("serve command handled earlier"), Commands::Serve(_) => unreachable!("serve command handled earlier"),
} }
} }

View file

@ -0,0 +1,67 @@
use anyhow::Result;
use clap::Args;
use super::macros::{define_delete_command, define_get_command};
use super::print_json;
use crate::domain::ids::GearId;
use crate::infrastructure::client::BrewlogClient;
#[derive(Debug, Args)]
pub struct AddGearCommand {
#[arg(long)]
pub category: String,
#[arg(long)]
pub make: String,
#[arg(long)]
pub model: String,
#[arg(long)]
pub notes: Option<String>,
}
pub async fn add_gear(client: &BrewlogClient, command: AddGearCommand) -> Result<()> {
let gear = client
.gear()
.create(&command.category, command.make, command.model, command.notes)
.await?;
print_json(&gear)
}
#[derive(Debug, Args)]
pub struct ListGearCommand {
#[arg(long)]
pub category: Option<String>,
}
pub async fn list_gear(client: &BrewlogClient, command: ListGearCommand) -> Result<()> {
let gear = client.gear().list(command.category).await?;
print_json(&gear)
}
define_get_command!(GetGearCommand, get_gear, GearId, gear);
#[derive(Debug, Args)]
pub struct UpdateGearCommand {
#[arg(long)]
pub id: i64,
#[arg(long)]
pub make: Option<String>,
#[arg(long)]
pub model: Option<String>,
#[arg(long)]
pub notes: Option<String>,
}
pub async fn update_gear(client: &BrewlogClient, command: UpdateGearCommand) -> Result<()> {
let gear = client
.gear()
.update(
GearId::new(command.id),
command.make,
command.model,
command.notes,
)
.await?;
print_json(&gear)
}
define_delete_command!(DeleteGearCommand, delete_gear, GearId, gear, "gear");

View file

@ -1,4 +1,5 @@
pub mod bags; pub mod bags;
pub mod gear;
mod macros; mod macros;
pub mod roasters; pub mod roasters;
pub mod roasts; pub mod roasts;
@ -8,6 +9,9 @@ use std::net::SocketAddr;
use bags::{AddBagCommand, DeleteBagCommand, GetBagCommand, ListBagsCommand, UpdateBagCommand}; use bags::{AddBagCommand, DeleteBagCommand, GetBagCommand, ListBagsCommand, UpdateBagCommand};
use clap::{Args, Parser, Subcommand}; use clap::{Args, Parser, Subcommand};
use gear::{
AddGearCommand, DeleteGearCommand, GetGearCommand, ListGearCommand, UpdateGearCommand,
};
use roasters::{AddRoasterCommand, DeleteRoasterCommand, GetRoasterCommand, UpdateRoasterCommand}; use roasters::{AddRoasterCommand, DeleteRoasterCommand, GetRoasterCommand, UpdateRoasterCommand};
use roasts::{AddRoastCommand, DeleteRoastCommand, GetRoastCommand, ListRoastsCommand}; use roasts::{AddRoastCommand, DeleteRoastCommand, GetRoastCommand, ListRoastsCommand};
use tokens::{CreateTokenCommand, RevokeTokenCommand}; use tokens::{CreateTokenCommand, RevokeTokenCommand};
@ -73,6 +77,18 @@ pub enum Commands {
UpdateBag(UpdateBagCommand), UpdateBag(UpdateBagCommand),
#[command(name = "delete-bag")] #[command(name = "delete-bag")]
DeleteBag(DeleteBagCommand), DeleteBag(DeleteBagCommand),
// Gear
#[command(name = "add-gear")]
AddGear(AddGearCommand),
#[command(name = "list-gear")]
ListGear(ListGearCommand),
#[command(name = "get-gear")]
GetGear(GetGearCommand),
#[command(name = "update-gear")]
UpdateGear(UpdateGearCommand),
#[command(name = "delete-gear")]
DeleteGear(DeleteGearCommand),
} }
#[derive(Debug, Args)] #[derive(Debug, Args)]