diff --git a/Cargo.lock b/Cargo.lock index 70b07ceb5..55dc5ea9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3008,6 +3008,9 @@ dependencies = [ "cxx-build", "glob", "nostr-sdk", + "tokio", + "tracing", + "tracing-subscriber", ] [[package]] diff --git a/bindings/nostr-sdk-cpp/.gitignore b/bindings/nostr-sdk-cpp/.gitignore index 424c745c1..e69de29bb 100644 --- a/bindings/nostr-sdk-cpp/.gitignore +++ b/bindings/nostr-sdk-cpp/.gitignore @@ -1 +0,0 @@ -*.h diff --git a/bindings/nostr-sdk-cpp/Cargo.toml b/bindings/nostr-sdk-cpp/Cargo.toml index 9308f870e..e0428a8ce 100644 --- a/bindings/nostr-sdk-cpp/Cargo.toml +++ b/bindings/nostr-sdk-cpp/Cargo.toml @@ -11,6 +11,9 @@ crate-type = ["staticlib"] anyhow = "1.0" cxx = "1.0" nostr-sdk = { workspace = true, features = ["all-nips"] } +tokio = { workspace = true, features = ["full"] } +tracing = { workspace = true, features = ["std"] } +tracing-subscriber.workspace = true [build-dependencies] cxx-build = "1.0" diff --git a/bindings/nostr-sdk-cpp/build.rs b/bindings/nostr-sdk-cpp/build.rs index 3bdb37c8e..b340137e9 100644 --- a/bindings/nostr-sdk-cpp/build.rs +++ b/bindings/nostr-sdk-cpp/build.rs @@ -3,10 +3,18 @@ // Distributed under the MIT software license use std::fs; +use std::process::Command; use glob::glob; fn main() { + if let Ok(output) = Command::new("git").args(["rev-parse", "HEAD"]).output() { + if let Ok(git_hash) = String::from_utf8(output.stdout) { + println!("cargo:rerun-if-changed={git_hash}"); + println!("cargo:rustc-env=GIT_HASH={git_hash}"); + } + } + let mut files: Vec = Vec::new(); // Recursively find all .rs files in src/ directory diff --git a/bindings/nostr-sdk-cpp/examples/client.cpp b/bindings/nostr-sdk-cpp/examples/client.cpp new file mode 100644 index 000000000..db29dbcb5 --- /dev/null +++ b/bindings/nostr-sdk-cpp/examples/client.cpp @@ -0,0 +1,28 @@ +#include +#include +#include + +#include + +int main() { + try { + // Init logger + logger::init(logger::LogLevel::Debug); + + // New client without signer + auto client = client::without_signer(); + + // Add relays + client->add_relay("wss://relay.damus.io"); + + // Connect + client->connect(); + + // Send event + std::string jsonString = R"({"content":"Think about this.\n\nThe most powerful centralized institutions in the world have been replaced by a protocol that protects the individual. #bitcoin\n\nDo you doubt that we can replace everything else?\n\nBullish on the future of humanity\nnostr:nevent1qqs9ljegkuk2m2ewfjlhxy054n6ld5dfngwzuep0ddhs64gc49q0nmqpzdmhxue69uhhyetvv9ukzcnvv5hx7un8qgsw3mfhnrr0l6ll5zzsrtpeufckv2lazc8k3ru5c3wkjtv8vlwngksrqsqqqqqpttgr27","created_at":1703184271,"id":"38acf9b08d06859e49237688a9fd6558c448766f47457236c2331f93538992c6","kind":1,"pubkey":"e8ed3798c6ffebffa08501ac39e271662bfd160f688f94c45d692d8767dd345a","sig":"f76d5ecc8e7de688ac12b9d19edaacdcffb8f0c8fa2a44c00767363af3f04dbc069542ddc5d2f63c94cb5e6ce701589d538cf2db3b1f1211a96596fabb6ecafe","tags":[["e","5fcb28b72cadab2e4cbf7311f4acf5f6d1a99a1c2e642f6b6f0d5518a940f9ec","","mention"],["p","e8ed3798c6ffebffa08501ac39e271662bfd160f688f94c45d692d8767dd345a","","mention"],["t","bitcoin"],["t","bitcoin"]]})"; + client->send_event(jsonString); + } catch (const std::exception& e) { + std::cerr << "Error: " << e.what() << std::endl; + } + return 0; +} diff --git a/bindings/nostr-sdk-cpp/include/.gitkeep b/bindings/nostr-sdk-cpp/include/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/bindings/nostr-sdk-cpp/include/nostr_sdk.h b/bindings/nostr-sdk-cpp/include/nostr_sdk.h new file mode 100644 index 000000000..7468f1c3f --- /dev/null +++ b/bindings/nostr-sdk-cpp/include/nostr_sdk.h @@ -0,0 +1,7 @@ +#pragma once + +#include +#include +#include +#include +#include diff --git a/bindings/nostr-sdk-cpp/justfile b/bindings/nostr-sdk-cpp/justfile new file mode 100755 index 000000000..48a6d9023 --- /dev/null +++ b/bindings/nostr-sdk-cpp/justfile @@ -0,0 +1,7 @@ +#!/usr/bin/env just --justfile + +build: + cargo build --release + +example: build + g++ examples/client.cpp -o client -I include/ -I ../../target/x86_64-unknown-linux-gnu/cxxbridge/ -L ../../target/x86_64-unknown-linux-gnu/release/ -lnostr_sdk_cpp diff --git a/bindings/nostr-sdk-cpp/src/client/mod.rs b/bindings/nostr-sdk-cpp/src/client/mod.rs new file mode 100644 index 000000000..731bcb326 --- /dev/null +++ b/bindings/nostr-sdk-cpp/src/client/mod.rs @@ -0,0 +1,57 @@ +// Copyright (c) 2022-2023 Yuki Kishimoto +// Copyright (c) 2023-2024 Rust Nostr Developers +// Distributed under the MIT software license + +use anyhow::Result; +use cxx::CxxString; +use nostr_sdk::client; +use nostr_sdk::nostr::{Event, JsonUtil}; + +use crate::RUNTIME; + +#[cxx::bridge(namespace = "client")] +mod ffi { + extern "Rust" { + type Client; + + fn without_signer() -> Box; + + fn add_relay(&self, url: &CxxString) -> Result; + + fn connect(&self); + + fn send_event(&self, json: &CxxString) -> Result<()>; + } +} + +pub struct Client { + inner: client::Client, +} + +fn without_signer() -> Box { + Box::new(Client { + inner: client::Client::default(), + }) +} + +impl Client { + pub fn add_relay(&self, url: &CxxString) -> Result { + RUNTIME.block_on(async { + let url: &str = url.to_str()?; + Ok(self.inner.add_relay(url).await?) + }) + } + + pub fn connect(&self) { + RUNTIME.block_on(async { self.inner.connect().await }) + } + + pub fn send_event(&self, json: &CxxString) -> Result<()> { + RUNTIME.block_on(async { + let json: &str = json.to_str()?; + let event = Event::from_json(json)?; + self.inner.send_event(event).await?; + Ok(()) + }) + } +} diff --git a/bindings/nostr-sdk-cpp/src/lib.rs b/bindings/nostr-sdk-cpp/src/lib.rs index 3329762f6..30beef8db 100644 --- a/bindings/nostr-sdk-cpp/src/lib.rs +++ b/bindings/nostr-sdk-cpp/src/lib.rs @@ -2,4 +2,24 @@ // Copyright (c) 2023-2024 Rust Nostr Developers // Distributed under the MIT software license +use std::sync::LazyLock; + +use tokio::runtime::Runtime; + +pub mod client; +pub mod logger; pub mod protocol; + +static RUNTIME: LazyLock = + LazyLock::new(|| Runtime::new().expect("failed to create tokio runtime")); + +pub fn git_hash_version() -> String { + option_env!("GIT_HASH").unwrap_or_default().to_owned() +} + +#[cxx::bridge] +mod ffi { + extern "Rust" { + fn git_hash_version() -> String; + } +} diff --git a/bindings/nostr-sdk-cpp/src/logger.rs b/bindings/nostr-sdk-cpp/src/logger.rs new file mode 100644 index 000000000..9a7c245db --- /dev/null +++ b/bindings/nostr-sdk-cpp/src/logger.rs @@ -0,0 +1,51 @@ +// Copyright (c) 2022-2023 Yuki Kishimoto +// Copyright (c) 2023-2024 Rust Nostr Developers +// Distributed under the MIT software license + +use tracing::Level; + +use super::git_hash_version; + +#[cxx::bridge(namespace = "logger")] +mod ffi { + enum LogLevel { + Error, + Warn, + Info, + Debug, + Trace, + } + + extern "Rust" { + fn init(level: LogLevel); + } +} + +impl From for Level { + fn from(value: ffi::LogLevel) -> Self { + match value { + ffi::LogLevel::Trace => Self::TRACE, + ffi::LogLevel::Debug => Self::DEBUG, + ffi::LogLevel::Info => Self::INFO, + ffi::LogLevel::Warn => Self::WARN, + ffi::LogLevel::Error => Self::ERROR, + _ => unreachable!(), + } + } +} + +pub fn init(level: ffi::LogLevel) { + let level: Level = level.into(); + let subscriber = tracing_subscriber::FmtSubscriber::builder() + .with_max_level(level) + .finish(); + match tracing::subscriber::set_global_default(subscriber) { + Ok(_) => { + tracing::info!("Desktop logger initialized"); + + // Log git hash (defined at compile time) + tracing::info!("Git hash: {}", git_hash_version()) + } + Err(e) => eprintln!("Impossible to init desktop logger: {e}"), + } +}