Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plugin config options with no defaults #5369

Merged
merged 2 commits into from
Nov 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions plugins/examples/cln-plugin-startup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,17 @@ async fn main() -> Result<(), anyhow::Error> {
options::Value::Integer(42),
"a test-option with default 42",
))
.option(options::ConfigOption::new(
"opt-option",
options::Value::OptInteger,
"An optional option",
))
.rpcmethod("testmethod", "This is a test", testmethod)
.rpcmethod(
"testoptions",
"Retrieve options from this plugin",
testoptions,
)
.subscribe("connect", connect_handler)
.hook("peer_connected", peer_connected_handler)
.start(state)
Expand All @@ -26,6 +36,12 @@ async fn main() -> Result<(), anyhow::Error> {
}
}

async fn testoptions(p: Plugin<()>, _v: serde_json::Value) -> Result<serde_json::Value, Error> {
Ok(json!({
"opt-option": format!("{:?}", p.option("opt-option").unwrap())
}))
}

async fn testmethod(_p: Plugin<()>, _v: serde_json::Value) -> Result<serde_json::Value, Error> {
Ok(json!("Hello"))
}
Expand Down
32 changes: 20 additions & 12 deletions plugins/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt};
extern crate log;
use log::trace;
use messages::Configuration;
use options::ConfigOption;
use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
Expand All @@ -14,7 +15,6 @@ use tokio::sync::Mutex;
use tokio_stream::StreamExt;
use tokio_util::codec::FramedRead;
use tokio_util::codec::FramedWrite;
use options::ConfigOption;

pub mod codec;
pub mod logging;
Expand All @@ -25,7 +25,6 @@ extern crate serde_json;

pub mod options;


/// Need to tell us about something that went wrong? Use this error
/// type to do that. Use this alias to be safe from future changes in
/// our internal error handling, since we'll implement any necessary
Expand Down Expand Up @@ -329,16 +328,25 @@ where
// Match up the ConfigOptions and fill in their values if we
// have a matching entry.
for opt in self.options.iter_mut() {
if let Some(val) = call.options.get(opt.name()) {
opt.value = Some(match (opt.default(), &val) {
(OValue::String(_), JValue::String(s)) => OValue::String(s.clone()),
(OValue::Integer(_), JValue::Number(n)) => OValue::Integer(n.as_i64().unwrap()),
(OValue::Boolean(_), JValue::Bool(n)) => OValue::Boolean(*n),

// It's ok to panic, if we get here Core Lightning
// has not enforced the option type.
(_, _) => panic!("Mismatching types in options: {:?} != {:?}", opt, val),
});
let val = call.options.get(opt.name());
opt.value = match (&opt, &opt.default(), &val) {
(_, OValue::String(_), Some(JValue::String(s))) => Some(OValue::String(s.clone())),
(_, OValue::OptString, Some(JValue::String(s))) => Some(OValue::String(s.clone())),
(_, OValue::OptString, None) => None,

(_, OValue::Integer(_), Some(JValue::Number(s))) => {
Some(OValue::Integer(s.as_i64().unwrap()))
}
(_, OValue::OptInteger, Some(JValue::Number(s))) => {
Some(OValue::Integer(s.as_i64().unwrap()))
}
(_, OValue::OptInteger, None) => None,

(_, OValue::Boolean(_), Some(JValue::Bool(s))) => Some(OValue::Boolean(*s)),
(_, OValue::OptBoolean, Some(JValue::Bool(s))) => Some(OValue::Boolean(*s)),
(_, OValue::OptBoolean, None) => None,

(o, _, _) => panic!("Type mismatch for option {:?}", o),
}
}

Expand Down
15 changes: 13 additions & 2 deletions plugins/src/options.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use serde::ser::{SerializeStruct, Serializer};
use serde::{Serialize};
use serde::Serialize;

#[derive(Clone, Debug)]
pub enum Value {
String(String),
Integer(i64),
Boolean(bool),
OptString,
OptInteger,
OptBoolean,
}

/// An stringly typed option that is passed to
Expand Down Expand Up @@ -45,11 +48,19 @@ impl Serialize for ConfigOption {
s.serialize_field("type", "int")?;
s.serialize_field("default", i)?;
}

Value::Boolean(b) => {
s.serialize_field("type", "bool")?;
s.serialize_field("default", b)?;
}
Value::OptString => {
s.serialize_field("type", "string")?;
}
Value::OptInteger => {
s.serialize_field("type", "int")?;
}
Value::OptBoolean => {
s.serialize_field("type", "bool")?;
}
}

s.serialize_field("description", &self.description)?;
Expand Down
14 changes: 14 additions & 0 deletions tests/test_cln_rs.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,20 @@ def test_plugin_start(node_factory):
l1.daemon.wait_for_log(r'Got a connect notification')


def test_plugin_optional_opts(node_factory):
"""Start a minimal plugin and ensure it is well-behaved
"""
bin_path = Path.cwd() / "target" / "debug" / "examples" / "cln-plugin-startup"
l1 = node_factory.get_node(options={"plugin": str(bin_path), 'opt-option': 31337})
opts = l1.rpc.testoptions()
print(opts)

# Do not set any value, should be None now
l1 = node_factory.get_node(options={"plugin": str(bin_path)})
opts = l1.rpc.testoptions()
print(opts)


def test_grpc_connect(node_factory):
"""Attempts to connect to the grpc interface and call getinfo"""
# These only exist if we have rust!
Expand Down