mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-02-22 06:41:44 +01:00
cln_plugin: Request value as rust primitive
In the old version requesting the config-value of an option was a little bit tricky. Let's say we want to have a plugin which uses a default port of 1234. ```rust let value = plugin.option("plugin-port"); match value { Some(Value::Integer(_)) => {}, Some(Value::String(_)) => {}, // Can never happen Some(Value::Boolean(_)) => {}, // Can never happen None => {}, // Can never happen } ``` Many users of the `cln_plugin` crate are overly cautious and handle all these error scenario's. Which is completely unneeded. Core Lightning will complain if you put a `String` where an `Integer` is expected and will never send the value to the plug-in. This change makes the API much more ergonomical and actually motivates some of the changes in previous commits. ``` const MY_OPTION : ConfigOption<i64> = ConfigOption::new_i64_with_default( "plugin-port', 1235, "Description"); let value : Result<i64> = plugin.option(MY_OPTION); ``` The result will provide a proper error-message. It is also safe to `unwrap` the result because it will only be triggered if the user neglected to provide the option to the `Builder`.
This commit is contained in:
parent
543e67495c
commit
74d13bb334
4 changed files with 295 additions and 143 deletions
|
@ -47,7 +47,7 @@ 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())
|
||||
"opt-option": format!("{:?}", p.option_str("opt-option").unwrap())
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use anyhow::{anyhow, Context, Result};
|
||||
use anyhow::{Context, Result};
|
||||
use cln_grpc::pb::node_server::NodeServer;
|
||||
use cln_plugin::{options, Builder};
|
||||
use log::{debug, warn};
|
||||
|
@ -14,6 +14,10 @@ struct PluginState {
|
|||
ca_cert: Vec<u8>,
|
||||
}
|
||||
|
||||
const OPTION_GRPC_PORT : options::IntegerConfigOption = options::ConfigOption::new_i64_no_default(
|
||||
"grpc-port",
|
||||
"Which port should the grpc plugin listen for incoming connections?");
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> Result<()> {
|
||||
debug!("Starting grpc plugin");
|
||||
|
@ -21,11 +25,7 @@ async fn main() -> Result<()> {
|
|||
let directory = std::env::current_dir()?;
|
||||
|
||||
let plugin = match Builder::new(tokio::io::stdin(), tokio::io::stdout())
|
||||
.option(options::ConfigOption::new_i64_with_default(
|
||||
"grpc-port",
|
||||
-1,
|
||||
"Which port should the grpc plugin listen for incoming connections?",
|
||||
))
|
||||
.option(OPTION_GRPC_PORT)
|
||||
.configure()
|
||||
.await?
|
||||
{
|
||||
|
@ -33,17 +33,15 @@ async fn main() -> Result<()> {
|
|||
None => return Ok(()),
|
||||
};
|
||||
|
||||
let bind_port = match plugin.option("grpc-port") {
|
||||
Some(options::Value::Integer(-1)) => {
|
||||
log::info!("`grpc-port` option is not configured, exiting.");
|
||||
let bind_port = match plugin.option(&OPTION_GRPC_PORT).unwrap() {
|
||||
Some(port) => port,
|
||||
None => {
|
||||
log::info!("'grpc-port' options i not configured. exiting.");
|
||||
plugin
|
||||
.disable("`grpc-port` option is not configured.")
|
||||
.disable("Missing 'grpc-port' option")
|
||||
.await?;
|
||||
return Ok(());
|
||||
return Ok(())
|
||||
}
|
||||
Some(options::Value::Integer(i)) => i,
|
||||
None => return Err(anyhow!("Missing 'grpc-port' option")),
|
||||
Some(o) => return Err(anyhow!("grpc-port is not a valid integer: {:?}", o)),
|
||||
};
|
||||
|
||||
let (identity, ca_cert) = tls::init(&directory)?;
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use crate::codec::{JsonCodec, JsonRpcCodec};
|
||||
pub use anyhow::anyhow;
|
||||
use anyhow::Context;
|
||||
use anyhow::{Context, Result};
|
||||
use futures::sink::SinkExt;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
extern crate log;
|
||||
use log::trace;
|
||||
use messages::{Configuration, FeatureBits, NotificationTopic};
|
||||
use options::UntypedConfigOption;
|
||||
use options::{OptionType, UntypedConfigOption};
|
||||
use std::collections::HashMap;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
|
@ -44,6 +44,7 @@ where
|
|||
|
||||
hooks: HashMap<String, Hook<S>>,
|
||||
options: HashMap<String, UntypedConfigOption>,
|
||||
option_values: HashMap<String, Option<options::Value>>,
|
||||
rpcmethods: HashMap<String, RpcMethod<S>>,
|
||||
subscriptions: HashMap<String, Subscription<S>>,
|
||||
notifications: Vec<NotificationTopic>,
|
||||
|
@ -66,6 +67,7 @@ where
|
|||
input: FramedRead<I, JsonRpcCodec>,
|
||||
output: Arc<Mutex<FramedWrite<O, JsonCodec>>>,
|
||||
options: HashMap<String, UntypedConfigOption>,
|
||||
option_values: HashMap<String, Option<options::Value>>,
|
||||
configuration: Configuration,
|
||||
rpcmethods: HashMap<String, AsyncCallback<S>>,
|
||||
hooks: HashMap<String, AsyncCallback<S>>,
|
||||
|
@ -100,6 +102,7 @@ where
|
|||
state: S,
|
||||
/// "options" field of "init" message sent by cln
|
||||
options: HashMap<String, UntypedConfigOption>,
|
||||
option_values: HashMap<String, Option<options::Value>>,
|
||||
/// "configuration" field of "init" message sent by cln
|
||||
configuration: Configuration,
|
||||
/// A signal that allows us to wait on the plugin's shutdown.
|
||||
|
@ -121,6 +124,9 @@ where
|
|||
hooks: HashMap::new(),
|
||||
subscriptions: HashMap::new(),
|
||||
options: HashMap::new(),
|
||||
// Should not be configured by user.
|
||||
// This values are set when parsing the init-call
|
||||
option_values: HashMap::new(),
|
||||
rpcmethods: HashMap::new(),
|
||||
notifications: vec![],
|
||||
featurebits: FeatureBits::default(),
|
||||
|
@ -334,6 +340,7 @@ where
|
|||
notifications: self.notifications,
|
||||
subscriptions,
|
||||
options: self.options,
|
||||
option_values: self.option_values,
|
||||
configuration,
|
||||
hooks: HashMap::new(),
|
||||
}))
|
||||
|
@ -390,26 +397,21 @@ where
|
|||
|
||||
// Match up the ConfigOptions and fill in their values if we
|
||||
// have a matching entry.
|
||||
for (_name, opt) in self.options.iter_mut() {
|
||||
let val = call.options.get(opt.name());
|
||||
for (name, option) in self.options.iter() {
|
||||
let json_value = call.options.get(name);
|
||||
let default_value = option.default();
|
||||
|
||||
opt.value = match (&opt.value, &opt.default(), &val) {
|
||||
(_, Some(OValue::String(_)), Some(JValue::String(s))) => {
|
||||
Some(OValue::String(s.clone()))
|
||||
}
|
||||
(_, None, Some(JValue::String(s))) => Some(OValue::String(s.clone())),
|
||||
(_, None, None) => None,
|
||||
let option_value: Option<options::Value> = match (json_value, default_value) {
|
||||
(None, None) => None,
|
||||
(None, Some(default)) => Some(default.clone()),
|
||||
(Some(JValue::String(s)), _) => Some(OValue::String(s.to_string())),
|
||||
(Some(JValue::Number(i)), _) => Some(OValue::Integer(i.as_i64().unwrap())),
|
||||
(Some(JValue::Bool(b)), _) => Some(OValue::Boolean(*b)),
|
||||
_ => panic!("Type mismatch for option {}", name),
|
||||
};
|
||||
|
||||
(_, Some(OValue::Integer(_)), Some(JValue::Number(s))) => {
|
||||
Some(OValue::Integer(s.as_i64().unwrap()))
|
||||
}
|
||||
(_, None, Some(JValue::Number(s))) => Some(OValue::Integer(s.as_i64().unwrap())),
|
||||
(_, Some(OValue::Boolean(_)), Some(JValue::Bool(s))) => Some(OValue::Boolean(*s)),
|
||||
(_, None, Some(JValue::Bool(s))) => Some(OValue::Boolean(*s)),
|
||||
(o, _, _) => panic!("Type mismatch for option {:?}", o),
|
||||
}
|
||||
self.option_values.insert(name.to_string(), option_value);
|
||||
}
|
||||
|
||||
Ok(call.configuration)
|
||||
}
|
||||
}
|
||||
|
@ -505,8 +507,19 @@ impl<S> Plugin<S>
|
|||
where
|
||||
S: Clone + Send,
|
||||
{
|
||||
pub fn option(&self, name: &str) -> Option<options::Value> {
|
||||
self.options.get(name).and_then(|x| x.value.clone())
|
||||
pub fn option_str(&self, name: &str) -> Result<Option<options::Value>> {
|
||||
self.option_values
|
||||
.get(name)
|
||||
.ok_or(anyhow!("No option named {}", name))
|
||||
.map(|c| c.clone())
|
||||
}
|
||||
|
||||
pub fn option<OV: OptionType>(
|
||||
&self,
|
||||
config_option: &options::ConfigOption<OV>,
|
||||
) -> Result<OV::OutputValue> {
|
||||
let value = self.option_str(config_option.name())?;
|
||||
Ok(OV::from_value(&value))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -529,6 +542,7 @@ where
|
|||
let plugin = Plugin {
|
||||
state,
|
||||
options: self.options,
|
||||
option_values: self.option_values,
|
||||
configuration: self.configuration,
|
||||
wait_handle,
|
||||
sender,
|
||||
|
@ -590,8 +604,19 @@ where
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn option(&self, name: &str) -> Option<options::Value> {
|
||||
self.options.get(name).and_then(|c| c.value.clone())
|
||||
pub fn option_str(&self, name: &str) -> Result<Option<options::Value>> {
|
||||
self.option_values
|
||||
.get(name)
|
||||
.ok_or(anyhow!("No option named '{}'", name))
|
||||
.map(|c| c.clone())
|
||||
}
|
||||
|
||||
pub fn option<OV: OptionType>(
|
||||
&self,
|
||||
config_option: &options::ConfigOption<OV>,
|
||||
) -> Result<OV::OutputValue> {
|
||||
let value = self.option_str(config_option.name())?;
|
||||
Ok(OV::from_value(&value))
|
||||
}
|
||||
|
||||
/// return the cln configuration send to the
|
||||
|
|
|
@ -1,54 +1,185 @@
|
|||
use anyhow::Result;
|
||||
use serde::ser::{SerializeStruct, Serializer};
|
||||
use serde::ser::Serializer;
|
||||
use serde::Serialize;
|
||||
|
||||
// Marker trait for possible values of options
|
||||
pub mod config_type {
|
||||
pub struct Integer;
|
||||
pub struct DefaultInteger;
|
||||
pub struct String;
|
||||
pub struct DefaultString;
|
||||
pub struct Boolean;
|
||||
pub struct DefaultBoolean;
|
||||
pub struct Flag;
|
||||
}
|
||||
|
||||
pub type IntegerConfigOption<'a> = ConfigOption<'a, config_type::Integer>;
|
||||
pub type StringConfigOption<'a> = ConfigOption<'a, config_type::String>;
|
||||
pub type BooleanConfigOption<'a> = ConfigOption<'a, config_type::Boolean>;
|
||||
|
||||
pub type DefaultIntegerConfigOption<'a> = ConfigOption<'a, config_type::DefaultInteger>;
|
||||
pub type DefaultStringConfigOption<'a> = ConfigOption<'a, config_type::DefaultString>;
|
||||
pub type DefaultBooleanConfigOption<'a> = ConfigOption<'a, config_type::DefaultBoolean>;
|
||||
/// Config value is represented as a flag
|
||||
pub type FlagConfigOption<'a> = ConfigOption<'a, config_type::Flag>;
|
||||
|
||||
|
||||
pub trait OptionType {
|
||||
fn convert_default(value: Option<&Self>) -> Option<Value>;
|
||||
type OutputValue;
|
||||
type DefaultValue;
|
||||
|
||||
fn convert_default(value: &Self::DefaultValue) -> Option<Value>;
|
||||
|
||||
fn from_value(value: &Option<Value>) -> Self::OutputValue;
|
||||
|
||||
fn get_value_type() -> ValueType;
|
||||
}
|
||||
|
||||
impl OptionType for &str {
|
||||
fn convert_default(value: Option<&Self>) -> Option<Value> {
|
||||
value.map(|s| Value::String(s.to_string()))
|
||||
impl OptionType for config_type::DefaultString {
|
||||
type OutputValue = String;
|
||||
type DefaultValue = &'static str;
|
||||
|
||||
fn convert_default(value: &Self::DefaultValue) -> Option<Value> {
|
||||
Some(Value::String(value.to_string()))
|
||||
}
|
||||
|
||||
fn from_value(value: &Option<Value>) -> Self::OutputValue {
|
||||
match value {
|
||||
Some(Value::String(s)) => s.to_string(),
|
||||
_ => panic!("Type mismatch. Expected string but found {:?}", value),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_value_type() -> ValueType {
|
||||
ValueType::String
|
||||
}
|
||||
}
|
||||
|
||||
impl OptionType for String {
|
||||
fn convert_default(value: Option<&Self>) -> Option<Value> {
|
||||
value.map(|s| Value::String(s.clone()))
|
||||
impl OptionType for config_type::DefaultInteger {
|
||||
type OutputValue = i64;
|
||||
type DefaultValue = i64;
|
||||
|
||||
fn convert_default(value: &Self::DefaultValue) -> Option<Value> {
|
||||
Some(Value::Integer(*value))
|
||||
}
|
||||
}
|
||||
impl OptionType for i64 {
|
||||
fn convert_default(value: Option<&Self>) -> Option<Value> {
|
||||
value.map(|i| Value::Integer(*i))
|
||||
|
||||
fn from_value(value: &Option<Value>) -> i64 {
|
||||
match value {
|
||||
Some(Value::Integer(i)) => *i,
|
||||
_ => panic!("Type mismatch. Expected Integer but found {:?}", value),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl OptionType for bool {
|
||||
fn convert_default(value: Option<&Self>) -> Option<Value> {
|
||||
value.map(|b| Value::Boolean(*b))
|
||||
|
||||
fn get_value_type() -> ValueType {
|
||||
ValueType::Integer
|
||||
}
|
||||
}
|
||||
|
||||
impl OptionType for Option<String> {
|
||||
fn convert_default(_value: Option<&Self>) -> Option<Value> {
|
||||
None
|
||||
impl OptionType for config_type::DefaultBoolean {
|
||||
type OutputValue = bool;
|
||||
type DefaultValue = bool;
|
||||
|
||||
fn convert_default(value: &bool) -> Option<Value> {
|
||||
Some(Value::Boolean(*value))
|
||||
}
|
||||
fn from_value(value: &Option<Value>) -> bool {
|
||||
match value {
|
||||
Some(Value::Boolean(b)) => *b,
|
||||
_ => panic!("Type mismatch. Expected Boolean but found {:?}", value),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_value_type() -> ValueType {
|
||||
ValueType::Boolean
|
||||
}
|
||||
}
|
||||
|
||||
impl OptionType for Option<&str> {
|
||||
fn convert_default(_value: Option<&Self>) -> Option<Value> {
|
||||
None
|
||||
impl OptionType for config_type::Flag {
|
||||
type OutputValue = bool;
|
||||
type DefaultValue = ();
|
||||
|
||||
fn convert_default(_value: &()) -> Option<Value> {
|
||||
Some(Value::Boolean(false))
|
||||
}
|
||||
|
||||
fn from_value(value: &Option<Value>) -> bool {
|
||||
match value {
|
||||
Some(Value::Boolean(b)) => *b,
|
||||
_ => panic!("Type mismatch. Expected Boolean but found {:?}", value),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_value_type() -> ValueType {
|
||||
ValueType::Flag
|
||||
}
|
||||
}
|
||||
impl OptionType for Option<i64> {
|
||||
fn convert_default(_value: Option<&Self>) -> Option<Value> {
|
||||
|
||||
impl OptionType for config_type::String {
|
||||
type OutputValue = Option<String>;
|
||||
type DefaultValue = ();
|
||||
|
||||
fn convert_default(_value: &()) -> Option<Value> {
|
||||
None
|
||||
}
|
||||
|
||||
fn from_value(value: &Option<Value>) -> Option<String> {
|
||||
match value {
|
||||
Some(Value::String(s)) => Some(s.to_string()),
|
||||
None => None,
|
||||
_ => panic!(
|
||||
"Type mismatch. Expected Option<string> but found {:?}",
|
||||
value
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_value_type() -> ValueType {
|
||||
ValueType::String
|
||||
}
|
||||
}
|
||||
impl OptionType for Option<bool> {
|
||||
fn convert_default(_value: Option<&Self>) -> Option<Value> {
|
||||
|
||||
impl OptionType for config_type::Integer {
|
||||
type OutputValue = Option<i64>;
|
||||
type DefaultValue = ();
|
||||
|
||||
fn convert_default(_value: &()) -> Option<Value> {
|
||||
None
|
||||
}
|
||||
|
||||
fn from_value(value: &Option<Value>) -> Self::OutputValue {
|
||||
match value {
|
||||
Some(Value::Integer(i)) => Some(*i),
|
||||
None => None,
|
||||
_ => panic!(
|
||||
"Type mismatch. Expected Option<Integer> but found {:?}",
|
||||
value
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_value_type() -> ValueType {
|
||||
ValueType::Integer
|
||||
}
|
||||
}
|
||||
impl OptionType for config_type::Boolean {
|
||||
type OutputValue = Option<bool>;
|
||||
type DefaultValue = ();
|
||||
|
||||
fn convert_default(_value: &()) -> Option<Value> {
|
||||
None
|
||||
}
|
||||
fn from_value(value: &Option<Value>) -> Self::OutputValue {
|
||||
match value {
|
||||
Some(Value::Boolean(b)) => Some(*b),
|
||||
None => None,
|
||||
_ => panic!(
|
||||
"Type mismatch. Expected Option<Boolean> but found {:?}",
|
||||
value
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_value_type() -> ValueType {
|
||||
ValueType::Boolean
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
|
@ -70,6 +201,19 @@ pub enum Value {
|
|||
Boolean(bool),
|
||||
}
|
||||
|
||||
impl Serialize for Value {
|
||||
fn serialize<S>(&self, serializer: S) -> std::prelude::v1::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match self {
|
||||
Value::String(s) => serializer.serialize_str(s),
|
||||
Value::Integer(i) => serializer.serialize_i64(*i),
|
||||
Value::Boolean(b) => serializer.serialize_bool(*b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
/// Returns true if the `Value` is a String. Returns false otherwise.
|
||||
///
|
||||
|
@ -127,25 +271,27 @@ impl Value {
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ConfigOption<'a, V: OptionType> {
|
||||
name: &'a str,
|
||||
default: Option<V>,
|
||||
value_type: ValueType,
|
||||
description: &'a str,
|
||||
/// The name of the `ConfigOption`.
|
||||
pub name: &'a str,
|
||||
/// The default value of the `ConfigOption`
|
||||
pub default: V::DefaultValue,
|
||||
pub description: &'a str,
|
||||
pub deprecated: bool,
|
||||
}
|
||||
|
||||
impl<V: OptionType> ConfigOption<'_, V> {
|
||||
pub fn build(&self) -> UntypedConfigOption {
|
||||
UntypedConfigOption {
|
||||
name: self.name.to_string(),
|
||||
value_type: self.value_type.clone(),
|
||||
default: OptionType::convert_default(self.default.as_ref()),
|
||||
value: None,
|
||||
value_type: V::get_value_type(),
|
||||
default: <V as OptionType>::convert_default(&self.default),
|
||||
description: self.description.to_string(),
|
||||
deprecated: self.deprecated,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigOption<'_, &'static str> {
|
||||
impl DefaultStringConfigOption<'_> {
|
||||
pub const fn new_str_with_default(
|
||||
name: &'static str,
|
||||
default: &'static str,
|
||||
|
@ -153,25 +299,25 @@ impl ConfigOption<'_, &'static str> {
|
|||
) -> Self {
|
||||
Self {
|
||||
name: name,
|
||||
default: Some(default),
|
||||
value_type: ValueType::String,
|
||||
default: default,
|
||||
description: description,
|
||||
deprecated: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigOption<'_, Option<&str>> {
|
||||
impl StringConfigOption<'_> {
|
||||
pub const fn new_str_no_default(name: &'static str, description: &'static str) -> Self {
|
||||
Self {
|
||||
name,
|
||||
default: None,
|
||||
value_type: ValueType::String,
|
||||
description,
|
||||
default: (),
|
||||
description : description,
|
||||
deprecated: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigOption<'_, i64> {
|
||||
impl DefaultIntegerConfigOption<'_> {
|
||||
pub const fn new_i64_with_default(
|
||||
name: &'static str,
|
||||
default: i64,
|
||||
|
@ -179,36 +325,36 @@ impl ConfigOption<'_, i64> {
|
|||
) -> Self {
|
||||
Self {
|
||||
name: name,
|
||||
default: Some(default),
|
||||
value_type: ValueType::Integer,
|
||||
default: default,
|
||||
description: description,
|
||||
deprecated: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigOption<'_, Option<i64>> {
|
||||
impl IntegerConfigOption<'_> {
|
||||
pub const fn new_i64_no_default(name: &'static str, description: &'static str) -> Self {
|
||||
Self {
|
||||
name: name,
|
||||
default: None,
|
||||
value_type: ValueType::Integer,
|
||||
default: (),
|
||||
description: description,
|
||||
deprecated: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigOption<'_, Option<bool>> {
|
||||
impl BooleanConfigOption<'_> {
|
||||
pub const fn new_bool_no_default(name: &'static str, description: &'static str) -> Self {
|
||||
Self {
|
||||
name,
|
||||
description,
|
||||
default: None,
|
||||
value_type: ValueType::Boolean,
|
||||
default: (),
|
||||
deprecated: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigOption<'_, bool> {
|
||||
impl DefaultBooleanConfigOption<'_> {
|
||||
pub const fn new_bool_with_default(
|
||||
name: &'static str,
|
||||
default: bool,
|
||||
|
@ -217,29 +363,38 @@ impl ConfigOption<'_, bool> {
|
|||
Self {
|
||||
name,
|
||||
description,
|
||||
default: Some(default),
|
||||
value_type: ValueType::Boolean,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn new_flag(name: &'static str, description: &'static str) -> Self {
|
||||
Self {
|
||||
name,
|
||||
description,
|
||||
default: Some(false),
|
||||
value_type: ValueType::Flag,
|
||||
default: default,
|
||||
deprecated: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FlagConfigOption<'_> {
|
||||
pub const fn new_flag(name: &'static str, description: &'static str) -> Self {
|
||||
Self {
|
||||
name,
|
||||
description,
|
||||
default: (),
|
||||
deprecated: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_false(b: &bool) -> bool {
|
||||
*b == false
|
||||
}
|
||||
|
||||
/// An stringly typed option that is passed to
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct UntypedConfigOption {
|
||||
name: String,
|
||||
#[serde(rename = "type")]
|
||||
pub(crate) value_type: ValueType,
|
||||
pub(crate) value: Option<Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
default: Option<Value>,
|
||||
description: String,
|
||||
#[serde(skip_serializing_if = "is_false")]
|
||||
deprecated: bool,
|
||||
}
|
||||
|
||||
impl UntypedConfigOption {
|
||||
|
@ -251,38 +406,6 @@ impl UntypedConfigOption {
|
|||
}
|
||||
}
|
||||
|
||||
// When we serialize we don't add the value. This is because we only
|
||||
// ever serialize when we pass the option back to lightningd during
|
||||
// the getmanifest call.
|
||||
impl Serialize for UntypedConfigOption {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut s = serializer.serialize_struct("ConfigOption", 4)?;
|
||||
s.serialize_field("name", &self.name)?;
|
||||
match &self.default {
|
||||
Some(Value::String(ss)) => {
|
||||
s.serialize_field("default", ss)?;
|
||||
}
|
||||
Some(Value::Integer(i)) => {
|
||||
s.serialize_field("default", i)?;
|
||||
}
|
||||
Some(Value::Boolean(b)) => {
|
||||
match self.value_type {
|
||||
ValueType::Boolean => s.serialize_field("default", b)?,
|
||||
ValueType::Flag => {}
|
||||
_ => {} // This should never happen
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
s.serialize_field("type", &self.value_type)?;
|
||||
s.serialize_field("description", &self.description)?;
|
||||
s.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> ConfigOption<'_, V>
|
||||
where
|
||||
V: OptionType,
|
||||
|
@ -298,6 +421,7 @@ where
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
|
@ -335,7 +459,8 @@ mod test {
|
|||
json!({
|
||||
"name" : "name",
|
||||
"description": "description",
|
||||
"type" : "flag"
|
||||
"type" : "flag",
|
||||
"default" : false
|
||||
}),
|
||||
),
|
||||
];
|
||||
|
@ -348,20 +473,24 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn const_config_option() {
|
||||
const _: ConfigOption<bool> = ConfigOption::new_flag("flag-option", "A flag option");
|
||||
const _: ConfigOption<bool> =
|
||||
// The main goal of this test is to test compilation
|
||||
|
||||
// Initiate every type as a const
|
||||
const _: FlagConfigOption =
|
||||
ConfigOption::new_flag("flag-option", "A flag option");
|
||||
const _: DefaultBooleanConfigOption =
|
||||
ConfigOption::new_bool_with_default("bool-option", false, "A boolean option");
|
||||
const _: ConfigOption<Option<bool>> =
|
||||
const _: BooleanConfigOption =
|
||||
ConfigOption::new_bool_no_default("bool-option", "A boolean option");
|
||||
|
||||
const _: ConfigOption<Option<i64>> =
|
||||
const _: IntegerConfigOption =
|
||||
ConfigOption::new_i64_no_default("integer-option", "A flag option");
|
||||
const _: ConfigOption<i64> =
|
||||
const _: DefaultIntegerConfigOption =
|
||||
ConfigOption::new_i64_with_default("integer-option", 12, "A flag option");
|
||||
|
||||
const _: ConfigOption<Option<&str>> =
|
||||
const _: StringConfigOption =
|
||||
ConfigOption::new_str_no_default("integer-option", "A flag option");
|
||||
const _: ConfigOption<&str> =
|
||||
const _: DefaultStringConfigOption =
|
||||
ConfigOption::new_str_with_default("integer-option", "erik", "A flag option");
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue