refactor: Store the config in CliOptions instead of its path

Signed-off-by: Awiteb <a@4rs.nl>
This commit is contained in:
Awiteb
2025-06-07 18:57:53 +00:00
parent 30b3056d39
commit a6a61aedf1
15 changed files with 84 additions and 73 deletions

View File

@@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add `alt` tag to the git issue - by Awiteb - Add `alt` tag to the git issue - by Awiteb
- Add `description` tag to the patch - by Awiteb - Add `description` tag to the patch - by Awiteb
### Refactor
- Store the config in `CliOptions` instead of its path - by Awiteb
## [0.2.0] - 2025-06-01 ## [0.2.0] - 2025-06-01
### Added ### Added

View File

@@ -21,7 +21,6 @@ use nostr::event::{EventBuilder, Tag};
use crate::{ use crate::{
cli::{ cli::{
CliConfig,
CliOptions, CliOptions,
CommandRunner, CommandRunner,
types::{NaddrOrSet, OptionNaddrOrSetVecExt, RelayOrSetVecExt}, types::{NaddrOrSet, OptionNaddrOrSetVecExt, RelayOrSetVecExt},
@@ -97,12 +96,11 @@ impl NewArgs {
impl CommandRunner for NewArgs { impl CommandRunner for NewArgs {
async fn run(self, options: CliOptions) -> N34Result<()> { async fn run(self, options: CliOptions) -> N34Result<()> {
let config = CliConfig::load_toml(&options.config_path)?;
let naddrs = utils::naddrs_or_file( let naddrs = utils::naddrs_or_file(
self.naddrs.flat_naddrs(&config.sets)?, self.naddrs.flat_naddrs(&options.config.sets)?,
&utils::nostr_address_path()?, &utils::nostr_address_path()?,
)?; )?;
let relays = options.relays.clone().flat_relays(&config.sets)?; let relays = options.relays.clone().flat_relays(&options.config.sets)?;
let client = NostrClient::init(&options, &relays).await; let client = NostrClient::init(&options, &relays).await;
let user_pubk = options.pubkey().await?; let user_pubk = options.pubkey().await?;
let mut naddrs_iter = naddrs.clone().into_iter(); let mut naddrs_iter = naddrs.clone().into_iter();

View File

@@ -26,7 +26,6 @@ pub mod repo;
pub mod sets; pub mod sets;
use std::fmt; use std::fmt;
use std::path::PathBuf;
use clap::{ArgGroup, Args, Parser}; use clap::{ArgGroup, Args, Parser};
use nostr::key::{Keys, PublicKey, SecretKey}; use nostr::key::{Keys, PublicKey, SecretKey};
@@ -36,8 +35,9 @@ use self::patch::PatchSubcommands;
use self::reply::ReplyArgs; use self::reply::ReplyArgs;
use self::repo::RepoSubcommands; use self::repo::RepoSubcommands;
use self::sets::SetsSubcommands; use self::sets::SetsSubcommands;
use super::traits::CommandRunner; use super::CliConfig;
use super::types::RelayOrSet; use super::types::RelayOrSet;
use super::{parsers, traits::CommandRunner};
use crate::error::{N34Error, N34Result}; use crate::error::{N34Error, N34Result};
/// Default path used when no path is provided via command line arguments. /// Default path used when no path is provided via command line arguments.
@@ -58,19 +58,19 @@ pub const DEFAULT_FALLBACK_PATH: &str = "I_DO_NOT_KNOW_WHY_CLAP_DO_NOT_SUPPORT_L
pub struct CliOptions { pub struct CliOptions {
/// Your Nostr secret key /// Your Nostr secret key
#[arg(short, long)] #[arg(short, long)]
pub secret_key: Option<SecretKey>, pub secret_key: Option<SecretKey>,
/// Fallbacks relay to write and read from it. Multiple relays can be /// Fallbacks relay to write and read from it. Multiple relays can be
/// passed. /// passed.
#[arg(short, long)] #[arg(short, long)]
pub relays: Vec<RelayOrSet>, pub relays: Vec<RelayOrSet>,
/// Proof of Work difficulty when creatring events /// Proof of Work difficulty when creatring events
#[arg(long, default_value_t = 0)] #[arg(long, default_value_t = 0)]
pub pow: u8, pub pow: u8,
/// Config path [default: `$XDG_CONFIG_HOME` or `$HOME/.config`] /// Config path [default: `$XDG_CONFIG_HOME` or `$HOME/.config`]
#[arg(long, value_name = "PATH", default_value = DEFAULT_FALLBACK_PATH, #[arg(long, value_name = "PATH", default_value = DEFAULT_FALLBACK_PATH,
hide_default_value = true hide_default_value = true, value_parser = parsers::parse_config_path
)] )]
pub config_path: PathBuf, pub config: CliConfig,
} }
/// N34 commands /// N34 commands

View File

@@ -29,7 +29,6 @@ use nostr::{
use super::GitPatch; use super::GitPatch;
use crate::{ use crate::{
cli::{ cli::{
CliConfig,
CliOptions, CliOptions,
traits::CommandRunner, traits::CommandRunner,
types::{NaddrOrSet, NostrEvent, OptionNaddrOrSetVecExt, RelayOrSetVecExt}, types::{NaddrOrSet, NostrEvent, OptionNaddrOrSetVecExt, RelayOrSetVecExt},
@@ -72,13 +71,12 @@ pub struct SendArgs {
impl CommandRunner for SendArgs { impl CommandRunner for SendArgs {
async fn run(self, options: CliOptions) -> N34Result<()> { async fn run(self, options: CliOptions) -> N34Result<()> {
let config = CliConfig::load_toml(&options.config_path)?;
let naddrs = utils::naddrs_or_file( let naddrs = utils::naddrs_or_file(
self.naddrs.flat_naddrs(&config.sets)?, self.naddrs.flat_naddrs(&options.config.sets)?,
&utils::nostr_address_path()?, &utils::nostr_address_path()?,
)?; )?;
let repo_coordinates = naddrs.clone().into_coordinates(); let repo_coordinates = naddrs.clone().into_coordinates();
let relays = options.relays.clone().flat_relays(&config.sets)?; let relays = options.relays.clone().flat_relays(&options.config.sets)?;
let user_pubk = options.pubkey().await?; let user_pubk = options.pubkey().await?;
let client = NostrClient::init(&options, &relays).await; let client = NostrClient::init(&options, &relays).await;

View File

@@ -30,10 +30,7 @@ use nostr::{
use super::{CliOptions, CommandRunner}; use super::{CliOptions, CommandRunner};
use crate::{ use crate::{
cli::{ cli::types::{NaddrOrSet, NostrEvent, OptionNaddrOrSetVecExt, RelayOrSetVecExt},
CliConfig,
types::{NaddrOrSet, NostrEvent, OptionNaddrOrSetVecExt, RelayOrSetVecExt},
},
error::{N34Error, N34Result}, error::{N34Error, N34Result},
nostr_utils::{ nostr_utils::{
NostrClient, NostrClient,
@@ -84,12 +81,11 @@ pub struct ReplyArgs {
impl CommandRunner for ReplyArgs { impl CommandRunner for ReplyArgs {
async fn run(self, options: CliOptions) -> N34Result<()> { async fn run(self, options: CliOptions) -> N34Result<()> {
let nostr_address_path = utils::nostr_address_path()?; let nostr_address_path = utils::nostr_address_path()?;
let config = CliConfig::load_toml(&options.config_path)?; let relays = options.relays.clone().flat_relays(&options.config.sets)?;
let relays = options.relays.clone().flat_relays(&config.sets)?;
let client = NostrClient::init(&options, &relays).await; let client = NostrClient::init(&options, &relays).await;
let user_pubk = options.pubkey().await?; let user_pubk = options.pubkey().await?;
let repo_naddrs = if let Some(naddrs) = self.naddrs.flat_naddrs(&config.sets)? { let repo_naddrs = if let Some(naddrs) = self.naddrs.flat_naddrs(&options.config.sets)? {
client.add_relays(&naddrs.extract_relays()).await; client.add_relays(&naddrs.extract_relays()).await;
Some(naddrs) Some(naddrs)
} else if fs::exists(&nostr_address_path).is_ok() { } else if fs::exists(&nostr_address_path).is_ok() {

View File

@@ -21,7 +21,7 @@ use futures::future;
use nostr::{event::EventBuilder, key::PublicKey, types::Url}; use nostr::{event::EventBuilder, key::PublicKey, types::Url};
use crate::{ use crate::{
cli::{CliConfig, CliOptions, CommandRunner, NOSTR_ADDRESS_FILE, types::RelayOrSetVecExt}, cli::{CliOptions, CommandRunner, NOSTR_ADDRESS_FILE, types::RelayOrSetVecExt},
error::N34Result, error::N34Result,
nostr_utils::{NostrClient, traits::NewGitRepositoryAnnouncement, utils}, nostr_utils::{NostrClient, traits::NewGitRepositoryAnnouncement, utils},
}; };
@@ -81,8 +81,7 @@ impl CommandRunner for AnnounceArgs {
async fn run(mut self, options: CliOptions) -> N34Result<()> { async fn run(mut self, options: CliOptions) -> N34Result<()> {
options.ensure_relays()?; options.ensure_relays()?;
let config = CliConfig::load_toml(&options.config_path)?; let relays = options.relays.clone().flat_relays(&options.config.sets)?;
let relays = options.relays.clone().flat_relays(&config.sets)?;
let client = NostrClient::init(&options, &relays).await; let client = NostrClient::init(&options, &relays).await;
let user_pubk = options.pubkey().await?; let user_pubk = options.pubkey().await?;
let relays_list = client.user_relays_list(user_pubk).await?; let relays_list = client.user_relays_list(user_pubk).await?;

View File

@@ -20,7 +20,6 @@ use clap::Args;
use crate::{ use crate::{
cli::{ cli::{
CliConfig,
CliOptions, CliOptions,
CommandRunner, CommandRunner,
types::{NaddrOrSet, OptionNaddrOrSetVecExt, RelayOrSetVecExt}, types::{NaddrOrSet, OptionNaddrOrSetVecExt, RelayOrSetVecExt},
@@ -44,12 +43,11 @@ impl CommandRunner for ViewArgs {
async fn run(self, options: CliOptions) -> N34Result<()> { async fn run(self, options: CliOptions) -> N34Result<()> {
// FIXME: The signer is not required here // FIXME: The signer is not required here
let config = CliConfig::load_toml(&options.config_path)?;
let naddrs = utils::naddrs_or_file( let naddrs = utils::naddrs_or_file(
self.naddrs.flat_naddrs(&config.sets)?, self.naddrs.flat_naddrs(&options.config.sets)?,
&utils::nostr_address_path()?, &utils::nostr_address_path()?,
)?; )?;
let relays = options.relays.clone().flat_relays(&config.sets)?; let relays = options.relays.clone().flat_relays(&options.config.sets)?;
let client = NostrClient::init(&options, &relays).await; let client = NostrClient::init(&options, &relays).await;
client.add_relays(&naddrs.extract_relays()).await; client.add_relays(&naddrs.extract_relays()).await;

View File

@@ -18,7 +18,6 @@ use clap::Args;
use crate::{ use crate::{
cli::{ cli::{
CliConfig,
CliOptions, CliOptions,
ConfigError, ConfigError,
MutRepoRelaySetsExt, MutRepoRelaySetsExt,
@@ -44,16 +43,15 @@ pub struct NewArgs {
impl CommandRunner for NewArgs { impl CommandRunner for NewArgs {
// FIXME: The signer is not required here // FIXME: The signer is not required here
async fn run(self, options: CliOptions) -> N34Result<()> { async fn run(self, mut options: CliOptions) -> N34Result<()> {
let mut config = CliConfig::load_toml(&options.config_path)?; let naddrs = self.naddrs.flat_naddrs(&options.config.sets)?;
let naddrs = self.naddrs.flat_naddrs(&config.sets)?; let relays = self.relays.flat_relays(&options.config.sets)?;
let relays = self.relays.flat_relays(&config.sets)?;
if relays.is_empty() && naddrs.is_empty() { if relays.is_empty() && naddrs.is_empty() {
return Err(ConfigError::NewEmptySet.into()); return Err(ConfigError::NewEmptySet.into());
} }
config.sets.push_set(self.name, naddrs, relays)?; options.config.sets.push_set(self.name, naddrs, relays)?;
config.dump_toml(&options.config_path) options.config.dump_toml()
} }
} }

View File

@@ -18,7 +18,6 @@ use clap::Args;
use crate::{ use crate::{
cli::{ cli::{
CliConfig,
CliOptions, CliOptions,
MutRepoRelaySetsExt, MutRepoRelaySetsExt,
traits::CommandRunner, traits::CommandRunner,
@@ -44,22 +43,27 @@ pub struct RemoveArgs {
impl CommandRunner for RemoveArgs { impl CommandRunner for RemoveArgs {
// FIXME: The signer is not required here // FIXME: The signer is not required here
async fn run(self, options: CliOptions) -> N34Result<()> { async fn run(self, mut options: CliOptions) -> N34Result<()> {
let mut config = CliConfig::load_toml(&options.config_path)?; let naddrs = self.naddrs.flat_naddrs(&options.config.sets)?;
let naddrs = self.naddrs.flat_naddrs(&config.sets)?; let relays = self.relays.flat_relays(&options.config.sets)?;
let relays = self.relays.flat_relays(&config.sets)?;
if relays.is_empty() && naddrs.is_empty() { if relays.is_empty() && naddrs.is_empty() {
config.sets.remove_set(self.name)?; options.config.sets.remove_set(self.name)?;
} else { } else {
if !relays.is_empty() { if !relays.is_empty() {
config.sets.remove_relays(&self.name, relays.into_iter())?; options
.config
.sets
.remove_relays(&self.name, relays.into_iter())?;
} }
if !naddrs.is_empty() { if !naddrs.is_empty() {
config.sets.remove_naddrs(self.name, naddrs.into_iter())?; options
.config
.sets
.remove_naddrs(self.name, naddrs.into_iter())?;
} }
} }
config.dump_toml(&options.config_path) options.config.dump_toml()
} }
} }

View File

@@ -18,7 +18,7 @@ use clap::Args;
use nostr::nips::nip19::ToBech32; use nostr::nips::nip19::ToBech32;
use crate::{ use crate::{
cli::{CliConfig, CliOptions, RepoRelaySet, RepoRelaySetsExt, traits::CommandRunner}, cli::{CliOptions, RepoRelaySet, RepoRelaySetsExt, traits::CommandRunner},
error::N34Result, error::N34Result,
}; };
@@ -31,14 +31,16 @@ pub struct ShowArgs {
impl CommandRunner for ShowArgs { impl CommandRunner for ShowArgs {
// FIXME: The signer is not required here // FIXME: The signer is not required here
async fn run(self, options: CliOptions) -> N34Result<()> { async fn run(self, options: CliOptions) -> N34Result<()> {
let config = CliConfig::load_toml(&options.config_path)?;
if let Some(name) = self.name { if let Some(name) = self.name {
println!("{}", format_set(config.sets.as_slice().get_set(&name)?)); println!(
"{}",
format_set(options.config.sets.as_slice().get_set(&name)?)
);
} else { } else {
println!( println!(
"{}", "{}",
config options
.config
.sets .sets
.iter() .iter()
.map(format_set) .map(format_set)

View File

@@ -20,7 +20,6 @@ use clap::Args;
use crate::{ use crate::{
cli::{ cli::{
CliConfig,
CliOptions, CliOptions,
MutRepoRelaySetsExt, MutRepoRelaySetsExt,
traits::CommandRunner, traits::CommandRunner,
@@ -48,12 +47,11 @@ pub struct UpdateArgs {
impl CommandRunner for UpdateArgs { impl CommandRunner for UpdateArgs {
// FIXME: The signer is not required here // FIXME: The signer is not required here
async fn run(self, options: CliOptions) -> N34Result<()> { async fn run(self, mut options: CliOptions) -> N34Result<()> {
let mut config = CliConfig::load_toml(&options.config_path)?; let naddrs = self.naddrs.flat_naddrs(&options.config.sets)?;
let naddrs = self.naddrs.flat_naddrs(&config.sets)?; let relays = self.relays.flat_relays(&options.config.sets)?;
let relays = self.relays.flat_relays(&config.sets)?;
let set = config.sets.get_mut_set(&self.name)?; let set = options.config.sets.get_mut_set(&self.name)?;
if self.override_set { if self.override_set {
if !relays.is_empty() { if !relays.is_empty() {
@@ -67,6 +65,6 @@ impl CommandRunner for UpdateArgs {
set.naddrs.extend(naddrs); set.naddrs.extend(naddrs);
} }
config.dump_toml(&options.config_path) options.config.dump_toml()
} }
} }

View File

@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // 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>. // along with this program. If not, see <https://gnu.org/licenses/gpl-3.0.html>.
use std::{collections::HashSet, fs, path::Path}; use std::{collections::HashSet, fs, path::PathBuf};
use nostr::{ use nostr::{
nips::nip19::{FromBech32, Nip19Coordinate, ToBech32}, nips::nip19::{FromBech32, Nip19Coordinate, ToBech32},
@@ -51,6 +51,9 @@ pub enum ConfigError {
/// Configuration for the command-line interface. /// Configuration for the command-line interface.
#[derive(serde::Serialize, serde::Deserialize, Clone, Default, Debug)] #[derive(serde::Serialize, serde::Deserialize, Clone, Default, Debug)]
pub struct CliConfig { pub struct CliConfig {
/// Path to the configuration file (not serialized)
#[serde(skip)]
path: PathBuf,
/// Groups of repositories and relays. /// Groups of repositories and relays.
#[serde(default, skip_serializing_if = "Vec::is_empty")] #[serde(default, skip_serializing_if = "Vec::is_empty")]
pub sets: Vec<RepoRelaySet>, pub sets: Vec<RepoRelaySet>,
@@ -77,7 +80,7 @@ pub struct RepoRelaySet {
impl CliConfig { impl CliConfig {
/// Reads and parse a TOML config file from the given path, creating it if /// Reads and parse a TOML config file from the given path, creating it if
/// missing. /// missing.
pub fn load_toml(file_path: &Path) -> N34Result<Self> { pub fn load_toml(file_path: PathBuf) -> N34Result<Self> {
tracing::info!(path = %file_path.display(), "Loading configuration from file"); tracing::info!(path = %file_path.display(), "Loading configuration from file");
// Make sure the file is exist // Make sure the file is exist
if let Some(parent) = file_path.parent() { if let Some(parent) = file_path.parent() {
@@ -85,11 +88,12 @@ impl CliConfig {
fs::create_dir_all(parent)?; fs::create_dir_all(parent)?;
} }
} }
let _ = fs::File::create_new(file_path); let _ = fs::File::create_new(&file_path);
let mut config: Self = let mut config: Self =
toml::from_str(&fs::read_to_string(file_path).map_err(ConfigError::ReadFile)?) toml::from_str(&fs::read_to_string(&file_path).map_err(ConfigError::ReadFile)?)
.map_err(ConfigError::ParseFile)?; .map_err(ConfigError::ParseFile)?;
config.path = file_path;
config.post_sets()?; config.post_sets()?;
@@ -97,12 +101,12 @@ impl CliConfig {
} }
/// Dump the config as toml in a file /// Dump the config as toml in a file
pub fn dump_toml(mut self, file_path: &Path) -> N34Result<()> { pub fn dump_toml(mut self) -> N34Result<()> {
tracing::debug!(config = ?self, "Writing configuration to {}", file_path.display()); tracing::debug!(config = ?self, "Writing configuration to {}", self.path.display());
self.post_sets()?; self.post_sets()?;
fs::write( fs::write(
file_path, &self.path,
toml::to_string_pretty(&self).map_err(ConfigError::Serialize)?, toml::to_string_pretty(&self).map_err(ConfigError::Serialize)?,
) )
.map_err(ConfigError::WriteFile)?; .map_err(ConfigError::WriteFile)?;

View File

@@ -71,3 +71,9 @@ impl Cli {
self.command.run(self.options).await self.command.run(self.options).await
} }
} }
/// Processes the CLI configuration and returns it if successful.
pub fn post_cli(cli: Cli) -> N34Result<Cli> {
// TODO
Ok(cli)
}

View File

@@ -14,14 +14,17 @@
// You should have received a copy of the GNU General Public License // 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>. // along with this program. If not, see <https://gnu.org/licenses/gpl-3.0.html>.
use std::{fs, path::Path}; use std::{
fs,
path::{Path, PathBuf},
};
use nostr::{ use nostr::{
Kind, Kind,
nips::nip19::{FromBech32, Nip19Coordinate}, nips::nip19::{FromBech32, Nip19Coordinate},
}; };
use super::Cli; use super::CliConfig;
use crate::{ use crate::{
cli::DEFAULT_FALLBACK_PATH, cli::DEFAULT_FALLBACK_PATH,
error::{N34Error, N34Result}, error::{N34Error, N34Result},
@@ -55,11 +58,14 @@ pub fn parse_nostr_address_file(file_path: &Path) -> N34Result<Vec<Nip19Coordina
Ok(addresses) Ok(addresses)
} }
/// Post parse cli arguments /// Loads CLI configuration from given path. Uses default config path if input
pub fn post_parse_cli(mut cli: Cli) -> N34Result<Cli> { /// matches fallback path.
if let Some(DEFAULT_FALLBACK_PATH) = cli.options.config_path.to_str() { pub fn parse_config_path(config_path: &str) -> N34Result<CliConfig> {
cli.options.config_path = super::defaults::config_path()?; let mut path = PathBuf::from(config_path.trim());
}
Ok(cli) if config_path == DEFAULT_FALLBACK_PATH {
path = super::defaults::config_path()?;
};
CliConfig::load_toml(path)
} }

View File

@@ -61,7 +61,7 @@ fn set_log_level(verbosity: Verbosity) {
#[tokio::main] #[tokio::main]
async fn main() -> ExitCode { async fn main() -> ExitCode {
let cli = match cli::parsers::post_parse_cli(Cli::parse()) { let cli = match cli::post_cli(Cli::parse()) {
Ok(cli) => cli, Ok(cli) => cli,
Err(err) => { Err(err) => {
eprintln!("{err}"); eprintln!("{err}");