diff --git a/plugins/zenoh-plugin-rest/config_schema.json5 b/plugins/zenoh-plugin-rest/config_schema.json5 index 743fb0bdac..bd13828c10 100644 --- a/plugins/zenoh-plugin-rest/config_schema.json5 +++ b/plugins/zenoh-plugin-rest/config_schema.json5 @@ -13,10 +13,14 @@ ] }, "__path__": { + "default": null, "type": [ - "string", + "array", "null" - ] + ], + "items": { + "type": "string" + } }, "__required__": { "type": [ diff --git a/plugins/zenoh-plugin-rest/src/config.rs b/plugins/zenoh-plugin-rest/src/config.rs index e8badbc8c9..56b9960467 100644 --- a/plugins/zenoh-plugin-rest/src/config.rs +++ b/plugins/zenoh-plugin-rest/src/config.rs @@ -23,7 +23,8 @@ const DEFAULT_HTTP_INTERFACE: &str = "[::]"; pub struct Config { #[serde(deserialize_with = "deserialize_http_port")] pub http_port: String, - __path__: Option, + #[serde(default, deserialize_with = "deserialize_path")] + __path__: Option>, __required__: Option, __config__: Option, } @@ -76,3 +77,142 @@ impl<'de> Visitor<'de> for HttpPortVisitor { Ok(format!("{interface}:{port}")) } } + +fn deserialize_path<'de, D>(deserializer: D) -> Result>, D::Error> +where + D: Deserializer<'de>, +{ + deserializer.deserialize_option(OptPathVisitor) +} + +struct OptPathVisitor; + +impl<'de> serde::de::Visitor<'de> for OptPathVisitor { + type Value = Option>; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "none or a string or an array of strings") + } + + fn visit_none(self) -> Result + where + E: de::Error, + { + Ok(None) + } + + fn visit_some(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_any(PathVisitor).map(Some) + } +} + +struct PathVisitor; + +impl<'de> serde::de::Visitor<'de> for PathVisitor { + type Value = Vec; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "a string or an array of strings") + } + + fn visit_str(self, v: &str) -> Result + where + E: de::Error, + { + Ok(vec![v.into()]) + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: de::SeqAccess<'de>, + { + let mut v = seq.size_hint().map_or_else(Vec::new, Vec::with_capacity); + + while let Some(s) = seq.next_element()? { + v.push(s); + } + Ok(v) + } +} + +#[cfg(test)] +mod tests { + use super::{Config, DEFAULT_HTTP_INTERFACE}; + + #[test] + fn test_path_field() { + // See: https://github.com/eclipse-zenoh/zenoh-plugin-webserver/issues/19 + let config = + serde_json::from_str::(r#"{"__path__": "/example/path", "http_port": 8080}"#); + + assert!(config.is_ok()); + let Config { + http_port, + __required__, + __path__, + .. + } = config.unwrap(); + + assert_eq!(http_port, format!("{DEFAULT_HTTP_INTERFACE}:8080")); + assert_eq!(__path__, Some(vec![String::from("/example/path")])); + assert_eq!(__required__, None); + } + + #[test] + fn test_required_field() { + // See: https://github.com/eclipse-zenoh/zenoh-plugin-webserver/issues/19 + let config = serde_json::from_str::(r#"{"__required__": true, "http_port": 8080}"#); + assert!(config.is_ok()); + let Config { + http_port, + __required__, + __path__, + .. + } = config.unwrap(); + + assert_eq!(http_port, format!("{DEFAULT_HTTP_INTERFACE}:8080")); + assert_eq!(__path__, None); + assert_eq!(__required__, Some(true)); + } + + #[test] + fn test_path_field_and_required_field() { + // See: https://github.com/eclipse-zenoh/zenoh-plugin-webserver/issues/19 + let config = serde_json::from_str::( + r#"{"__path__": "/example/path", "__required__": true, "http_port": 8080}"#, + ); + + assert!(config.is_ok()); + let Config { + http_port, + __required__, + __path__, + .. + } = config.unwrap(); + + assert_eq!(http_port, format!("{DEFAULT_HTTP_INTERFACE}:8080")); + assert_eq!(__path__, Some(vec![String::from("/example/path")])); + assert_eq!(__required__, Some(true)); + } + + #[test] + fn test_no_path_field_and_no_required_field() { + // See: https://github.com/eclipse-zenoh/zenoh-plugin-webserver/issues/19 + let config = serde_json::from_str::(r#"{"http_port": 8080}"#); + + assert!(config.is_ok()); + let Config { + http_port, + __required__, + __path__, + .. + } = config.unwrap(); + + assert_eq!(http_port, format!("{DEFAULT_HTTP_INTERFACE}:8080")); + assert_eq!(__path__, None); + assert_eq!(__required__, None); + } +}