refactor: remove dbus C dep

Signed-off-by: Awiteb <a@4rs.nl>
This commit is contained in:
Awiteb
2025-09-29 07:16:48 +00:00
parent d2adff7882
commit 463010c9f6
11 changed files with 1074 additions and 388 deletions

View File

@@ -34,6 +34,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Check the nip07 signer in `CliOptions::ensure_signer` - by Awiteb
- Required the `applied_commits` - by Awiteb
### Refactor
- Remove `dbus` C dep - by Awiteb
## [0.4.0] - 2025-08-08
### Added

1263
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -14,6 +14,7 @@ categories = ["command-line-utilities"]
rust-version = "1.88.0"
[dependencies]
async-utility = "0.3.1"
chrono = "0.4.41"
convert_case = "0.8.0"
dirs = "6.0.0"
@@ -28,6 +29,16 @@ toml = "0.9.4"
tracing = "0.1.41"
tracing-subscriber = "0.3.19"
[dependencies.keyring]
features = [
"apple-native",
"windows-native",
"async-secret-service",
"tokio",
"crypto-rust",
]
version = "3.6.3"
[dependencies.clap]
features = ["derive"]
version = "4.5.42"
@@ -46,10 +57,6 @@ tracing-subscriber = "0.3.19"
default-features = false
version = "0.43.0"
[dependencies.nostr-keyring]
default-features = false
version = "0.43.0"
[dependencies.nostr-sdk]
default-features = false
version = "0.43.0"

View File

