From 0fa30964375f03aa90e723da70dcde68140bc9f1 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Mon, 25 Dec 2023 19:47:13 +0100 Subject: [PATCH 1/5] feat(plugin): add async io for the plugin Adding the support of the async io for reading to the std io. Link: https://github.com/laanwj/cln4rust/issues/98 Signed-off-by: Vincenzo Palazzo --- plugin/Cargo.toml | 3 +- plugin/src/io.rs | 77 ++++++++++++++++++++++++++++++++++++++++++++ plugin/src/lib.rs | 1 + plugin/src/plugin.rs | 51 +++++++++++++++-------------- 4 files changed, 106 insertions(+), 26 deletions(-) create mode 100644 plugin/src/io.rs diff --git a/plugin/Cargo.toml b/plugin/Cargo.toml index d7f4f0a..d21e696 100644 --- a/plugin/Cargo.toml +++ b/plugin/Cargo.toml @@ -11,10 +11,11 @@ keywords = [ "plugin", "cln", "rpc", "lightning", "bitcoin" ] readme = "README.md" [dependencies] +clightningrpc-common = { version = "0.3.0-beta.4" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -clightningrpc-common = { version = "0.3.0-beta.4" } log = { version = "0.4.17", optional = true } +mio = { version = "0.8.10", features = ["os-ext"] } [features] log = ["dep:log"] diff --git a/plugin/src/io.rs b/plugin/src/io.rs new file mode 100644 index 0000000..9192d13 --- /dev/null +++ b/plugin/src/io.rs @@ -0,0 +1,77 @@ +//! async io module of the plugin io. +//! +//! Vincenzo Palazzo +use std::io; +use std::io::{Read, Write}; +use std::os::fd::AsRawFd; + +const IN: mio::Token = mio::Token(0); + +pub(crate) struct AsyncIO { + poll: mio::Poll, +} + +impl AsyncIO { + /// Create a new instance of an AsyncIO + pub fn new() -> io::Result { + Ok(Self { + poll: mio::Poll::new()?, + }) + } + + pub fn register(&mut self) -> io::Result<()> { + let stdin = std::io::stdin().as_raw_fd(); + let mut stdin = mio::unix::SourceFd(&stdin); + + self.poll.registry().register( + &mut stdin, + IN, + mio::Interest::READABLE | mio::Interest::WRITABLE, + )?; + Ok(()) + } + + pub fn into_loop Option>( + &mut self, + mut async_callback: F, + ) -> io::Result<()> { + let mut events = mio::Events::with_capacity(1024); + loop { + self.poll.poll(&mut events, None)?; + for event in events.iter() { + #[cfg(feature = "log")] + log::info!("getting the event: {:?}", event); + match event.token() { + IN => { + if event.is_readable() { + let mut reader = io::stdin().lock(); + let mut buffer = String::new(); + loop { + let mut byte = [0; 1]; + reader.read_exact(&mut byte).unwrap(); + + // Append the byte to the buffer + buffer.push(byte[0] as char); + + // Check if the buffer ends with double newline + if buffer.ends_with("\n\n") { + drop(reader); + break; // Exit the loop + } + } + let Some(resp) = async_callback(buffer.clone()) else { + continue; + }; + let mut writer = io::stdout().lock(); + writer.write_all(resp.as_bytes())?; + writer.flush()?; + } + } + _ => unreachable!(), + } + #[cfg(feature = "log")] + log::info!("event handled: {:?}", event); + } + } + } +} diff --git a/plugin/src/lib.rs b/plugin/src/lib.rs index c10d189..dff9fde 100644 --- a/plugin/src/lib.rs +++ b/plugin/src/lib.rs @@ -9,6 +9,7 @@ #![crate_name = "clightningrpc_plugin"] pub mod commands; pub mod errors; +mod io; pub mod macros; pub mod plugin; pub mod types; diff --git a/plugin/src/plugin.rs b/plugin/src/plugin.rs index ccca087..5826fa7 100644 --- a/plugin/src/plugin.rs +++ b/plugin/src/plugin.rs @@ -7,14 +7,16 @@ use std::io::Write; use std::string::String; use std::sync::Arc; +use serde_json::Value; + use clightningrpc_common::json_utils::{add_str, init_payload, init_success_response}; use clightningrpc_common::types::Request; -use serde_json::Value; use crate::commands::builtin::{InitRPC, ManifestRPC}; use crate::commands::types::{CLNConf, RPCHookInfo, RPCMethodInfo}; use crate::commands::RPCCommand; use crate::errors::PluginError; +use crate::io::AsyncIO; use crate::types::{LogLevel, RpcOption}; #[cfg(feature = "log")] @@ -64,10 +66,10 @@ impl log::Log for Log { fn log(&self, record: &Record) { if self.enabled(record.metadata()) { + let mut writer = io::stdout().lock(); let level: LogLevel = record.level().into(); let msg = record.args(); - let mut writer = io::stdout(); let mut payload = init_payload(); add_str(&mut payload, "level", &level.to_string()); add_str(&mut payload, "message", &format!("{msg}")); @@ -77,10 +79,8 @@ impl log::Log for Log { method: "log".to_owned(), params: payload, }; - writer - .write_all(serde_json::to_string(&request).unwrap().as_bytes()) - .unwrap(); - writer.flush().unwrap(); + let _ = writer.write_all(serde_json::to_string(&request).unwrap().as_bytes()); + let _ = writer.flush(); } } @@ -112,7 +112,7 @@ impl<'a, T: 'a + Clone> Plugin { } pub fn log(&self, level: LogLevel, msg: &str) { - let mut writer = io::stdout(); + let mut writer = io::stdout().lock(); let mut payload = init_payload(); add_str(&mut payload, "level", &level.to_string()); add_str(&mut payload, "message", msg); @@ -256,10 +256,7 @@ impl<'a, T: 'a + Clone> Plugin { } } - pub fn start(mut self) { - let reader = io::stdin(); - let mut writer = io::stdout(); - let mut buffer = String::new(); + pub fn start(mut self) -> io::Result<()> { #[cfg(feature = "log")] { use std::str::FromStr; @@ -276,29 +273,33 @@ impl<'a, T: 'a + Clone> Plugin { on_init: self.on_init.clone(), }), ); - // FIXME: core lightning end with the double endline, so this can cause - // problem for some input reader. - // we need to parse the writer, and avoid this while loop - while let Ok(_) = reader.read_line(&mut buffer) { - let req_str = buffer.to_string(); - buffer.clear(); - let Ok(request) = serde_json::from_str::>(&req_str) else { - continue; - }; + let mut asyncio = AsyncIO::new()?; + asyncio.register()?; + asyncio.into_loop(|buffer| { + #[cfg(feature = "log")] + log::info!("looping around the string: {buffer}"); + let request: Request = serde_json::from_str(&buffer).unwrap(); if let Some(id) = request.id { // when the id is specified this is a RPC or Hook, so we need to return a response let response = self.call_rpc_method(&request.method, request.params); let mut rpc_response = init_success_response(id); self.write_respose(&response, &mut rpc_response); - writer - .write_all(serde_json::to_string(&rpc_response).unwrap().as_bytes()) - .unwrap(); - writer.flush().unwrap(); + #[cfg(feature = "log")] + log::info!( + "rpc or hook: {} with reponse {:?}", + request.method, + rpc_response + ); + return Some(serde_json::to_string(&rpc_response).unwrap()); } else { // in case of the id is None, we are receiving the notification, so the server is not // interested in the answer. self.handle_notification(&request.method, request.params); + #[cfg(feature = "log")] + log::info!("notification: {}", request.method); + return None; } - } + })?; + Ok(()) } } From 7138010c40625d59da6bd62499377951a7bdeafd Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Sun, 24 Mar 2024 02:30:12 +0100 Subject: [PATCH 2/5] plugin: deny unwrap call Signed-off-by: Vincenzo Palazzo --- plugin/src/lib.rs | 1 + plugin/src/plugin.rs | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugin/src/lib.rs b/plugin/src/lib.rs index dff9fde..5bb3d2c 100644 --- a/plugin/src/lib.rs +++ b/plugin/src/lib.rs @@ -7,6 +7,7 @@ //! //! author and mantainer: Vincenzo Palazzo https://github.com/vincenzopalazzo #![crate_name = "clightningrpc_plugin"] +#![deny(clippy::unwrap_used)] pub mod commands; pub mod errors; mod io; diff --git a/plugin/src/plugin.rs b/plugin/src/plugin.rs index 5826fa7..28c0d4d 100644 --- a/plugin/src/plugin.rs +++ b/plugin/src/plugin.rs @@ -122,10 +122,9 @@ impl<'a, T: 'a + Clone> Plugin { method: "log".to_owned(), params: payload, }; - writer - .write_all(serde_json::to_string(&request).unwrap().as_bytes()) - .unwrap(); - writer.flush().unwrap(); + // We do not like unwrap there + let _ = writer.write_all(serde_json::to_string(&request).unwrap().as_bytes()); + let _ = writer.flush(); } /// register the plugin option. From 3dda4c738763a22ad0a642b24ba157e52b0cd63f Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Sun, 24 Mar 2024 16:57:41 +0100 Subject: [PATCH 3/5] io: manage would block event Signed-off-by: Vincenzo Palazzo --- plugin/src/io.rs | 128 ++++++++++++++++++++++++++++--------------- plugin/src/plugin.rs | 14 +++-- 2 files changed, 92 insertions(+), 50 deletions(-) diff --git a/plugin/src/io.rs b/plugin/src/io.rs index 9192d13..9a2bcfc 100644 --- a/plugin/src/io.rs +++ b/plugin/src/io.rs @@ -1,77 +1,115 @@ //! async io module of the plugin io. //! //! Vincenzo Palazzo -use std::io; -use std::io::{Read, Write}; +use std::io::Write; +use std::io::{self, Read}; use std::os::fd::AsRawFd; +use std::time::Duration; -const IN: mio::Token = mio::Token(0); +const SERVER_TOKEN: mio::Token = mio::Token(0); -pub(crate) struct AsyncIO { +pub struct AsyncIO { poll: mio::Poll, + events: mio::Events, } impl AsyncIO { - /// Create a new instance of an AsyncIO pub fn new() -> io::Result { Ok(Self { poll: mio::Poll::new()?, + events: mio::Events::with_capacity(1024), }) } - pub fn register(&mut self) -> io::Result<()> { + pub fn register(&self) -> io::Result<()> { let stdin = std::io::stdin().as_raw_fd(); let mut stdin = mio::unix::SourceFd(&stdin); - - self.poll.registry().register( - &mut stdin, - IN, - mio::Interest::READABLE | mio::Interest::WRITABLE, - )?; + self.poll + .registry() + .register(&mut stdin, SERVER_TOKEN, mio::Interest::READABLE)?; Ok(()) } - pub fn into_loop Option>( - &mut self, - mut async_callback: F, - ) -> io::Result<()> { - let mut events = mio::Events::with_capacity(1024); + pub fn into_loop(&mut self, mut async_callback: F) -> io::Result<()> + where + F: FnMut(String) -> Option, + { loop { - self.poll.poll(&mut events, None)?; - for event in events.iter() { - #[cfg(feature = "log")] - log::info!("getting the event: {:?}", event); + self.poll + .poll(&mut self.events, Some(Duration::from_millis(100)))?; + for event in self.events.iter() { match event.token() { - IN => { + SERVER_TOKEN => { if event.is_readable() { - let mut reader = io::stdin().lock(); - let mut buffer = String::new(); - loop { - let mut byte = [0; 1]; - reader.read_exact(&mut byte).unwrap(); - - // Append the byte to the buffer - buffer.push(byte[0] as char); - - // Check if the buffer ends with double newline - if buffer.ends_with("\n\n") { - drop(reader); - break; // Exit the loop - } - } - let Some(resp) = async_callback(buffer.clone()) else { - continue; - }; - let mut writer = io::stdout().lock(); - writer.write_all(resp.as_bytes())?; - writer.flush()?; + self.handle_connection(&mut async_callback)?; } } _ => unreachable!(), } - #[cfg(feature = "log")] - log::info!("event handled: {:?}", event); } } } + + fn handle_connection(&self, async_callback: &mut F) -> io::Result<()> + where + F: FnMut(String) -> Option, + { + loop { + let mut reader = io::stdin().lock(); + let mut buffer = String::new(); + loop { + let mut byte = [0; 1]; + crate::poll_check!(reader.read_exact(&mut byte))?; + + // Append the byte to the buffer + buffer.push(byte[0] as char); + + // Check if the buffer ends with double newline + if buffer.ends_with("\n\n") { + break; // Exit the loop + } + } + + if let Some(resp) = async_callback(buffer.clone()) { + let mut writer = io::stdout().lock(); + crate::poll_check!(writer.write_all(resp.as_bytes()))?; + crate::poll_check!(writer.flush())?; + } + } + Ok(()) + } +} + +#[macro_export] +macro_rules! poll_check { + ($expr:expr) => {{ + match $expr { + Ok(val) => Ok::<_, std::io::Error>(val), + Err(ref err) if err.kind() == std::io::ErrorKind::WouldBlock => { + // Handle WouldBlock error + // For example, wait for readiness event and retry + // You may need to use mio's event loop to wait for readiness + // and then retry the operation + // For simplicity, we'll just continue the loop here + break; + } + Err(err) => Err(err.into()), + } + }}; +} + +#[macro_export] +macro_rules! poll_loop { + ($code:block) => {{ + while let Err(ref err) = $code { + if err.kind() == std::io::ErrorKind::WouldBlock { + // Handle WouldBlock error + // For example, wait for readiness event and retry + // You may need to use mio's event loop to wait for readiness + // and then retry the operation + // For simplicity, we'll just continue the loop here + continue; + } + } + }}; } diff --git a/plugin/src/plugin.rs b/plugin/src/plugin.rs index 28c0d4d..8000557 100644 --- a/plugin/src/plugin.rs +++ b/plugin/src/plugin.rs @@ -79,8 +79,11 @@ impl log::Log for Log { method: "log".to_owned(), params: payload, }; - let _ = writer.write_all(serde_json::to_string(&request).unwrap().as_bytes()); - let _ = writer.flush(); + + crate::poll_loop!({ + writer.write_all(serde_json::to_string(&request).unwrap().as_bytes()) + }); + crate::poll_loop!({ writer.flush() }); } } @@ -122,9 +125,10 @@ impl<'a, T: 'a + Clone> Plugin { method: "log".to_owned(), params: payload, }; - // We do not like unwrap there - let _ = writer.write_all(serde_json::to_string(&request).unwrap().as_bytes()); - let _ = writer.flush(); + crate::poll_loop!({ + writer.write_all(serde_json::to_string(&request).unwrap().as_bytes()) + }); + crate::poll_loop!({ writer.flush() }); } /// register the plugin option. From 12505dc1033bf3e82e20a4bee3941017bc0bc511 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Sun, 24 Mar 2024 18:10:44 +0100 Subject: [PATCH 4/5] clippy: fix warnings Signed-off-by: Vincenzo Palazzo --- plugin/examples/foo_plugin.rs | 7 +++++-- plugin/src/commands/mod.rs | 2 +- plugin/src/plugin.rs | 10 +++++----- plugin_macros/src/attr_parser.rs | 2 +- plugin_macros/src/plugin.rs | 17 ++++------------- tests/src/lib.rs | 8 ++++---- 6 files changed, 20 insertions(+), 26 deletions(-) diff --git a/plugin/examples/foo_plugin.rs b/plugin/examples/foo_plugin.rs index 2c01ad6..42c1692 100644 --- a/plugin/examples/foo_plugin.rs +++ b/plugin/examples/foo_plugin.rs @@ -1,5 +1,7 @@ extern crate clightningrpc_plugin; +use std::io; + use clightningrpc_plugin::types::LogLevel; use clightningrpc_plugin::{commands::RPCCommand, errors::PluginError, plugin::Plugin}; use serde_json::{json, Value}; @@ -35,7 +37,7 @@ impl RPCCommand for OnShutdown { } } -fn main() { +fn main() -> io::Result<()> { let plugin = Plugin::::new(PluginState(()), true) .add_rpc_method( "hello", @@ -56,5 +58,6 @@ fn main() { json!({}) }) .clone(); - plugin.start(); + plugin.start()?; + Ok(()) } diff --git a/plugin/src/commands/mod.rs b/plugin/src/commands/mod.rs index 304c4dc..d85123f 100644 --- a/plugin/src/commands/mod.rs +++ b/plugin/src/commands/mod.rs @@ -24,7 +24,7 @@ pub trait RPCCommand: RPCCommandClone { } /// void call is a generic method that it is used to simulate a callback with a void return type - fn call_void<'c>(&self, _plugin: &mut Plugin, _request: &'c serde_json::Value) {} + fn call_void(&self, _plugin: &mut Plugin, _request: &serde_json::Value) {} } // Splitting RPCCommandClone into its own trait allows us to provide a blanket diff --git a/plugin/src/plugin.rs b/plugin/src/plugin.rs index 8000557..95be857 100644 --- a/plugin/src/plugin.rs +++ b/plugin/src/plugin.rs @@ -142,10 +142,10 @@ impl<'a, T: 'a + Clone> Plugin { ) -> &mut Self { let def_val = match opt_type { "flag" | "bool" => { - def_val.and_then(|val| Some(serde_json::json!(val.parse::().unwrap()))) + def_val.map(|val| serde_json::json!(val.parse::().unwrap())) } - "int" => def_val.and_then(|val| Some(serde_json::json!(val.parse::().unwrap()))), - "string" => def_val.and_then(|val| Some(serde_json::json!(val))), + "int" => def_val.map(|val| serde_json::json!(val.parse::().unwrap())), + "string" => def_val.map(|val| serde_json::json!(val)), _ => unreachable!("{opt_type} not supported"), }; self.option.insert( @@ -293,14 +293,14 @@ impl<'a, T: 'a + Clone> Plugin { request.method, rpc_response ); - return Some(serde_json::to_string(&rpc_response).unwrap()); + Some(serde_json::to_string(&rpc_response).unwrap()) } else { // in case of the id is None, we are receiving the notification, so the server is not // interested in the answer. self.handle_notification(&request.method, request.params); #[cfg(feature = "log")] log::info!("notification: {}", request.method); - return None; + None } })?; Ok(()) diff --git a/plugin_macros/src/attr_parser.rs b/plugin_macros/src/attr_parser.rs index 3f2a0f6..8287c33 100644 --- a/plugin_macros/src/attr_parser.rs +++ b/plugin_macros/src/attr_parser.rs @@ -36,7 +36,7 @@ fn parse_key_values( trace!(tracer, "removing the `,` tok"); check!(",", stream.advance())?; } - let value = value.to_string().replace("\"", ""); + let value = value.to_string().replace('\"', ""); trace!(tracer, "key {key} = value {value}"); hash_map.insert(key.to_string(), value.to_string()); trace!(tracer, "map is {:?}", hash_map); diff --git a/plugin_macros/src/plugin.rs b/plugin_macros/src/plugin.rs index 2fc3d51..7bf1ef8 100644 --- a/plugin_macros/src/plugin.rs +++ b/plugin_macros/src/plugin.rs @@ -6,6 +6,7 @@ use kproc_parser::proc_macro::{TokenStream, TokenTree}; use kproc_parser::{build_error, check, trace}; #[derive(Debug)] +#[derive(Default)] pub struct PluginDeclaration { pub state: Option, pub dynamic: Option, @@ -27,7 +28,7 @@ impl std::fmt::Display for PluginDeclaration { .map_or(String::from("false"), |val| val.to_string()) )?; if let Some(ref inner) = self.notificatios { - let mut inner = KTokenStream::new(&inner); + let mut inner = KTokenStream::new(inner); while !inner.is_end() { let notification = inner.advance(); writeln!(f, "let call = {}();", notification)?; @@ -42,7 +43,7 @@ impl std::fmt::Display for PluginDeclaration { } } if let Some(ref inner) = self.rpc_methods { - let mut inner = KTokenStream::new(&inner); + let mut inner = KTokenStream::new(inner); while !inner.is_end() { let rpc = inner.advance(); writeln!(f, "let call = {}();", rpc)?; @@ -58,17 +59,7 @@ impl std::fmt::Display for PluginDeclaration { } } -impl Default for PluginDeclaration { - fn default() -> Self { - Self { - state: None, - dynamic: None, - notificatios: None, - hooks: None, - rpc_methods: None, - } - } -} + /// proc macro syntax is something like this /// diff --git a/tests/src/lib.rs b/tests/src/lib.rs index 5daf845..d3a3e8d 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -36,14 +36,14 @@ pub mod fixtures { pub fn lightningd() -> cln::Node { init(); let pwd = std::env::var("PWD").unwrap(); - let cln = async_run!(cln::Node::with_params(&format!("--developer --plugin={pwd}/target/debug/examples/foo_plugin --plugin={pwd}/target/debug/examples/macros_ex"), "regtest")).unwrap(); - cln + + async_run!(cln::Node::with_params(&format!("--developer --plugin={pwd}/target/debug/examples/foo_plugin --plugin={pwd}/target/debug/examples/macros_ex"), "regtest")).unwrap() } #[fixture] pub fn lightningd_second() -> cln::Node { init(); - let cln = async_run!(cln::Node::with_params("--developer", "regtest")).unwrap(); - cln + + async_run!(cln::Node::with_params("--developer", "regtest")).unwrap() } } From e048ae0aa16f700b0d4498be1c57f7e2d9f7f7fe Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Sun, 24 Mar 2024 18:25:37 +0100 Subject: [PATCH 5/5] clippy: add safety comment for unwrap calls Signed-off-by: Vincenzo Palazzo --- plugin/src/commands/builtin.rs | 11 ++++++++++- plugin/src/commands/mod.rs | 2 +- plugin/src/io.rs | 1 + plugin/src/plugin.rs | 15 +++++++++++++++ plugin_macros/src/plugin.rs | 5 +---- tests/src/lib.rs | 4 ++-- 6 files changed, 30 insertions(+), 8 deletions(-) diff --git a/plugin/src/commands/builtin.rs b/plugin/src/commands/builtin.rs index c1eebe9..7dd2366 100644 --- a/plugin/src/commands/builtin.rs +++ b/plugin/src/commands/builtin.rs @@ -51,14 +51,21 @@ impl RPCCommand for ManifestRPC { #[derive(Clone)] /// Type to define the init method and its attributes, used in plugin pub struct InitRPC { + #[allow(clippy::type_complexity)] pub(crate) on_init: Option) -> Value>>, } impl InitRPC { fn parse_option(&self, plugin: &mut Plugin, options: &HashMap) { for option_name in options.keys() { + // SAFETY: We are iterating over the key this never None + #[allow(clippy::unwrap_used)] let option = options.get(option_name).unwrap(); - plugin.option.get_mut(option_name).unwrap().value = Some(option.to_owned()); + // SAFETY: we put them into it so it is safe to unwrap. + // If we panic this mean that there is a bug + #[allow(clippy::unwrap_used)] + let opt = plugin.option.get_mut(option_name).unwrap(); + opt.value = Some(option.to_owned()); } } } @@ -66,6 +73,8 @@ impl InitRPC { impl RPCCommand for InitRPC { fn call<'c>(&self, plugin: &mut Plugin, request: Value) -> Result { let mut response = init_payload(); + // SAFETY: Shouwl be valid json so should be safe to unwrap + #[allow(clippy::unwrap_used)] let init: InitConf = serde_json::from_value(request.to_owned()).unwrap(); plugin.configuration = Some(init.configuration); self.parse_option(plugin, &init.options); diff --git a/plugin/src/commands/mod.rs b/plugin/src/commands/mod.rs index d85123f..4caee2a 100644 --- a/plugin/src/commands/mod.rs +++ b/plugin/src/commands/mod.rs @@ -15,7 +15,7 @@ use crate::errors::PluginError; /// in contrast, it is more complex but the plugin_macros package will help to simplify the API. pub trait RPCCommand: RPCCommandClone { /// call is a generic method that it is used to simulate the callback. - fn call<'c>( + fn call( &self, _: &mut Plugin, _: serde_json::Value, diff --git a/plugin/src/io.rs b/plugin/src/io.rs index 9a2bcfc..74f57df 100644 --- a/plugin/src/io.rs +++ b/plugin/src/io.rs @@ -30,6 +30,7 @@ impl AsyncIO { Ok(()) } + #[allow(clippy::wrong_self_convention)] pub fn into_loop(&mut self, mut async_callback: F) -> io::Result<()> where F: FnMut(String) -> Option, diff --git a/plugin/src/plugin.rs b/plugin/src/plugin.rs index 95be857..ab6522f 100644 --- a/plugin/src/plugin.rs +++ b/plugin/src/plugin.rs @@ -126,6 +126,8 @@ impl<'a, T: 'a + Clone> Plugin { params: payload, }; crate::poll_loop!({ + // SAFETY: it is valid json and if we panic there is a buf somewhere + #[allow(clippy::unwrap_used)] writer.write_all(serde_json::to_string(&request).unwrap().as_bytes()) }); crate::poll_loop!({ writer.flush() }); @@ -142,8 +144,12 @@ impl<'a, T: 'a + Clone> Plugin { ) -> &mut Self { let def_val = match opt_type { "flag" | "bool" => { + // FIXME: remove unwrap and return the error + #[allow(clippy::unwrap_used)] def_val.map(|val| serde_json::json!(val.parse::().unwrap())) } + // FIXME: remove unwrap and return the error + #[allow(clippy::unwrap_used)] "int" => def_val.map(|val| serde_json::json!(val.parse::().unwrap())), "string" => def_val.map(|val| serde_json::json!(val)), _ => unreachable!("{opt_type} not supported"), @@ -212,6 +218,9 @@ impl<'a, T: 'a + Clone> Plugin { } fn handle_notification(&'a mut self, name: &str, params: serde_json::Value) { + // SAFETY: we register the notification and if we do not have inside the map + // this is a bug. + #[allow(clippy::unwrap_used)] let notification = self.rpc_notification.get(name).unwrap().clone(); notification.call_void(self, ¶ms); } @@ -253,6 +262,8 @@ impl<'a, T: 'a + Clone> Plugin { match result { Ok(json_resp) => response["result"] = json_resp.to_owned(), Err(json_err) => { + // SAFETY: should be valud json + #[allow(clippy::unwrap_used)] let err_resp = serde_json::to_value(json_err).unwrap(); response["error"] = err_resp; } @@ -281,6 +292,8 @@ impl<'a, T: 'a + Clone> Plugin { asyncio.into_loop(|buffer| { #[cfg(feature = "log")] log::info!("looping around the string: {buffer}"); + // SAFETY: should be valud json + #[allow(clippy::unwrap_used)] let request: Request = serde_json::from_str(&buffer).unwrap(); if let Some(id) = request.id { // when the id is specified this is a RPC or Hook, so we need to return a response @@ -293,6 +306,8 @@ impl<'a, T: 'a + Clone> Plugin { request.method, rpc_response ); + // SAFETY: should be valud json + #[allow(clippy::unwrap_used)] Some(serde_json::to_string(&rpc_response).unwrap()) } else { // in case of the id is None, we are receiving the notification, so the server is not diff --git a/plugin_macros/src/plugin.rs b/plugin_macros/src/plugin.rs index 7bf1ef8..f8d89d1 100644 --- a/plugin_macros/src/plugin.rs +++ b/plugin_macros/src/plugin.rs @@ -5,8 +5,7 @@ use kproc_parser::kproc_macros::KTokenStream; use kproc_parser::proc_macro::{TokenStream, TokenTree}; use kproc_parser::{build_error, check, trace}; -#[derive(Debug)] -#[derive(Default)] +#[derive(Debug, Default)] pub struct PluginDeclaration { pub state: Option, pub dynamic: Option, @@ -59,8 +58,6 @@ impl std::fmt::Display for PluginDeclaration { } } - - /// proc macro syntax is something like this /// /// ```ignore diff --git a/tests/src/lib.rs b/tests/src/lib.rs index d3a3e8d..b0a2fa3 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -36,14 +36,14 @@ pub mod fixtures { pub fn lightningd() -> cln::Node { init(); let pwd = std::env::var("PWD").unwrap(); - + async_run!(cln::Node::with_params(&format!("--developer --plugin={pwd}/target/debug/examples/foo_plugin --plugin={pwd}/target/debug/examples/macros_ex"), "regtest")).unwrap() } #[fixture] pub fn lightningd_second() -> cln::Node { init(); - + async_run!(cln::Node::with_params("--developer", "regtest")).unwrap() } }