cln_plugin/
options.rs

1//! This module contains all logic related to `ConfigOption`'s that can be
2//! set in Core Lightning. The [Core Lightning documentation](https://docs.corelightning.org/reference/lightningd-config)
3//! describes how the user can specify configuration. This can be done using
4//! a command-line argument or by specifying the value in the `config`-file.
5//!
6//! ## A simple example
7//!
8//! A config option can either be specified using helper-methods or explicitly.
9//!
10//! ```no_run
11//! use anyhow::Result;
12//!
13//! use cln_plugin::ConfiguredPlugin;
14//! use cln_plugin::Builder;
15//! use cln_plugin::options::{StringConfigOption, DefaultStringConfigOption};
16//!
17//! const STRING_OPTION : StringConfigOption =
18//!     StringConfigOption::new_str_no_default(
19//!         "string-option",
20//!         "A config option of type string with no default"
21//! );
22//!
23//! const DEFAULT_STRING_OPTION : DefaultStringConfigOption =
24//!     DefaultStringConfigOption::new_str_with_default(
25//!         "string-option",
26//!         "bitcoin",
27//!         "A config option which uses 'bitcoin when as a default"
28//! );
29//!
30//! #[tokio::main]
31//! async fn main() -> Result<()>{
32//!     let configured_plugin = Builder::new(tokio::io::stdin(), tokio::io::stdout())
33//!         .option(STRING_OPTION)
34//!         .option(DEFAULT_STRING_OPTION)
35//!         .configure()
36//!         .await?;
37//!     
38//!     let configured_plugin :ConfiguredPlugin<(),_,_> = match configured_plugin {
39//!         Some(plugin) => plugin,
40//!         None => return Ok(())       // Core Lightning was started with --help
41//!     };
42//!
43//!     // Note the types here.
44//!     // In `string_option` the developer did not specify a default and `None`
45//!     // will be returned if the user doesn't specify a configuration.
46//!     //
47//!     // In `default_string_option` the developer set a default-value.
48//!     // If the user doesn't specify a configuration the `String` `"bitcoin"`
49//!     // will be returned.
50//!     let string_option : Option<String> = configured_plugin
51//!         .option(&STRING_OPTION)
52//!         .expect("Failed to configure option");
53//!     let default_string_option : String = configured_plugin
54//!         .option(&DEFAULT_STRING_OPTION)
55//!         .expect("Failed to configure option");
56//!
57//!     // You can start the plugin here
58//!     // ...
59//!
60//!     Ok(())
61//! }
62//!
63//! ```
64//!
65//! ## Explicit initialization
66//!
67//! A `ConfigOption` can be initialized explicitly or using one of the helper methods.
68//! The two code-samples below are equivalent. The explicit version is more verbose
69//! but allows specifying additional information.
70//!
71//! ```
72//! use cln_plugin::options::{StringConfigOption};
73//!
74//! const STRING_OPTION : StringConfigOption = StringConfigOption {
75//!     name : "string-option",
76//!     default : (), // We provide no default here
77//!     description : "A config option of type string that takes no default",
78//!     deprecated : false,     // Option is not deprecated
79//!     dynamic: false, //Option is not dynamic
80//!     multi: false, //Option must not be multi, use StringArray instead
81//! };
82//! ```
83//!
84//! ```
85//! use cln_plugin::options::{StringConfigOption};
86//! // This code is equivalent
87//! const STRING_OPTION_EQ : StringConfigOption = StringConfigOption::new_str_no_default(
88//!     "string-option-eq",
89//!     "A config option of type string that takes no default"
90//! );
91//! ```
92//!
93//! ## Required options
94//!
95//! In some cases you want to require the user to specify a value.
96//! This can be achieved using [`crate::ConfiguredPlugin::disable`].
97//!
98//! ```no_run
99//! use anyhow::Result;
100//!
101//! use cln_plugin::ConfiguredPlugin;
102//! use cln_plugin::Builder;
103//! use cln_plugin::options::{IntegerConfigOption};
104//!
105//! const WEBPORTAL_PORT : IntegerConfigOption = IntegerConfigOption::new_i64_no_default(
106//!     "webportal-port",
107//!     "The port on which the web-portal will be exposed"
108//! );
109//!
110//! #[tokio::main]
111//! async fn main() -> Result<()> {
112//!     let configured_plugin = Builder::new(tokio::io::stdin(), tokio::io::stdout())
113//!         .option(WEBPORTAL_PORT)
114//!         .configure()
115//!         .await?;
116//!
117//!     let configured_plugin :ConfiguredPlugin<(),_,_> = match configured_plugin {
118//!         Some(plugin) => plugin,
119//!         None => return Ok(())       // Core Lightning was started with --help
120//!     };
121//!
122//!     let webportal_port : i64 = match(configured_plugin.option(&WEBPORTAL_PORT)?) {
123//!         Some(port) => port,
124//!         None => {
125//!             return configured_plugin.disable("No value specified for webportal-port").await
126//!         }
127//!     };
128//!
129//!     // Start the plugin here
130//!     //..
131//!
132//!     Ok(())
133//! }
134//! ```
135use serde::ser::{SerializeSeq, Serializer};
136use serde::Serialize;
137
138pub mod config_type {
139    #[derive(Clone, Debug)]
140    pub struct Integer;
141    #[derive(Clone, Debug)]
142    pub struct DefaultInteger;
143    #[derive(Clone, Debug)]
144    pub struct IntegerArray;
145    #[derive(Clone, Debug)]
146    pub struct DefaultIntegerArray;
147    #[derive(Clone, Debug)]
148    pub struct String;
149    #[derive(Clone, Debug)]
150    pub struct DefaultString;
151    #[derive(Clone, Debug)]
152    pub struct StringArray;
153    #[derive(Clone, Debug)]
154    pub struct DefaultStringArray;
155    #[derive(Clone, Debug)]
156    pub struct Boolean;
157    #[derive(Clone, Debug)]
158    pub struct DefaultBoolean;
159    #[derive(Clone, Debug)]
160    pub struct Flag;
161}
162
163/// Config values are represented as an i64. No default is used
164pub type IntegerConfigOption<'a> = ConfigOption<'a, config_type::Integer>;
165// Config values are represented as a Vec<i64>. No default is used.
166pub type IntegerArrayConfigOption<'a> = ConfigOption<'a, config_type::IntegerArray>;
167/// Config values are represented as a String. No default is used.
168pub type StringConfigOption<'a> = ConfigOption<'a, config_type::String>;
169// Config values are represented as a Vec<String>. No default is used.
170pub type StringArrayConfigOption<'a> = ConfigOption<'a, config_type::StringArray>;
171/// Config values are represented as a boolean. No default is used.
172pub type BooleanConfigOption<'a> = ConfigOption<'a, config_type::Boolean>;
173/// Config values are repsentedas an i64. A default is used
174pub type DefaultIntegerConfigOption<'a> = ConfigOption<'a, config_type::DefaultInteger>;
175// Config values are represented as a Vec<i64>. A default is used
176pub type DefaultIntegerArrayConfigOption<'a> = ConfigOption<'a, config_type::DefaultIntegerArray>;
177/// Config values are repsentedas an String. A default is used
178pub type DefaultStringConfigOption<'a> = ConfigOption<'a, config_type::DefaultString>;
179// Config values are represented as a Vec<String>. A default is used
180pub type DefaultStringArrayConfigOption<'a> = ConfigOption<'a, config_type::DefaultStringArray>;
181/// Config values are repsentedas an bool. A default is used
182pub type DefaultBooleanConfigOption<'a> = ConfigOption<'a, config_type::DefaultBoolean>;
183/// Config value is represented as a flag
184pub type FlagConfigOption<'a> = ConfigOption<'a, config_type::Flag>;
185
186pub trait OptionType<'a> {
187    type OutputValue;
188    type DefaultValue;
189
190    fn convert_default(value: &Self::DefaultValue) -> Option<Value>;
191
192    fn from_value(value: &Option<Value>) -> Self::OutputValue;
193
194    fn get_value_type() -> ValueType;
195}
196
197impl<'a> OptionType<'a> for config_type::DefaultString {
198    type OutputValue = String;
199    type DefaultValue = &'a str;
200
201    fn convert_default(value: &Self::DefaultValue) -> Option<Value> {
202        Some(Value::String(value.to_string()))
203    }
204
205    fn from_value(value: &Option<Value>) -> Self::OutputValue {
206        match value {
207            Some(Value::String(s)) => s.to_string(),
208            _ => panic!("Type mismatch. Expected string but found {:?}", value),
209        }
210    }
211
212    fn get_value_type() -> ValueType {
213        ValueType::String
214    }
215}
216
217impl<'a> OptionType<'a> for config_type::DefaultStringArray {
218    type OutputValue = Vec<String>;
219    type DefaultValue = &'a str;
220
221    fn convert_default(value: &Self::DefaultValue) -> Option<Value> {
222        Some(Value::String(value.to_string()))
223    }
224
225    fn from_value(value: &Option<Value>) -> Self::OutputValue {
226        match value {
227            Some(Value::StringArray(s)) => s.clone(),
228            _ => panic!("Type mismatch. Expected string-array but found {:?}", value),
229        }
230    }
231
232    fn get_value_type() -> ValueType {
233        ValueType::String
234    }
235}
236
237impl<'a> OptionType<'a> for config_type::DefaultInteger {
238    type OutputValue = i64;
239    type DefaultValue = i64;
240
241    fn convert_default(value: &Self::DefaultValue) -> Option<Value> {
242        Some(Value::Integer(*value))
243    }
244
245    fn from_value(value: &Option<Value>) -> Self::OutputValue {
246        match value {
247            Some(Value::Integer(i)) => *i,
248            _ => panic!("Type mismatch. Expected Integer but found {:?}", value),
249        }
250    }
251
252    fn get_value_type() -> ValueType {
253        ValueType::Integer
254    }
255}
256
257impl<'a> OptionType<'a> for config_type::DefaultIntegerArray {
258    type OutputValue = Vec<i64>;
259    type DefaultValue = i64;
260
261    fn convert_default(value: &Self::DefaultValue) -> Option<Value> {
262        Some(Value::Integer(*value))
263    }
264
265    fn from_value(value: &Option<Value>) -> Self::OutputValue {
266        match value {
267            Some(Value::IntegerArray(i)) => i.clone(),
268            _ => panic!(
269                "Type mismatch. Expected Integer-array but found {:?}",
270                value
271            ),
272        }
273    }
274
275    fn get_value_type() -> ValueType {
276        ValueType::Integer
277    }
278}
279
280impl<'a> OptionType<'a> for config_type::DefaultBoolean {
281    type OutputValue = bool;
282    type DefaultValue = bool;
283
284    fn convert_default(value: &bool) -> Option<Value> {
285        Some(Value::Boolean(*value))
286    }
287    fn from_value(value: &Option<Value>) -> Self::OutputValue {
288        match value {
289            Some(Value::Boolean(b)) => *b,
290            _ => panic!("Type mismatch. Expected Boolean but found {:?}", value),
291        }
292    }
293
294    fn get_value_type() -> ValueType {
295        ValueType::Boolean
296    }
297}
298
299impl<'a> OptionType<'a> for config_type::Flag {
300    type OutputValue = bool;
301    type DefaultValue = ();
302
303    fn convert_default(_value: &()) -> Option<Value> {
304        Some(Value::Boolean(false))
305    }
306
307    fn from_value(value: &Option<Value>) -> Self::OutputValue {
308        match value {
309            Some(Value::Boolean(b)) => *b,
310            _ => panic!("Type mismatch. Expected Boolean but found {:?}", value),
311        }
312    }
313
314    fn get_value_type() -> ValueType {
315        ValueType::Flag
316    }
317}
318
319impl<'a> OptionType<'a> for config_type::String {
320    type OutputValue = Option<String>;
321    type DefaultValue = ();
322
323    fn convert_default(_value: &()) -> Option<Value> {
324        None
325    }
326
327    fn from_value(value: &Option<Value>) -> Self::OutputValue {
328        match value {
329            Some(Value::String(s)) => Some(s.to_string()),
330            None => None,
331            _ => panic!(
332                "Type mismatch. Expected Option<string> but found {:?}",
333                value
334            ),
335        }
336    }
337
338    fn get_value_type() -> ValueType {
339        ValueType::String
340    }
341}
342
343impl<'a> OptionType<'a> for config_type::StringArray {
344    type OutputValue = Option<Vec<String>>;
345    type DefaultValue = ();
346
347    fn convert_default(_value: &()) -> Option<Value> {
348        None
349    }
350
351    fn from_value(value: &Option<Value>) -> Self::OutputValue {
352        match value {
353            Some(Value::StringArray(s)) => Some(s.clone()),
354            None => None,
355            _ => panic!(
356                "Type mismatch. Expected Option<Vec<String>> but found {:?}",
357                value
358            ),
359        }
360    }
361
362    fn get_value_type() -> ValueType {
363        ValueType::String
364    }
365}
366
367impl<'a> OptionType<'a> for config_type::Integer {
368    type OutputValue = Option<i64>;
369    type DefaultValue = ();
370
371    fn convert_default(_value: &()) -> Option<Value> {
372        None
373    }
374
375    fn from_value(value: &Option<Value>) -> Self::OutputValue {
376        match value {
377            Some(Value::Integer(i)) => Some(*i),
378            None => None,
379            _ => panic!(
380                "Type mismatch. Expected Option<Integer> but found {:?}",
381                value
382            ),
383        }
384    }
385
386    fn get_value_type() -> ValueType {
387        ValueType::Integer
388    }
389}
390
391impl<'a> OptionType<'a> for config_type::IntegerArray {
392    type OutputValue = Option<Vec<i64>>;
393    type DefaultValue = ();
394
395    fn convert_default(_value: &()) -> Option<Value> {
396        None
397    }
398
399    fn from_value(value: &Option<Value>) -> Self::OutputValue {
400        match value {
401            Some(Value::IntegerArray(i)) => Some(i.clone()),
402            None => None,
403            _ => panic!(
404                "Type mismatch. Expected Option<Vec<Integer>> but found {:?}",
405                value
406            ),
407        }
408    }
409
410    fn get_value_type() -> ValueType {
411        ValueType::Integer
412    }
413}
414
415impl<'a> OptionType<'a> for config_type::Boolean {
416    type OutputValue = Option<bool>;
417    type DefaultValue = ();
418
419    fn convert_default(_value: &()) -> Option<Value> {
420        None
421    }
422    fn from_value(value: &Option<Value>) -> Self::OutputValue {
423        match value {
424            Some(Value::Boolean(b)) => Some(*b),
425            None => None,
426            _ => panic!(
427                "Type mismatch. Expected Option<Boolean> but found {:?}",
428                value
429            ),
430        }
431    }
432
433    fn get_value_type() -> ValueType {
434        ValueType::Boolean
435    }
436}
437
438#[derive(Clone, Debug, Serialize)]
439pub enum ValueType {
440    #[serde(rename = "string")]
441    String,
442    #[serde(rename = "int")]
443    Integer,
444    #[serde(rename = "bool")]
445    Boolean,
446    #[serde(rename = "flag")]
447    Flag,
448}
449
450#[derive(Clone, Debug)]
451pub enum Value {
452    String(String),
453    Integer(i64),
454    Boolean(bool),
455    StringArray(Vec<String>),
456    IntegerArray(Vec<i64>),
457}
458
459impl Serialize for Value {
460    fn serialize<S>(&self, serializer: S) -> std::prelude::v1::Result<S::Ok, S::Error>
461    where
462        S: Serializer,
463    {
464        match self {
465            Value::String(s) => serializer.serialize_str(s),
466            Value::Integer(i) => serializer.serialize_i64(*i),
467            Value::Boolean(b) => serializer.serialize_bool(*b),
468            Value::StringArray(sa) => {
469                let mut seq = serializer.serialize_seq(Some(sa.len()))?;
470                for element in sa {
471                    seq.serialize_element(element)?;
472                }
473                seq.end()
474            }
475            Value::IntegerArray(sa) => {
476                let mut seq = serializer.serialize_seq(Some(sa.len()))?;
477                for element in sa {
478                    seq.serialize_element(element)?;
479                }
480                seq.end()
481            }
482        }
483    }
484}
485
486impl Value {
487    /// Returns true if the `Value` is a String. Returns false otherwise.
488    ///
489    /// For any Value on which `is_string` returns true, `as_str` is guaranteed
490    /// to return the string slice.
491    pub fn is_string(&self) -> bool {
492        self.as_str().is_some()
493    }
494
495    /// If the `Value` is a String, returns the associated str. Returns None
496    /// otherwise.
497    pub fn as_str(&self) -> Option<&str> {
498        match self {
499            Value::String(s) => Some(&s),
500            Value::Integer(_) => None,
501            Value::Boolean(_) => None,
502            Value::StringArray(_) => None,
503            Value::IntegerArray(_) => None,
504        }
505    }
506
507    /// Returns true if the `Value` is an integer between `i64::MIN` and
508    /// `i64::MAX`.
509    ///
510    /// For any Value on which `is_i64` returns true, `as_i64` is guaranteed to
511    /// return the integer value.
512    pub fn is_i64(&self) -> bool {
513        self.as_i64().is_some()
514    }
515
516    /// If the `Value` is an integer, represent it as i64. Returns
517    /// None otherwise.
518    pub fn as_i64(&self) -> Option<i64> {
519        match *self {
520            Value::Integer(n) => Some(n),
521            _ => None,
522        }
523    }
524
525    /// Returns true if the `Value` is a Boolean. Returns false otherwise.
526    ///
527    /// For any Value on which `is_boolean` returns true, `as_bool` is
528    /// guaranteed to return the boolean value.
529    pub fn is_boolean(&self) -> bool {
530        self.as_bool().is_some()
531    }
532
533    /// If the `Value` is a Boolean, returns the associated bool. Returns None
534    /// otherwise.
535    pub fn as_bool(&self) -> Option<bool> {
536        match *self {
537            Value::Boolean(b) => Some(b),
538            _ => None,
539        }
540    }
541
542    /// Returns true if the `Value` is a Vec<String>. Returns false otherwise.
543    ///
544    /// For any Value on which `is_str_arr` returns true, `as_str_arr` is
545    /// guaranteed to return the Vec<String> value.
546    pub fn is_str_arr(&self) -> bool {
547        self.as_str_arr().is_some()
548    }
549
550    /// If the `Value` is a Vec<String>, returns the associated Vec<String>.
551    /// Returns None otherwise.
552    pub fn as_str_arr(&self) -> Option<&Vec<String>> {
553        match self {
554            Value::StringArray(sa) => Some(sa),
555            _ => None,
556        }
557    }
558
559    /// Returns true if the `Value` is a Vec<i64>. Returns false otherwise.
560    ///
561    /// For any Value on which `is_i64_arr` returns true, `as_i64_arr` is
562    /// guaranteed to return the Vec<i64> value.
563    pub fn is_i64_arr(&self) -> bool {
564        self.as_i64_arr().is_some()
565    }
566
567    /// If the `Value` is a Vec<i64>, returns the associated Vec<i64>.
568    /// Returns None otherwise.
569    pub fn as_i64_arr(&self) -> Option<&Vec<i64>> {
570        match self {
571            Value::IntegerArray(sa) => Some(sa),
572            _ => None,
573        }
574    }
575}
576
577#[derive(Clone, Debug)]
578pub struct ConfigOption<'a, V: OptionType<'a>> {
579    /// The name of the `ConfigOption`.
580    pub name: &'a str,
581    /// The default value of the `ConfigOption`
582    pub default: V::DefaultValue,
583    pub description: &'a str,
584    pub deprecated: bool,
585    pub dynamic: bool,
586    pub multi: bool,
587}
588
589impl<'a, V: OptionType<'a>> ConfigOption<'a, V> {
590    pub fn build(&self) -> UntypedConfigOption {
591        UntypedConfigOption {
592            name: self.name.to_string(),
593            value_type: V::get_value_type(),
594            default: <V as OptionType>::convert_default(&self.default),
595            description: self.description.to_string(),
596            deprecated: self.deprecated,
597            dynamic: self.dynamic,
598            multi: self.multi,
599        }
600    }
601}
602
603impl<'a> DefaultStringConfigOption<'a> {
604    pub const fn new_str_with_default(
605        name: &'a str,
606        default: &'a str,
607        description: &'a str,
608    ) -> Self {
609        Self {
610            name: name,
611            default: default,
612            description: description,
613            deprecated: false,
614            dynamic: false,
615            multi: false,
616        }
617    }
618    pub fn dynamic(mut self) -> Self {
619        self.dynamic = true;
620        self
621    }
622}
623
624impl<'a> StringConfigOption<'a> {
625    pub const fn new_str_no_default(name: &'a str, description: &'a str) -> Self {
626        Self {
627            name,
628            default: (),
629            description: description,
630            deprecated: false,
631            dynamic: false,
632            multi: false,
633        }
634    }
635    pub fn dynamic(mut self) -> Self {
636        self.dynamic = true;
637        self
638    }
639}
640
641impl<'a> DefaultStringArrayConfigOption<'a> {
642    pub const fn new_str_arr_with_default(
643        name: &'a str,
644        default: &'a str,
645        description: &'a str,
646    ) -> Self {
647        Self {
648            name,
649            default,
650            description,
651            deprecated: false,
652            dynamic: false,
653            multi: true,
654        }
655    }
656    pub fn dynamic(mut self) -> Self {
657        self.dynamic = true;
658        self
659    }
660}
661
662impl<'a> StringArrayConfigOption<'a> {
663    pub const fn new_str_arr_no_default(name: &'a str, description: &'a str) -> Self {
664        Self {
665            name,
666            default: (),
667            description,
668            deprecated: false,
669            dynamic: false,
670            multi: true,
671        }
672    }
673    pub fn dynamic(mut self) -> Self {
674        self.dynamic = true;
675        self
676    }
677}
678
679impl<'a> DefaultIntegerConfigOption<'a> {
680    pub const fn new_i64_with_default(name: &'a str, default: i64, description: &'a str) -> Self {
681        Self {
682            name: name,
683            default: default,
684            description: description,
685            deprecated: false,
686            dynamic: false,
687            multi: false,
688        }
689    }
690    pub fn dynamic(mut self) -> Self {
691        self.dynamic = true;
692        self
693    }
694}
695
696impl<'a> IntegerConfigOption<'a> {
697    pub const fn new_i64_no_default(name: &'a str, description: &'a str) -> Self {
698        Self {
699            name: name,
700            default: (),
701            description: description,
702            deprecated: false,
703            dynamic: false,
704            multi: false,
705        }
706    }
707    pub fn dynamic(mut self) -> Self {
708        self.dynamic = true;
709        self
710    }
711}
712
713impl<'a> DefaultIntegerArrayConfigOption<'a> {
714    pub const fn new_i64_arr_with_default(
715        name: &'a str,
716        default: i64,
717        description: &'a str,
718    ) -> Self {
719        Self {
720            name,
721            default,
722            description,
723            deprecated: false,
724            dynamic: false,
725            multi: true,
726        }
727    }
728    pub fn dynamic(mut self) -> Self {
729        self.dynamic = true;
730        self
731    }
732}
733
734impl<'a> IntegerArrayConfigOption<'a> {
735    pub const fn new_i64_arr_no_default(name: &'a str, description: &'a str) -> Self {
736        Self {
737            name,
738            default: (),
739            description,
740            deprecated: false,
741            dynamic: false,
742            multi: true,
743        }
744    }
745    pub fn dynamic(mut self) -> Self {
746        self.dynamic = true;
747        self
748    }
749}
750
751impl<'a> BooleanConfigOption<'a> {
752    pub const fn new_bool_no_default(name: &'a str, description: &'a str) -> Self {
753        Self {
754            name,
755            description,
756            default: (),
757            deprecated: false,
758            dynamic: false,
759            multi: false,
760        }
761    }
762    pub fn dynamic(mut self) -> Self {
763        self.dynamic = true;
764        self
765    }
766}
767
768impl<'a> DefaultBooleanConfigOption<'a> {
769    pub const fn new_bool_with_default(name: &'a str, default: bool, description: &'a str) -> Self {
770        Self {
771            name,
772            description,
773            default: default,
774            deprecated: false,
775            dynamic: false,
776            multi: false,
777        }
778    }
779    pub fn dynamic(mut self) -> Self {
780        self.dynamic = true;
781        self
782    }
783}
784
785impl<'a> FlagConfigOption<'a> {
786    pub const fn new_flag(name: &'a str, description: &'a str) -> Self {
787        Self {
788            name,
789            description,
790            default: (),
791            deprecated: false,
792            dynamic: false,
793            multi: false,
794        }
795    }
796    pub fn dynamic(mut self) -> Self {
797        self.dynamic = true;
798        self
799    }
800}
801
802fn is_false(b: &bool) -> bool {
803    *b == false
804}
805
806/// An stringly typed option that is passed to
807#[derive(Clone, Debug, Serialize)]
808pub struct UntypedConfigOption {
809    name: String,
810    #[serde(rename = "type")]
811    pub(crate) value_type: ValueType,
812    #[serde(skip_serializing_if = "Option::is_none")]
813    default: Option<Value>,
814    description: String,
815    #[serde(skip_serializing_if = "is_false")]
816    deprecated: bool,
817    dynamic: bool,
818    multi: bool,
819}
820
821impl UntypedConfigOption {
822    pub fn name(&self) -> &str {
823        &self.name
824    }
825    pub fn default(&self) -> &Option<Value> {
826        &self.default
827    }
828    pub fn dynamic(mut self) -> Self {
829        self.dynamic = true;
830        self
831    }
832}
833
834impl<'a, V> ConfigOption<'a, V>
835where
836    V: OptionType<'a>,
837{
838    pub fn name(&self) -> &str {
839        &self.name
840    }
841
842    pub fn description(&self) -> &str {
843        &self.description
844    }
845}
846
847#[cfg(test)]
848mod test {
849
850    use super::*;
851
852    #[test]
853    fn test_option_serialize() {
854        let tests = vec![
855            (
856                ConfigOption::new_str_with_default("name", "default", "description").build(),
857                json!({
858                "name": "name",
859                        "description":"description",
860                        "default": "default",
861                        "type": "string",
862                        "dynamic": false,
863                        "multi": false,
864                    }),
865            ),
866            (
867                ConfigOption::new_i64_with_default("name", 42, "description").build(),
868                json!({
869                "name": "name",
870                        "description":"description",
871                        "default": 42,
872                        "type": "int",
873                        "dynamic": false,
874                        "multi": false,
875                    }),
876            ),
877            (
878                {
879                    ConfigOption::new_bool_with_default("name", true, "description")
880                        .build()
881                        .dynamic()
882                },
883                json!({
884                "name": "name",
885                        "description":"description",
886                        "default": true,
887                        "type": "bool",
888                        "dynamic": true,
889                        "multi": false,
890                    }),
891            ),
892            (
893                ConfigOption::new_flag("name", "description").build(),
894                json!({
895                    "name" : "name",
896                    "description": "description",
897                    "type" : "flag",
898                    "default" : false,
899                    "dynamic": false,
900                    "multi": false,
901                }),
902            ),
903            (
904                ConfigOption::new_str_arr_with_default("name", "Default1", "description").build(),
905                json!({
906                    "name" : "name",
907                    "description": "description",
908                    "type" : "string",
909                    "default" : "Default1",
910                    "dynamic": false,
911                    "multi": true,
912                }),
913            ),
914            (
915                ConfigOption::new_i64_arr_with_default("name", -46, "description").build(),
916                json!({
917                    "name" : "name",
918                    "description": "description",
919                    "type" : "int",
920                    "default" : -46,
921                    "dynamic": false,
922                    "multi": true,
923                }),
924            ),
925        ];
926
927        for (input, expected) in tests.iter() {
928            let res = serde_json::to_value(input).unwrap();
929            assert_eq!(&res, expected);
930        }
931    }
932
933    #[test]
934    fn const_config_option() {
935        // The main goal of this test is to test compilation
936
937        // Initiate every type as a const
938        const _: FlagConfigOption = ConfigOption::new_flag("flag-option", "A flag option");
939        const _: DefaultBooleanConfigOption =
940            ConfigOption::new_bool_with_default("bool-option", false, "A boolean option");
941        const _: BooleanConfigOption =
942            ConfigOption::new_bool_no_default("bool-option", "A boolean option");
943
944        const _: IntegerConfigOption =
945            ConfigOption::new_i64_no_default("integer-option", "A flag option");
946        const _: DefaultIntegerConfigOption =
947            ConfigOption::new_i64_with_default("integer-option", 12, "A flag option");
948
949        const _: StringConfigOption =
950            ConfigOption::new_str_no_default("integer-option", "A flag option");
951        const _: DefaultStringConfigOption =
952            ConfigOption::new_str_with_default("integer-option", "erik", "A flag option");
953    }
954
955    #[test]
956    fn test_type_serialize() {
957        assert_eq!(json!(ValueType::Integer), json!("int"));
958        assert_eq!(json!(ValueType::Flag), json!("flag"));
959    }
960}