@@ -23,11 +23,9 @@
# n34 development environment
devShells.default = mkShell {
packages = [
dbus
git-cliff
mdbook
nushell
pkg-config
taplo
];
@@ -50,14 +48,6 @@
cargoLock.lockFile = ./Cargo.lock;
src = lib.cleanSource ./.;
nativeBuildInputs = [
pkg-config
];
buildInputs = [
dbus
];
meta = {
inherit (manifest) description homepage;
license = lib.licenses.gpl3Plus;

View File

@@ -17,8 +17,9 @@
use clap::{ArgGroup, Args};
use crate::{
cli::{Cli, CliOptions, traits::CommandRunner},
cli::{CliOptions, traits::CommandRunner},
error::N34Result,
nostr_keyring,
};
#[derive(Args, Debug)]
@@ -46,8 +47,6 @@ impl CommandRunner for KeyringArgs {
const NEED_SIGNER: bool = false;
async fn run(self, mut options: CliOptions) -> N34Result<()> {
let keyring = nostr_keyring::NostrKeyring::new(Cli::N34_KEYRING_SERVICE_NAME);
if self.enable {
options.config.keyring_secret_key = true;
} else if self.disable {
@@ -55,7 +54,7 @@ impl CommandRunner for KeyringArgs {
}
if self.reset || self.disable {
let _ = keyring.delete(Cli::USER_KEY_PAIR_ENTRY);
let _ = nostr_keyring::user::delete().await;
}
options.config.dump()

View File

@@ -158,7 +158,7 @@ impl CliOptions {
if let Some(ref bunker_url) = self.bunker_url {
let mut nostrconnect = NostrConnect::new(
bunker_url.clone(),
Cli::n34_keypair()?,
Cli::n34_keypair().await?,
BUNKER_TIMEOUT,
None,
)

View File

@@ -42,8 +42,6 @@ use nostr::key::Keys;
use nostr::key::SecretKey;
use nostr_browser_signer_proxy::BrowserSignerProxy;
use nostr_browser_signer_proxy::BrowserSignerProxyOptions;
use nostr_keyring::KeyringError;
use nostr_keyring::NostrKeyring;
use types::RelayOrSet;
pub use self::commands::*;
@@ -52,7 +50,7 @@ use self::traits::CommandRunner;
use crate::cli::options_state::BROWSER_SIGNER_PROXY_TIMEOUT;
use crate::error::N34Error;
use crate::error::N34Result;
use crate::nostr_utils::traits::NostrKeyringErrorUtils;
use crate::nostr_keyring;
/// Header message, used in the help message
const HEADER: &str = r#"Copyright (C) 2025 Awiteb <a@4rs.nl>
@@ -86,13 +84,6 @@ pub struct Cli {
impl Cli {
/// Keyring service name of n34
pub const N34_KEYRING_SERVICE_NAME: &str = "n34";
/// Keyring entry name of the n34 keypair
pub const N34_KEY_PAIR_ENTRY: &str = "n34_keypair";
/// Keyring entry name of the user secret key
pub const USER_KEY_PAIR_ENTRY: &str = "user_keypair";
/// Executes the command
pub async fn run(self) -> N34Result<()> {
self.command.run(self.options).await
@@ -100,33 +91,30 @@ impl Cli {
/// Gets the n34 keypair from the keyring or generates and stores a new one
/// if none exists.
pub fn n34_keypair() -> N34Result<Keys> {
let keyring = NostrKeyring::new(Self::N34_KEYRING_SERVICE_NAME);
match keyring.get(Self::N34_KEY_PAIR_ENTRY) {
pub async fn n34_keypair() -> N34Result<Keys> {
match nostr_keyring::n34::get().await {
Ok(keys) => Ok(keys),
Err(nostr_keyring::Error::Keyring(KeyringError::NoEntry)) => {
Err(N34Error::Keyring(keyring::Error::NoEntry)) => {
let new_keys = Keys::generate();
keyring.set(Self::N34_KEY_PAIR_ENTRY, &new_keys)?;
nostr_keyring::n34::set(&new_keys).await?;
Ok(new_keys)
}
Err(err) => Err(N34Error::Keyring(err)),
Err(err) => Err(err),
}
}
/// Retrieves the user's keypair from the keyring. If no key exists and one
/// is provided, stores and returns it. If no key exists and none is
/// provided, returns an error.
pub fn user_keypair(secret_key: Option<SecretKey>) -> N34Result<Keys> {
let keyring = NostrKeyring::new(Self::N34_KEYRING_SERVICE_NAME);
let keyring_key = keyring.get(Self::USER_KEY_PAIR_ENTRY);
pub async fn user_keypair(secret_key: Option<SecretKey>) -> N34Result<Keys> {
let keyring_key = nostr_keyring::user::get().await;
if let Err(ref err) = keyring_key
&& err.is_keyring_no_entry()
&& let Some(secret_key) = secret_key
{
let keypair = Keys::new(secret_key);
keyring.set(Self::USER_KEY_PAIR_ENTRY, &keypair)?;
nostr_keyring::user::set(&keypair).await?;
return Ok(keypair);
}
@@ -134,7 +122,7 @@ impl Cli {
if err.is_keyring_no_entry() {
N34Error::SecretKeyKeyringWithoutEntry
} else {
N34Error::Keyring(err)
err
}
})
}
@@ -142,7 +130,7 @@ impl Cli {
/// Processes the CLI configuration by applying fallback values from config if
/// needed. Returns the processed Cli configuration if successful.
pub fn post_cli(mut cli: Cli) -> N34Result<Cli> {
pub async fn post_cli(mut cli: Cli) -> N34Result<Cli> {
cli.options.pow = cli.options.pow.or(cli.options.config.pow);
if cli.options.relays.is_empty()
@@ -169,7 +157,8 @@ pub fn post_cli(mut cli: Cli) -> N34Result<Cli> {
cli.options.bunker_url = Some(bunker_url.clone());
} else if cli.options.config.keyring_secret_key {
cli.options.secret_key = Some(
Cli::user_keypair(cli.options.secret_key)?
Cli::user_keypair(cli.options.secret_key)
.await?
.secret_key()
.clone(),
);

View File

@@ -51,8 +51,12 @@ pub enum N34Error {
Addr(#[from] AddrParseError),
#[error("Browser Signer Proxy Error: {0}")]
BrowserSignerProxy(#[from] nostr_browser_signer_proxy::Error),
#[error("Keys error: {0}")]
Keys(#[from] nostr::key::Error),
#[error("Keyring error: {0}")]
Keyring(#[from] nostr_keyring::Error),
Keyring(#[from] keyring::Error),
#[error("Join error: {0}")]
Join(#[from] tokio::task::JoinError),
#[error("{0}")]
Config(#[from] ConfigError),
#[error(
@@ -187,4 +191,10 @@ impl N34Error {
_ => ExitCode::FAILURE,
}
}
/// Checks if the error indicates a missing keyring entry.
#[inline]
pub fn is_keyring_no_entry(&self) -> bool {
matches!(self, Self::Keyring(keyring::Error::NoEntry))
}
}

View File

@@ -18,6 +18,8 @@
pub mod cli;
/// N34 errors
pub mod error;
/// Nostr keyring
pub mod nostr_keyring;
/// Nostr utils module
pub mod nostr_utils;
@@ -79,7 +81,7 @@ async fn try_main() -> error::N34Result<()> {
let guard =
tracing::subscriber::set_default(tracing_subscriber::registry().with(stderr_log_layer()));
let cli = cli::post_cli(Cli::parse())?;
let cli = cli::post_cli(Cli::parse()).await?;
let logs_file = cli::utils::logs_file()?;
// Replace the thread-local subscriber with a global default subscriber based on

98
src/nostr_keyring.rs Normal file
View File

@@ -0,0 +1,98 @@
// 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 async_utility::task;
use keyring::Entry;
use nostr::key::{Keys, SecretKey};
use crate::error::{N34Error, N34Result};
/// Keyring service name of n34
const N34_KEYRING_SERVICE_NAME: &str = "n34";
/// Keyring entry name of the n34 keypair
const N34_KEY_PAIR_ENTRY: &str = "n34_keypair";
/// Keyring entry name of the user secret key
const USER_KEY_PAIR_ENTRY: &str = "user_keypair";
/// Retrieves the keypair entry for `n34`.
#[inline]
fn n34_entry() -> keyring::Result<Entry> {
Entry::new(N34_KEYRING_SERVICE_NAME, N34_KEY_PAIR_ENTRY)
}
/// Retrieves the keypair entry for the user.
#[inline]
fn user_entry() -> keyring::Result<Entry> {
Entry::new(N34_KEYRING_SERVICE_NAME, USER_KEY_PAIR_ENTRY)
}
pub mod user {
use super::*;
/// Stores the user's keypair in the system.
pub async fn set(key: &Keys) -> N34Result<()> {
let private_key = key.secret_key().to_secret_bytes();
task::spawn_blocking(move || {
user_entry()?
.set_secret(&private_key)
.map_err(N34Error::from)
})
.await?
}
/// Retrieves the user's keypair from the system.
pub async fn get() -> N34Result<Keys> {
task::spawn_blocking(move || {
Ok(Keys::new(SecretKey::from_slice(
&user_entry()?.get_secret()?,
)?))
})
.await?
}
/// Delete the user's keypair from the system.
pub async fn delete() -> N34Result<()> {
task::spawn_blocking(move || user_entry()?.delete_credential().map_err(N34Error::from))
.await?
}
}
pub mod n34 {
use super::*;
/// Stores the `n34` client's keypair in the system.
pub async fn set(key: &Keys) -> N34Result<()> {
let private_key = key.secret_key().to_secret_bytes();
task::spawn_blocking(move || {
n34_entry()?
.set_secret(&private_key)
.map_err(N34Error::from)
})
.await?
}
/// Retrieves the `n34` client's keypair from the system.
pub async fn get() -> N34Result<Keys> {
task::spawn_blocking(move || {
Ok(Keys::new(SecretKey::from_slice(
&n34_entry()?.get_secret()?,
)?))
})
.await?
}
}

View File

@@ -31,7 +31,6 @@ use nostr::{
parser::Token,
types::{RelayUrl, Url},
};
use nostr_keyring::KeyringError;
use crate::cli::issue::ISSUE_ALT_PREFIX;
use crate::cli::patch::{
@@ -378,15 +377,6 @@ impl Event {
}
}
#[easy_ext::ext(NostrKeyringErrorUtils)]
impl nostr_keyring::Error {
/// Checks if the error indicates a missing keyring entry.
#[inline]
pub fn is_keyring_no_entry(&self) -> bool {
matches!(self, nostr_keyring::Error::Keyring(KeyringError::NoEntry))
}
}
#[easy_ext::ext(KindExt)]
impl Kind {
/// Checks if the kind is a root kind, such as GitIssue, GitPatch, GitPr or