use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; #[derive(Clone, Debug)] pub enum Value { String(String), Integer(i64), Boolean(bool), OptString, OptInteger, OptBoolean, } impl Value { /// Returns true if the `Value` is a String. Returns false otherwise. /// /// For any Value on which `is_string` returns true, `as_str` is guaranteed /// to return the string slice. pub fn is_string(&self) -> bool { self.as_str().is_some() } /// If the `Value` is a String, returns the associated str. Returns None /// otherwise. pub fn as_str(&self) -> Option<&str> { match self { Value::String(s) => Some(s), _ => None, } } /// Returns true if the `Value` is an integer between `i64::MIN` and /// `i64::MAX`. /// /// For any Value on which `is_i64` returns true, `as_i64` is guaranteed to /// return the integer value. pub fn is_i64(&self) -> bool { self.as_i64().is_some() } /// If the `Value` is an integer, represent it as i64. Returns /// None otherwise. pub fn as_i64(&self) -> Option { match *self { Value::Integer(n) => Some(n), _ => None, } } /// Returns true if the `Value` is a Boolean. Returns false otherwise. /// /// For any Value on which `is_boolean` returns true, `as_bool` is /// guaranteed to return the boolean value. pub fn is_boolean(&self) -> bool { self.as_bool().is_some() } /// If the `Value` is a Boolean, returns the associated bool. Returns None /// otherwise. pub fn as_bool(&self) -> Option { match *self { Value::Boolean(b) => Some(b), _ => None, } } } /// An stringly typed option that is passed to #[derive(Clone, Debug)] pub struct ConfigOption { name: String, pub(crate) value: Option, default: Value, description: String, } impl ConfigOption { pub fn name(&self) -> &str { &self.name } pub fn default(&self) -> &Value { &self.default } } // 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 ConfigOption { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let mut s = serializer.serialize_struct("ConfigOption", 4)?; s.serialize_field("name", &self.name)?; match &self.default { Value::String(ss) => { s.serialize_field("type", "string")?; s.serialize_field("default", ss)?; } Value::Integer(i) => { 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)?; s.end() } } impl ConfigOption { pub fn new(name: &str, default: Value, description: &str) -> Self { Self { name: name.to_string(), default, description: description.to_string(), value: None, } } pub fn value(&self) -> Value { match &self.value { None => self.default.clone(), Some(v) => v.clone(), } } pub fn description(&self) -> String { self.description.clone() } } #[cfg(test)] mod test { use super::*; #[test] fn test_option_serialize() { let tests = vec![ ( ConfigOption::new("name", Value::String("default".to_string()), "description"), json!({ "name": "name", "description":"description", "default": "default", "type": "string", }), ), ( ConfigOption::new("name", Value::Integer(42), "description"), json!({ "name": "name", "description":"description", "default": 42, "type": "int", }), ), ( ConfigOption::new("name", Value::Boolean(true), "description"), json!({ "name": "name", "description":"description", "default": true, "type": "bool", }), ), ]; for (input, expected) in tests.iter() { let res = serde_json::to_value(input).unwrap(); assert_eq!(&res, expected); } } }