use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Protocol {
Http,
WebSocket,
Mqtt,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EndpointMeta {
pub name: String,
pub path: String,
pub protocol: Protocol,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub method: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub request_schema: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_schema: Option<serde_json::Value>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tags: Vec<String>,
}
pub struct EndpointMetaStatic {
pub name: &'static str,
pub path: &'static str,
pub protocol: Protocol,
pub description: Option<&'static str>,
pub method: Option<&'static str>,
pub tags: &'static [&'static str],
pub request_schema_fn: Option<fn() -> serde_json::Value>,
pub response_schema_fn: Option<fn() -> serde_json::Value>,
}
impl EndpointMetaStatic {
pub fn into_endpoint_meta(&self) -> EndpointMeta {
EndpointMeta {
name: self.name.to_owned(),
path: self.path.to_owned(),
protocol: self.protocol.clone(),
description: self.description.map(|s| s.to_owned()),
method: self.method.map(|s| s.to_owned()),
request_schema: self.request_schema_fn.map(|f| f()),
response_schema: self.response_schema_fn.map(|f| f()),
tags: self.tags.iter().map(|s| s.to_string()).collect(),
}
}
}
inventory::collect!(EndpointMetaStatic);
impl EndpointMeta {
pub fn new(name: impl Into<String>, path: impl Into<String>, protocol: Protocol) -> Self {
Self {
name: name.into(),
path: path.into(),
protocol,
description: None,
method: None,
request_schema: None,
response_schema: None,
tags: Vec::new(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
const EXPECTED_HTTP_JSON: &str = "\"Http\"";
const EXPECTED_WEBSOCKET_JSON: &str = "\"WebSocket\"";
const EXPECTED_MQTT_JSON: &str = "\"Mqtt\"";
const HEALTH_NAME: &str = "health";
const HEALTH_PATH: &str = "/health";
#[test]
fn protocol_http_serializes_to_http_string() {
let json = serde_json::to_string(&Protocol::Http)
.expect("serializing Protocol::Http should never fail");
assert_eq!(json, EXPECTED_HTTP_JSON);
}
#[test]
fn protocol_websocket_serializes_to_websocket_string() {
let json = serde_json::to_string(&Protocol::WebSocket)
.expect("serializing Protocol::WebSocket should never fail");
assert_eq!(json, EXPECTED_WEBSOCKET_JSON);
}
#[test]
fn protocol_mqtt_serializes_to_mqtt_string() {
let json = serde_json::to_string(&Protocol::Mqtt)
.expect("serializing Protocol::Mqtt should never fail");
assert_eq!(json, EXPECTED_MQTT_JSON);
}
#[test]
fn endpoint_meta_round_trips_through_serde_json() {
let original = EndpointMeta::new(HEALTH_NAME, HEALTH_PATH, Protocol::Http);
let serialized =
serde_json::to_string(&original).expect("serialization of EndpointMeta must succeed");
let deserialized: EndpointMeta = serde_json::from_str(&serialized)
.expect("deserialization of EndpointMeta must succeed");
assert_eq!(deserialized.name, HEALTH_NAME);
assert_eq!(deserialized.path, HEALTH_PATH);
assert_eq!(deserialized.protocol, Protocol::Http);
assert!(deserialized.description.is_none());
assert!(deserialized.method.is_none());
assert!(deserialized.request_schema.is_none());
assert!(deserialized.response_schema.is_none());
assert!(deserialized.tags.is_empty());
}
}