diff --git a/plugins/examples/cln-plugin-startup.rs b/plugins/examples/cln-plugin-startup.rs index 48144ceee..c876ba2e4 100644 --- a/plugins/examples/cln-plugin-startup.rs +++ b/plugins/examples/cln-plugin-startup.rs @@ -12,15 +12,15 @@ async fn main() -> Result<(), anyhow::Error> { let state = (); if let Some(plugin) = Builder::new(tokio::io::stdin(), tokio::io::stdout()) - .option( - options::ConfigOption::new_i64_with_default( - "test-option", - 42, - "a test-option with default 42", - ) - .build(), - ) - .option(options::ConfigOption::new_opt_i64("opt-option", "An optional option").build()) + .option(options::ConfigOption::new_i64_with_default( + "test-option", + 42, + "a test-option with default 42", + )) + .option(options::ConfigOption::new_i64_no_default( + "opt-option", + "An optional option", + )) .rpcmethod("testmethod", "This is a test", testmethod) .rpcmethod( "testoptions", diff --git a/plugins/grpc-plugin/src/main.rs b/plugins/grpc-plugin/src/main.rs index 6d16dea92..3f6de54db 100644 --- a/plugins/grpc-plugin/src/main.rs +++ b/plugins/grpc-plugin/src/main.rs @@ -25,7 +25,7 @@ async fn main() -> Result<()> { "grpc-port", -1, "Which port should the grpc plugin listen for incoming connections?", - ).build()) + )) .configure() .await? { diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index c57860e72..ce2ed1e3c 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -130,8 +130,11 @@ where } } - pub fn option(mut self, opt: options::UntypedConfigOption) -> Builder { - self.options.insert(opt.name().to_string(), opt); + pub fn option( + mut self, + opt: options::ConfigOption, + ) -> Builder { + self.options.insert(opt.name().to_string(), opt.build()); self } diff --git a/plugins/src/options.rs b/plugins/src/options.rs index 35f22a71b..465aa18f6 100644 --- a/plugins/src/options.rs +++ b/plugins/src/options.rs @@ -3,14 +3,53 @@ use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; // Marker trait for possible values of options -pub trait OptionType {} +pub trait OptionType { + fn convert_default(value: Option<&Self>) -> Option; +} -impl OptionType for String {} -impl OptionType for i64 {} -impl OptionType for bool {} -impl OptionType for Option {} -impl OptionType for Option {} -impl OptionType for Option {} +impl OptionType for &str { + fn convert_default(value: Option<&Self>) -> Option { + value.map(|s| Value::String(s.to_string())) + } +} + +impl OptionType for String { + fn convert_default(value: Option<&Self>) -> Option { + value.map(|s| Value::String(s.clone())) + } +} +impl OptionType for i64 { + fn convert_default(value: Option<&Self>) -> Option { + value.map(|i| Value::Integer(*i)) + } +} +impl OptionType for bool { + fn convert_default(value: Option<&Self>) -> Option { + value.map(|b| Value::Boolean(*b)) + } +} + +impl OptionType for Option { + fn convert_default(_value: Option<&Self>) -> Option { + None + } +} + +impl OptionType for Option<&str> { + fn convert_default(_value: Option<&Self>) -> Option { + None + } +} +impl OptionType for Option { + fn convert_default(_value: Option<&Self>) -> Option { + None + } +} +impl OptionType for Option { + fn convert_default(_value: Option<&Self>) -> Option { + None + } +} #[derive(Clone, Debug, Serialize)] pub enum ValueType { @@ -87,118 +126,106 @@ impl Value { } #[derive(Clone, Debug)] -pub struct ConfigOption { - name: String, +pub struct ConfigOption<'a, V: OptionType> { + name: &'a str, default: Option, value_type: ValueType, - description: String, + description: &'a str, } -impl ConfigOption { +impl ConfigOption<'_, V> { pub fn build(&self) -> UntypedConfigOption { UntypedConfigOption { - name: self.name.clone(), + name: self.name.to_string(), value_type: self.value_type.clone(), - default: self.default.as_ref().map(|s| Value::String(s.clone())), - description: self.description.clone(), + default: OptionType::convert_default(self.default.as_ref()), value: None, + description: self.description.to_string(), } } +} - pub fn new_str_with_default, S2: AsRef, S3: AsRef>( - name: S1, - default: S2, - description: S3, +impl ConfigOption<'_, &'static str> { + pub const fn new_str_with_default( + name: &'static str, + default: &'static str, + description: &'static str, ) -> Self { Self { - name: name.as_ref().to_string(), - default: Some(default.as_ref().to_string()), + name: name, + default: Some(default), value_type: ValueType::String, - description: description.as_ref().to_string(), + description: description, } } } -impl ConfigOption { - pub fn build(&self) -> UntypedConfigOption { - UntypedConfigOption { - name: self.name.clone(), - value_type: self.value_type.clone(), - default: self.default.map(|i| Value::Integer(i)), - description: self.description.clone(), - value: None, +impl ConfigOption<'_, Option<&str>> { + pub const fn new_str_no_default(name: &'static str, description: &'static str) -> Self { + Self { + name, + default: None, + value_type: ValueType::String, + description, } } +} - pub fn new_i64_with_default, C: AsRef>( - name: A, +impl ConfigOption<'_, i64> { + pub const fn new_i64_with_default( + name: &'static str, default: i64, - description: C, + description: &'static str, ) -> Self { Self { - name: name.as_ref().to_string(), + name: name, default: Some(default), value_type: ValueType::Integer, - description: description.as_ref().to_string(), + description: description, } } } -impl ConfigOption> { - pub fn new_opt_i64, S2: AsRef>(name: S1, description: S2) -> Self { +impl ConfigOption<'_, Option> { + pub const fn new_i64_no_default(name: &'static str, description: &'static str) -> Self { Self { - name: name.as_ref().to_string(), + name: name, default: None, value_type: ValueType::Integer, - description: description.as_ref().to_string(), - } - } - - pub fn build(&self) -> UntypedConfigOption { - UntypedConfigOption { - name: self.name.clone(), - value_type: self.value_type.clone(), - default: None, - description: self.description.clone(), - value: None, + description: description, } } } -impl ConfigOption { - pub fn build(&self) -> UntypedConfigOption { - let default = match self.value_type { - ValueType::Flag => Some(Value::Boolean(false)), - ValueType::Boolean => self.default.map(|b| Value::Boolean(b)), - _ => panic!("Failed to build type"), - }; - - UntypedConfigOption { - name: self.name.clone(), - value_type: self.value_type.clone(), - default, - description: self.description.clone(), - value: None, +impl ConfigOption<'_, Option> { + pub const fn new_bool_no_default(name: &'static str, description: &'static str) -> Self { + Self { + name, + description, + default: None, + value_type: ValueType::Boolean, } } +} - pub fn new_bool_with_default, S2: AsRef>( - name: S1, +impl ConfigOption<'_, bool> { + pub const fn new_bool_with_default( + name: &'static str, default: bool, - description: S2, + description: &'static str, ) -> Self { Self { - name: name.as_ref().to_string(), - description: description.as_ref().to_string(), + name, + description, default: Some(default), value_type: ValueType::Boolean, } } - pub fn new_flag, S2: AsRef>(name: S1, description: S2) -> Self { + pub const fn new_flag(name: &'static str, description: &'static str) -> Self { Self { - name: name.as_ref().to_string(), - description: description.as_ref().to_string(), + name, + description, default: Some(false), value_type: ValueType::Flag, } @@ -256,7 +283,7 @@ impl Serialize for UntypedConfigOption { } } -impl ConfigOption +impl ConfigOption<'_, V> where V: OptionType, { @@ -319,10 +346,28 @@ mod test { } } + #[test] + fn const_config_option() { + const _: ConfigOption = ConfigOption::new_flag("flag-option", "A flag option"); + const _: ConfigOption = + ConfigOption::new_bool_with_default("bool-option", false, "A boolean option"); + const _: ConfigOption> = + ConfigOption::new_bool_no_default("bool-option", "A boolean option"); + + const _: ConfigOption> = + ConfigOption::new_i64_no_default("integer-option", "A flag option"); + const _: ConfigOption = + ConfigOption::new_i64_with_default("integer-option", 12, "A flag option"); + + const _: ConfigOption> = + ConfigOption::new_str_no_default("integer-option", "A flag option"); + const _: ConfigOption<&str> = + ConfigOption::new_str_with_default("integer-option", "erik", "A flag option"); + } + #[test] fn test_type_serialize() { assert_eq!(json!(ValueType::Integer), json!("int")); - assert_eq!(json!(ValueType::Flag), json!("flag")); } }