From b1027b3b04c22d31ecb738dc0c5f8b216bb6b26c Mon Sep 17 00:00:00 2001
From: Awiteb
Date: Sun, 8 Jun 2025 20:12:32 +0000
Subject: [PATCH] refactor: Improve commands running and make the signer
optional for some commands
Signed-off-by: Awiteb
---
CHANGELOG.md | 1 +
src/cli/commands/config/mod.rs | 5 +--
src/cli/commands/config/pow.rs | 4 +--
src/cli/commands/config/relays.rs | 4 +--
src/cli/commands/issue/mod.rs | 4 +--
src/cli/commands/mod.rs | 38 ++++++++++-----------
src/cli/commands/patch/mod.rs | 4 +--
src/cli/commands/repo/announce.rs | 4 +--
src/cli/commands/repo/mod.rs | 5 +--
src/cli/commands/repo/view.rs | 4 +--
src/cli/commands/sets/mod.rs | 7 +---
src/cli/commands/sets/new.rs | 3 +-
src/cli/commands/sets/remove.rs | 3 +-
src/cli/commands/sets/show.rs | 3 +-
src/cli/commands/sets/update.rs | 3 +-
src/cli/macros.rs | 55 +++++++++++++++++++++++++++++++
src/cli/mod.rs | 2 ++
src/cli/traits.rs | 7 ++++
src/error.rs | 2 ++
19 files changed, 107 insertions(+), 51 deletions(-)
create mode 100644 src/cli/macros.rs
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cfa5b40..82905e1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Refactor
- Store the config in `CliOptions` instead of its path - by Awiteb
+- Improve commands running and make the signer optional for some commands - by Awiteb
## [0.2.0] - 2025-06-01
diff --git a/src/cli/commands/config/mod.rs b/src/cli/commands/config/mod.rs
index 0174e72..10864e6 100644
--- a/src/cli/commands/config/mod.rs
+++ b/src/cli/commands/config/mod.rs
@@ -38,9 +38,6 @@ pub enum ConfigSubcommands {
impl CommandRunner for ConfigSubcommands {
async fn run(self, options: CliOptions) -> N34Result<()> {
- match self {
- Self::Pow(args) => args.run(options).await,
- Self::Relays(args) => args.run(options).await,
- }
+ crate::run_command!(self, options, & Pow Relays)
}
}
diff --git a/src/cli/commands/config/pow.rs b/src/cli/commands/config/pow.rs
index 49ff1d5..9655498 100644
--- a/src/cli/commands/config/pow.rs
+++ b/src/cli/commands/config/pow.rs
@@ -28,9 +28,9 @@ pub struct PowArgs {
}
impl CommandRunner for PowArgs {
- async fn run(self, mut options: CliOptions) -> N34Result<()> {
- // FIXME: The signer is not required here
+ const NEED_SIGNER: bool = false;
+ async fn run(self, mut options: CliOptions) -> N34Result<()> {
options.config.pow = Some(self.difficulty);
options.config.dump()
}
diff --git a/src/cli/commands/config/relays.rs b/src/cli/commands/config/relays.rs
index 3656f43..3e4d23f 100644
--- a/src/cli/commands/config/relays.rs
+++ b/src/cli/commands/config/relays.rs
@@ -33,9 +33,9 @@ pub struct RelaysArgs {
}
impl CommandRunner for RelaysArgs {
- async fn run(self, mut options: CliOptions) -> N34Result<()> {
- // FIXME: The signer is not required here
+ const NEED_SIGNER: bool = false;
+ async fn run(self, mut options: CliOptions) -> N34Result<()> {
if self.relays.is_empty() {
options.config.fallback_relays = None;
} else if self.override_relays {
diff --git a/src/cli/commands/issue/mod.rs b/src/cli/commands/issue/mod.rs
index fa6f463..495c729 100644
--- a/src/cli/commands/issue/mod.rs
+++ b/src/cli/commands/issue/mod.rs
@@ -34,8 +34,6 @@ pub enum IssueSubcommands {
impl CommandRunner for IssueSubcommands {
async fn run(self, options: CliOptions) -> N34Result<()> {
- match self {
- Self::New(args) => args.run(options).await,
- }
+ crate::run_command!(self, options, &New)
}
}
diff --git a/src/cli/commands/mod.rs b/src/cli/commands/mod.rs
index ba34c2a..48f240b 100644
--- a/src/cli/commands/mod.rs
+++ b/src/cli/commands/mod.rs
@@ -48,14 +48,13 @@ use crate::error::{N34Error, N34Result};
/// This is a workaround since Clap doesn't support lazy evaluation of defaults.
pub const DEFAULT_FALLBACK_PATH: &str = "I_DO_NOT_KNOW_WHY_CLAP_DO_NOT_SUPPORT_LAZY_DEFAULT";
-// TODO: Make the `signer` group optional
/// The command-line interface options
#[derive(Args, Clone)]
#[clap(
group(
ArgGroup::new("signer")
.args(&["secret_key"])
- .required(true)
+ .required(false)
)
)]
pub struct CliOptions {
@@ -109,23 +108,6 @@ pub enum Commands {
}
-impl CommandRunner for Commands {
- async fn run(self, options: CliOptions) -> N34Result<()> {
- tracing::trace!("Options: {options:#?}");
- tracing::trace!("Handling: {self:#?}");
- match self {
- Self::Repo { subcommands } => subcommands.run(options).await,
- Self::Issue { subcommands } => subcommands.run(options).await,
- Self::Reply(args) => args.run(options).await,
- Self::Sets { subcommands } => subcommands.run(options).await,
- Self::Patch { subcommands } => subcommands.run(options).await,
- Self::Config { subcommands } => subcommands.run(options).await,
- }
- }
-}
-
-// TODO: Implement `ensure_signer` function with similar functionality to
-// `ensure_relays`
impl CliOptions {
/// Gets the public key of the user.
pub async fn pubkey(&self) -> N34Result {
@@ -142,6 +124,15 @@ impl CliOptions {
}
Ok(())
}
+
+ /// Returns an error if there are no signers
+ pub fn ensure_signer(&self) -> N34Result<()> {
+ if self.secret_key.is_none() {
+ Err(N34Error::SignerRequired)
+ } else {
+ Ok(())
+ }
+ }
}
impl fmt::Debug for CliOptions {
@@ -154,3 +145,12 @@ impl fmt::Debug for CliOptions {
.finish()
}
}
+
+impl CommandRunner for Commands {
+ async fn run(self, options: CliOptions) -> N34Result<()> {
+ tracing::trace!("Options: {options:#?}");
+ tracing::trace!("Handling: {self:#?}");
+
+ crate::run_command!(self, options, Repo Issue Sets Patch Config & Reply)
+ }
+}
diff --git a/src/cli/commands/patch/mod.rs b/src/cli/commands/patch/mod.rs
index 0ed4b4c..da83c49 100644
--- a/src/cli/commands/patch/mod.rs
+++ b/src/cli/commands/patch/mod.rs
@@ -54,9 +54,7 @@ pub struct GitPatch {
impl CommandRunner for PatchSubcommands {
async fn run(self, options: CliOptions) -> N34Result<()> {
- match self {
- Self::Send(args) => args.run(options).await,
- }
+ crate::run_command!(self, options, &Send)
}
}
diff --git a/src/cli/commands/repo/announce.rs b/src/cli/commands/repo/announce.rs
index 65717a4..3dd2e0d 100644
--- a/src/cli/commands/repo/announce.rs
+++ b/src/cli/commands/repo/announce.rs
@@ -78,9 +78,9 @@ pub struct AnnounceArgs {
}
impl CommandRunner for AnnounceArgs {
- async fn run(mut self, options: CliOptions) -> N34Result<()> {
- options.ensure_relays()?;
+ const NEED_RELAYS: bool = true;
+ async fn run(mut self, options: CliOptions) -> N34Result<()> {
let relays = options.relays.clone().flat_relays(&options.config.sets)?;
let client = NostrClient::init(&options, &relays).await;
let user_pubk = options.pubkey().await?;
diff --git a/src/cli/commands/repo/mod.rs b/src/cli/commands/repo/mod.rs
index a77fc73..343f7ba 100644
--- a/src/cli/commands/repo/mod.rs
+++ b/src/cli/commands/repo/mod.rs
@@ -38,9 +38,6 @@ pub enum RepoSubcommands {
impl CommandRunner for RepoSubcommands {
async fn run(self, options: CliOptions) -> N34Result<()> {
- match self {
- Self::View(args) => args.run(options).await,
- Self::Announce(args) => args.run(options).await,
- }
+ crate::run_command!(self, options, & View Announce)
}
}
diff --git a/src/cli/commands/repo/view.rs b/src/cli/commands/repo/view.rs
index fff6e90..80b15ed 100644
--- a/src/cli/commands/repo/view.rs
+++ b/src/cli/commands/repo/view.rs
@@ -40,9 +40,9 @@ pub struct ViewArgs {
}
impl CommandRunner for ViewArgs {
- async fn run(self, options: CliOptions) -> N34Result<()> {
- // FIXME: The signer is not required here
+ const NEED_SIGNER: bool = false;
+ async fn run(self, options: CliOptions) -> N34Result<()> {
let naddrs = utils::naddrs_or_file(
self.naddrs.flat_naddrs(&options.config.sets)?,
&utils::nostr_address_path()?,
diff --git a/src/cli/commands/sets/mod.rs b/src/cli/commands/sets/mod.rs
index 11465bd..f6863cb 100644
--- a/src/cli/commands/sets/mod.rs
+++ b/src/cli/commands/sets/mod.rs
@@ -47,11 +47,6 @@ pub enum SetsSubcommands {
impl CommandRunner for SetsSubcommands {
async fn run(self, options: CliOptions) -> N34Result<()> {
- match self {
- Self::Remove(args) => args.run(options).await,
- Self::New(args) => args.run(options).await,
- Self::Update(args) => args.run(options).await,
- Self::Show(args) => args.run(options).await,
- }
+ crate::run_command!(self, options, & Remove New Update Show)
}
}
diff --git a/src/cli/commands/sets/new.rs b/src/cli/commands/sets/new.rs
index ddc6f04..609f319 100644
--- a/src/cli/commands/sets/new.rs
+++ b/src/cli/commands/sets/new.rs
@@ -42,7 +42,8 @@ pub struct NewArgs {
}
impl CommandRunner for NewArgs {
- // FIXME: The signer is not required here
+ const NEED_SIGNER: bool = false;
+
async fn run(self, mut options: CliOptions) -> N34Result<()> {
let naddrs = self.naddrs.flat_naddrs(&options.config.sets)?;
let relays = self.relays.flat_relays(&options.config.sets)?;
diff --git a/src/cli/commands/sets/remove.rs b/src/cli/commands/sets/remove.rs
index def354e..2096b13 100644
--- a/src/cli/commands/sets/remove.rs
+++ b/src/cli/commands/sets/remove.rs
@@ -42,7 +42,8 @@ pub struct RemoveArgs {
}
impl CommandRunner for RemoveArgs {
- // FIXME: The signer is not required here
+ const NEED_SIGNER: bool = false;
+
async fn run(self, mut options: CliOptions) -> N34Result<()> {
let naddrs = self.naddrs.flat_naddrs(&options.config.sets)?;
let relays = self.relays.flat_relays(&options.config.sets)?;
diff --git a/src/cli/commands/sets/show.rs b/src/cli/commands/sets/show.rs
index bb51b88..d4cd045 100644
--- a/src/cli/commands/sets/show.rs
+++ b/src/cli/commands/sets/show.rs
@@ -29,7 +29,8 @@ pub struct ShowArgs {
}
impl CommandRunner for ShowArgs {
- // FIXME: The signer is not required here
+ const NEED_SIGNER: bool = false;
+
async fn run(self, options: CliOptions) -> N34Result<()> {
if let Some(name) = self.name {
println!(
diff --git a/src/cli/commands/sets/update.rs b/src/cli/commands/sets/update.rs
index ddc864a..4930108 100644
--- a/src/cli/commands/sets/update.rs
+++ b/src/cli/commands/sets/update.rs
@@ -46,7 +46,8 @@ pub struct UpdateArgs {
}
impl CommandRunner for UpdateArgs {
- // FIXME: The signer is not required here
+ const NEED_SIGNER: bool = false;
+
async fn run(self, mut options: CliOptions) -> N34Result<()> {
let naddrs = self.naddrs.flat_naddrs(&options.config.sets)?;
let relays = self.relays.flat_relays(&options.config.sets)?;
diff --git a/src/cli/macros.rs b/src/cli/macros.rs
new file mode 100644
index 0000000..b5ba9f6
--- /dev/null
+++ b/src/cli/macros.rs
@@ -0,0 +1,55 @@
+// n34 - A CLI to interact with NIP-34 and other stuff related to codes in nostr
+// Copyright (C) 2025 Awiteb
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+use super::traits::CommandRunner;
+
+
+/// Returns whether the command runner type `T` requires relays.
+pub fn get_relays_state(_v: &T) -> bool {
+ T::NEED_RELAYS
+}
+
+/// Returns whether the command runner type `T` requires a signer.
+pub fn get_signer_state(_v: &T) -> bool {
+ T::NEED_SIGNER
+}
+
+/// Executes a command with required setup checks. The first parameter is the
+/// command to match on (often `self`), followed by options. Optional
+/// subcommands come next, and commands with arguments (after `&`) are listed
+/// last.
+#[macro_export]
+macro_rules! run_command {
+ (r $command:ident $options:ident) => {{
+ if $crate::cli::macros::get_relays_state(&$command) {
+ $options.ensure_relays()?;
+ }
+ if $crate::cli::macros::get_signer_state(&$command) {
+ $options.ensure_signer()?;
+ }
+ $command.run($options).await
+ }};
+ ($command:ident, $options:ident, $($subcommands:ident)* & $($commands:ident)*) => {
+ match $command {
+ $(
+ Self::$subcommands { subcommands } => subcommands.run($options).await,
+ )*
+ $(
+ Self::$commands ( args ) => $crate::run_command!(r args $options),
+ )*
+ }
+ };
+}
diff --git a/src/cli/mod.rs b/src/cli/mod.rs
index 156d2cd..d5cd925 100644
--- a/src/cli/mod.rs
+++ b/src/cli/mod.rs
@@ -20,6 +20,8 @@ pub mod commands;
pub mod config;
/// Default lazy values for CLI arguments
pub mod defaults;
+/// Macros for CLI application.
+pub mod macros;
/// CLI arguments parsers
pub mod parsers;
/// CLI traits
diff --git a/src/cli/traits.rs b/src/cli/traits.rs
index 905192f..917338a 100644
--- a/src/cli/traits.rs
+++ b/src/cli/traits.rs
@@ -19,6 +19,13 @@ use crate::error::N34Result;
/// A trait defining the interface for command runners in the CLI.
pub trait CommandRunner {
+ /// Whether this command needs the relays option (false by default).
+ /// Only applies to commands, not subcommands.
+ const NEED_RELAYS: bool = false;
+ /// Indicates if this command requires the signer. Defaults to true.
+ /// Only applies to commands, not subcommands.
+ const NEED_SIGNER: bool = true;
+
/// Executes the command and returns a Result indicating success or failure.
fn run(self, options: CliOptions) -> impl Future