Skip to content

Commit

Permalink
feat(plugin): support binding options from init method
Browse files Browse the repository at this point in the history
Signed-off-by: Vincenzo Palazzo <[email protected]>
  • Loading branch information
vincenzopalazzo committed Jan 7, 2023
1 parent 47ac171 commit 75ce542
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 28 deletions.
2 changes: 1 addition & 1 deletion plugin/examples/foo_plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ impl RPCCommand<PluginState> for OnChannelOpened {
}

fn main() {
let mut plugin = Plugin::<PluginState>::new(PluginState(()), true)
let plugin = Plugin::<PluginState>::new(PluginState(()), true)
.add_rpc_method(
"hello",
"",
Expand Down
20 changes: 18 additions & 2 deletions plugin/src/commands/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
//! by core lightning in other to configure the plugin at startup.
//!
//! author: https://github.com/vincenzopalazzo
use std::collections::HashMap;

use crate::commands::{
types::{RPCHookInfo, RPCMethodInfo},
RPCCommand,
Expand All @@ -12,6 +14,8 @@ use crate::types::RpcOption;
use clightningrpc_common::json_utils::{add_bool, add_vec, init_payload};
use serde_json::Value;

use super::types::InitConf;

#[derive(Clone)]
/// Type to define the manifest method and its attributes, used during plugin initialization
pub struct ManifestRPC {}
Expand All @@ -22,7 +26,7 @@ impl<T: Clone> RPCCommand<T> for ManifestRPC {
add_vec::<RpcOption>(
&mut response,
"options",
plugin.option.clone().into_iter().collect(),
plugin.option.clone().into_iter().map(|it| it.1).collect(),
);
add_vec::<RPCMethodInfo>(
&mut response,
Expand Down Expand Up @@ -51,10 +55,22 @@ pub struct InitRPC<T: 'static + Clone> {
pub(crate) on_init: Option<&'static OnInit<T>>,
}

impl<T: Clone> InitRPC<T> {
fn parse_option(&self, plugin: &mut Plugin<T>, options: &HashMap<String, serde_json::Value>) {
for option_name in options.keys() {
let option = options.get(option_name).unwrap();
plugin.option.get_mut(option_name).unwrap().value = Some(option.to_owned());
}
}
}

impl<T: Clone> RPCCommand<T> for InitRPC<T> {
fn call<'c>(&self, plugin: &mut Plugin<T>, request: &'c Value) -> Result<Value, PluginError> {
let response = init_payload();
plugin.conf = serde_json::from_value(request.to_owned()).unwrap();
let init: InitConf = serde_json::from_value(request.to_owned()).unwrap();
plugin.configuration = Some(init.configuration);
self.parse_option(plugin, &init.options);

if let Some(callback) = self.on_init {
(*callback)(plugin);
}
Expand Down
8 changes: 4 additions & 4 deletions plugin/src/commands/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ pub struct RPCHookInfo {

#[derive(Deserialize, Clone)]
/// Type to define attributes for the plugin's init method
pub struct InitConf {
pub options: serde_json::Value,
pub configuration: ConfFiled,
pub(crate) struct InitConf {
pub options: HashMap<String, serde_json::Value>,
pub configuration: CLNConf,
}

#[derive(Deserialize, Clone)]
/// Type to define the configuration options for the plugin's init method
pub struct ConfFiled {
pub struct CLNConf {
#[serde(rename = "lightning-dir")]
pub lightning_dir: String,
#[serde(rename = "rpc-file")]
Expand Down
52 changes: 32 additions & 20 deletions plugin/src/plugin.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
//! Core of the plugin API
//!
//! Unofficial API interface to develop plugin in Rust.
use crate::commands::{
builtin::{InitRPC, ManifestRPC},
types::{InitConf, RPCHookInfo, RPCMethodInfo},
RPCCommand,
};
use crate::commands::builtin::{InitRPC, ManifestRPC};
use crate::commands::types::{CLNConf, RPCHookInfo, RPCMethodInfo};
use crate::commands::RPCCommand;
use crate::errors::PluginError;
use crate::types::{LogLevel, RpcOption};
use clightningrpc_common::json_utils::{add_str, init_payload, init_success_response};
Expand All @@ -27,7 +25,7 @@ where
pub state: T,
/// all the option contained inside the
/// hash map.
pub option: HashSet<RpcOption>,
pub option: HashMap<String, RpcOption>,
/// all the options rpc method that the
/// plugin need to support, included the builtin rpc method.
pub rpc_method: HashMap<String, Box<dyn RPCCommand<T>>>,
Expand All @@ -44,24 +42,24 @@ where
/// mark a plugin as dynamic, in this way the plugin can be run
/// from core lightning without stop the lightningd daemon
pub dynamic: bool,
/// plugin configuration given by core lightning
pub conf: Option<InitConf>,
/// onInit callback called when the method on init is runned.
/// core lightning configuration sent with the init call.
pub configuration: Option<CLNConf>,
/// onInit callback called when the method on init is ran.
on_init: Option<&'static OnInit<T>>,
}

impl<'a, T: 'a + Clone> Plugin<T> {
pub fn new(state: T, dynamic: bool) -> Self {
Plugin {
state,
option: HashSet::new(),
option: HashMap::new(),
rpc_method: HashMap::new(),
rpc_info: HashSet::new(),
rpc_hook: HashMap::new(),
hook_info: HashSet::new(),
rpc_notification: HashMap::new(),
dynamic,
conf: None,
configuration: None,
on_init: None,
}
}
Expand Down Expand Up @@ -93,6 +91,7 @@ impl<'a, T: 'a + Clone> Plugin<T> {
writer.flush().unwrap();
}

/// register the plugin option.
pub fn add_opt(
&mut self,
name: &str,
Expand All @@ -101,16 +100,29 @@ impl<'a, T: 'a + Clone> Plugin<T> {
description: &str,
deprecated: bool,
) -> &mut Self {
self.option.insert(RpcOption {
name: name.to_string(),
opt_typ: opt_type.to_string(),
default: def_val,
description: description.to_string(),
deprecated,
});
self.option.insert(
name.to_owned(),
RpcOption {
name: name.to_string(),
opt_typ: opt_type.to_string(),
default: def_val,
description: description.to_string(),
deprecated,
value: None,
},
);
self
}

/// get an option value that cln sent back to the plugin.
pub fn get_opt<R: for<'de> serde::de::Deserialize<'de>>(
&self,
name: &str,
) -> Result<R, PluginError> {
let opt = self.option.get(name).unwrap();
Ok(opt.value())
}

// FIXME: adding the long description as parameter
pub fn add_rpc_method<F: 'static>(
&'a mut self,
Expand Down Expand Up @@ -195,7 +207,7 @@ impl<'a, T: 'a + Clone> Plugin<T> {
}
}

pub fn start(&'a mut self) {
pub fn start(mut self) {
let reader = io::stdin();
let mut writer = io::stdout();
let mut buffer = String::new();
Expand All @@ -220,7 +232,7 @@ impl<'a, T: 'a + Clone> Plugin<T> {
buffer.clear();
let request: Request<serde_json::Value> = serde_json::from_str(&req_str).unwrap();
if let Some(id) = request.id {
// whe the id is specified this is a RPC or Hook, so we need to return a response
// 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);
Expand Down
12 changes: 11 additions & 1 deletion plugin/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
//! types
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::fmt;

#[derive(Deserialize, Serialize, Clone, Eq, Hash, PartialEq)]
#[derive(Deserialize, Serialize, Clone)]
pub struct RpcOption {
/// option name that is specified by the
/// core lightning user, like --foo
Expand All @@ -18,6 +19,15 @@ pub struct RpcOption {
pub description: String,
/// if the filed is deprecated
pub deprecated: bool,
/// The value specified by the user
pub value: Option<Value>,
}

impl RpcOption {
pub fn value<T: for<'de> serde::de::Deserialize<'de>>(&self) -> T {
let value: T = serde_json::from_value(self.value.to_owned().unwrap()).unwrap();
value
}
}

pub enum LogLevel {
Expand Down

0 comments on commit 75ce542

Please sign in to comment.