refactor: Improve commands running and make the signer optional for some commands
Signed-off-by: Awiteb <a@4rs.nl>
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<PublicKey> {
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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?;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()?,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
55
src/cli/macros.rs
Normal file
55
src/cli/macros.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
// n34 - A CLI to interact with NIP-34 and other stuff related to codes in nostr
|
||||
// Copyright (C) 2025 Awiteb <a@4rs.nl>
|
||||
//
|
||||
// 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 <https://gnu.org/licenses/gpl-3.0.html>.
|
||||
|
||||
use super::traits::CommandRunner;
|
||||
|
||||
|
||||
/// Returns whether the command runner type `T` requires relays.
|
||||
pub fn get_relays_state<T: CommandRunner>(_v: &T) -> bool {
|
||||
T::NEED_RELAYS
|
||||
}
|
||||
|
||||
/// Returns whether the command runner type `T` requires a signer.
|
||||
pub fn get_signer_state<T: CommandRunner>(_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),
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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<Output = N34Result<()>> + Send;
|
||||
}
|
||||
|
||||
@@ -66,6 +66,8 @@ pub enum N34Error {
|
||||
InvalidNostrAddressFileContent(String),
|
||||
#[error("This command requires at least one relay, but none were provided")]
|
||||
EmptyRelays,
|
||||
#[error("This command requires a signer to sign events")]
|
||||
SignerRequired,
|
||||
#[error(
|
||||
"Invalid repository address. Expected one of these formats:\n- NIP-05 identifier with \
|
||||
repository ID: `<user@domain.com>/<repo_id>`\n- Valid NIP-19 naddr string (starts with \
|
||||
|
||||
Reference in New Issue
Block a user