From b9a88672b8734448615354e3f46748d2fdc2f647 Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Sun, 1 Oct 2023 00:00:00 +0100 Subject: feat(prs-create) send commit to relay - add client - use client to send event - add async functionality - enabler for relay interaction whilst getting cli input --- Cargo.lock | 215 +++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 6 +- src/client.rs | 65 +++++++++++++ src/main.rs | 6 +- src/sub_commands/prs/create.rs | 20 +++- src/sub_commands/prs/mod.rs | 4 +- test_utils/Cargo.toml | 2 +- 7 files changed, 305 insertions(+), 13 deletions(-) create mode 100644 src/client.rs diff --git a/Cargo.lock b/Cargo.lock index 219c02f..15bc112 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -267,6 +267,29 @@ dependencies = [ "syn 2.0.38", ] +[[package]] +name = "async-utility" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3716c0d3970fe92d79a8f4cda2caf91113574505dff5b18e455e549d4b078e98" +dependencies = [ + "futures-util", + "gloo-timers", + "tokio", + "wasm-bindgen-futures", +] + +[[package]] +name = "async_io_stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" +dependencies = [ + "futures", + "pharos", + "rustc_version", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -334,6 +357,7 @@ dependencies = [ "bitcoin_hashes 0.12.0", "hex_lit", "secp256k1", + "serde", ] [[package]] @@ -650,6 +674,12 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "data-encoding" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" + [[package]] name = "derivative" version = "2.2.0" @@ -1001,6 +1031,18 @@ dependencies = [ "url", ] +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "h2" version = "0.3.21" @@ -1444,15 +1486,18 @@ version = "0.0.1" dependencies = [ "anyhow", "assert_cmd", + "async-trait", "chacha20poly1305", "clap", "dialoguer", "directories", "duplicate", + "futures", "git2", "keyring", "mockall", "nostr", + "nostr-sdk", "once_cell", "passwords", "rexpect 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1461,6 +1506,7 @@ dependencies = [ "serde_json", "serial_test", "test_utils", + "tokio", "zeroize", ] @@ -1499,25 +1545,56 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "nostr" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525a8f75106f4eeb1fedaacadc61547548fe4715c3edde7d03eed2900b467952" +checksum = "df0af088a37ea0026bf96dcd66db8bf21ed7ff528b7cbe34b7f32f6af3ae14a0" dependencies = [ "aes 0.8.3", "base64", - "bech32", "bip39", "bitcoin", - "bitcoin_hashes 0.12.0", "cbc", + "chacha20", "getrandom", "instant", + "once_cell", "reqwest", - "secp256k1", "serde", "serde_json", "tracing", - "url", + "url-fork", +] + +[[package]] +name = "nostr-sdk" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5facab78c73baf3853f8c807006e23567dd3825392ef13a3a07122f4ce18b56d" +dependencies = [ + "async-utility", + "nostr", + "nostr-sdk-net", + "once_cell", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "nostr-sdk-net" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4058b0267a1537c25b4674db9ed7a18152fc4c33df246dd4a88701007084ee" +dependencies = [ + "futures-util", + "thiserror", + "tokio", + "tokio-rustls", + "tokio-socks", + "tokio-tungstenite", + "url-fork", + "webpki-roots", + "ws_stream_wasm", ] [[package]] @@ -1596,6 +1673,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "object" version = "0.32.1" @@ -1716,6 +1803,16 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +[[package]] +name = "pharos" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" +dependencies = [ + "futures", + "rustc_version", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -2084,6 +2181,15 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "0.37.24" @@ -2248,6 +2354,18 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" + [[package]] name = "serde" version = "1.0.188" @@ -2555,11 +2673,24 @@ dependencies = [ "bytes", "libc", "mio", + "num_cpus", "pin-project-lite", "socket2 0.5.4", + "tokio-macros", "windows-sys 0.48.0", ] +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "tokio-rustls" version = "0.24.1" @@ -2582,6 +2713,21 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-tungstenite" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" +dependencies = [ + "futures-util", + "log", + "rustls", + "tokio", + "tokio-rustls", + "tungstenite", + "webpki-roots", +] + [[package]] name = "tokio-util" version = "0.7.9" @@ -2657,6 +2803,26 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +[[package]] +name = "tungstenite" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "rustls", + "sha1", + "thiserror", + "url", + "utf-8", +] + [[package]] name = "typenum" version = "1.17.0" @@ -2725,9 +2891,27 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", +] + +[[package]] +name = "url-fork" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956afc9d7e101f0b718a6776489cd7998d0b17fc79f4cdb6ee6761fb72d1c2ce" +dependencies = [ + "form_urlencoded", + "percent-encoding", "serde", + "unicode-bidi", + "unicode-normalization", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8parse" version = "0.2.1" @@ -3051,6 +3235,25 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "ws_stream_wasm" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7999f5f4217fe3818726b66257a4475f71e74ffd190776ad053fa159e50737f5" +dependencies = [ + "async_io_stream", + "futures", + "js-sys", + "log", + "pharos", + "rustc_version", + "send_wrapper", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "xdg-home" version = "1.0.0" diff --git a/Cargo.toml b/Cargo.toml index 1b2c458..f7577a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,17 +13,21 @@ categories = ["command-line-utilities","git"] [dependencies] anyhow = "1.0.75" +async-trait = "0.1.73" chacha20poly1305 = "0.10.1" clap = { version = "4.3.19", features = ["derive"] } dialoguer = "0.10.4" directories = "5.0.1" +futures = "0.3.28" git2 = "0.18.1" keyring = "2.0.5" -nostr = "0.23.0" +nostr = "0.24.0" +nostr-sdk = "0.24.0" passwords = "3.1.13" scrypt = "0.11.0" serde = { version = "1.0.181", features = ["derive"] } serde_json = "1.0.105" +tokio = "1.33.0" zeroize = "1.6.0" [dev-dependencies] diff --git a/src/client.rs b/src/client.rs new file mode 100644 index 0000000..a6f7dda --- /dev/null +++ b/src/client.rs @@ -0,0 +1,65 @@ +// have you considered + +// TO USE ASYNC + +// in traits (required for mocking unit tests) +// https://rust-lang.github.io/async-book/07_workarounds/05_async_in_traits.html +// https://github.com/dtolnay/async-trait +// see https://blog.rust-lang.org/inside-rust/2022/11/17/async-fn-in-trait-nightly.html +// I think we can use the async-trait crate and switch to the native feature +// which is currently in nightly. alternatively we can use nightly as it looks +// certain that the implementation is going to make it to stable but we don't +// want to inadvertlty use other features of nightly that might be removed. +use anyhow::Result; +use async_trait::async_trait; +#[cfg(test)] +use mockall::*; +use nostr::Event; + +pub struct Client { + client: nostr_sdk::Client, +} + +#[async_trait] +#[cfg_attr(test, automock)] +pub trait Connect { + fn default() -> Self; + fn new(opts: Params) -> Self; + async fn connect(&self) -> Result<()>; + async fn send_event_to(&self, url: &str, event: nostr::event::Event) -> Result; +} + +#[async_trait] +impl Connect for Client { + fn default() -> Self { + Client { + client: nostr_sdk::Client::new(&nostr::Keys::generate()), + } + } + fn new(opts: Params) -> Self { + Client { + client: nostr_sdk::Client::new(&opts.keys.unwrap_or(nostr::Keys::generate())), + } + } + async fn connect(&self) -> Result<()> { + self.client.add_relay("ws://localhost:8080", None).await?; + self.client.connect().await; + // self.client.s + Ok(()) + } + async fn send_event_to(&self, url: &str, event: Event) -> Result { + Ok(self.client.send_event_to(url, event).await?) + } +} + +#[derive(Default)] +pub struct Params { + pub keys: Option, +} + +impl Params { + pub fn with_keys(mut self, keys: nostr::Keys) -> Self { + self.keys = Some(keys); + self + } +} diff --git a/src/main.rs b/src/main.rs index 5094c11..9c37aa7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ use anyhow::Result; use clap::{Parser, Subcommand}; mod cli_interactor; +mod client; mod config; mod git; mod key_handling; @@ -33,10 +34,11 @@ enum Commands { Prs(sub_commands::prs::SubCommandArgs), } -fn main() -> Result<()> { +#[tokio::main] +async fn main() -> Result<()> { let cli = Cli::parse(); match &cli.command { Commands::Login(args) => sub_commands::login::launch(&cli, args), - Commands::Prs(args) => sub_commands::prs::launch(&cli, args), + Commands::Prs(args) => futures::executor::block_on(sub_commands::prs::launch(&cli, args)), } } diff --git a/src/sub_commands/prs/create.rs b/src/sub_commands/prs/create.rs index dd32c65..89ea652 100644 --- a/src/sub_commands/prs/create.rs +++ b/src/sub_commands/prs/create.rs @@ -3,6 +3,7 @@ use nostr::{prelude::sha1::Hash as Sha1Hash, EventBuilder, Marker, Tag, TagKind} use crate::{ cli_interactor::{Interactor, InteractorPrompt, PromptConfirmParms, PromptInputParms}, + client::{Client, Connect, Params as ClientParams}, git::{Repo, RepoActions}, login, Cli, }; @@ -23,7 +24,7 @@ pub struct SubCommandArgs { to_branch: Option, } -pub fn launch( +pub async fn launch( cli_args: &Cli, _pr_args: &super::SubCommandArgs, args: &SubCommandArgs, @@ -81,6 +82,7 @@ pub fn launch( let root_commit = git_repo .get_root_commit(to_branch.as_str()) .context("failed to get root commit of the repository")?; + // create PR event let keys = login::launch(&cli_args.nsec, &cli_args.password)?; @@ -138,7 +140,23 @@ pub fn launch( ); } + let client = Client::new(ClientParams::default().with_keys(keys)); + + println!("connecting..."); + client.connect().await?; + println!("connected..."); + // TODO check if there is already a similarly named PR + let _ = client + .send_event_to("ws://localhost:8080", pr_event) + .await?; + // TODO post each PR + // TODO report + println!("posted successfully to 4/5 of your relays and 0/4 of maintainers relays"); + // should we have a relays in Repository event? + // yes + // + // TODO connect to relays and post Ok(()) diff --git a/src/sub_commands/prs/mod.rs b/src/sub_commands/prs/mod.rs index c316e73..982e866 100644 --- a/src/sub_commands/prs/mod.rs +++ b/src/sub_commands/prs/mod.rs @@ -15,8 +15,8 @@ pub enum Commands { Create(create::SubCommandArgs), } -pub fn launch(cli_args: &Cli, pr_args: &SubCommandArgs) -> Result<()> { +pub async fn launch(cli_args: &Cli, pr_args: &SubCommandArgs) -> Result<()> { match &pr_args.prs_command { - Commands::Create(args) => create::launch(cli_args, pr_args, args), + Commands::Create(args) => create::launch(cli_args, pr_args, args).await, } } diff --git a/test_utils/Cargo.toml b/test_utils/Cargo.toml index 3d936b5..1773d93 100644 --- a/test_utils/Cargo.toml +++ b/test_utils/Cargo.toml @@ -9,7 +9,7 @@ assert_cmd = "2.0.12" dialoguer = "0.10.4" directories = "5.0.1" git2 = "0.18.1" -nostr = "0.23.0" +nostr = "0.24.0" once_cell = "1.18.0" rand = "0.8" rexpect = { git = "https://github.com/phaer/rexpect.git", branch= "skip-ansi-escape-codes" } -- cgit v1.2.3