1use std::collections::BTreeMap;
2use thiserror::Error;
3use uuid::Uuid;
4
5#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
7pub struct TemplateParameterSpec {
8 pub name: String,
10 #[serde(skip_serializing_if = "Option::is_none")]
12 pub default_value: Option<String>,
13 #[serde(skip_serializing_if = "Option::is_none")]
15 pub description: Option<String>,
16}
17
18#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
20pub struct RouteTemplateSpec {
21 pub id: String,
23 pub parameters: Vec<TemplateParameterSpec>,
25 pub route: serde_json::Value,
27}
28
29#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
31pub struct TemplatedRouteSpec {
32 pub route_template_ref: String,
34 #[serde(skip_serializing_if = "Option::is_none")]
36 pub route_id: Option<String>,
37 pub parameters: BTreeMap<String, String>,
39}
40
41#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
43pub struct TemplateInstanceRecord {
44 pub template_id: String,
46 pub instance_id: Uuid,
48 pub route_id: String,
50 pub parameters: BTreeMap<String, String>,
52}
53
54#[derive(Debug, Clone, Error)]
56#[non_exhaustive]
57pub enum TemplateError {
58 #[error("missing required parameter: {0}")]
59 MissingParameter(String),
60
61 #[error("unknown parameter: {0}")]
62 UnknownParameter(String),
63
64 #[error("template already registered: {0}")]
65 AlreadyRegistered(String),
66
67 #[error("invalid template body: {0}")]
68 InvalidBody(String),
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74
75 #[test]
76 fn template_error_converts_to_camel_error() {
77 use crate::error::CamelError;
78 let err = TemplateError::MissingParameter("host".into());
79 let camel: CamelError = err.into();
80 assert!(matches!(camel, CamelError::Config(_)));
81 }
82
83 #[test]
84 fn template_parameter_spec_serializes() {
85 let spec = TemplateParameterSpec {
86 name: "host".into(),
87 default_value: Some("localhost".into()),
88 description: Some("The target host".into()),
89 };
90 let json = serde_json::to_string(&spec).unwrap();
91 assert!(json.contains("host"));
92 assert!(json.contains("localhost"));
93 }
94
95 #[test]
96 fn template_parameter_spec_roundtrip() {
97 let spec = TemplateParameterSpec {
98 name: "port".into(),
99 default_value: None,
100 description: None,
101 };
102 let json = serde_json::to_string(&spec).unwrap();
103 let round: TemplateParameterSpec = serde_json::from_str(&json).unwrap();
104 assert_eq!(round.name, "port");
105 assert!(round.default_value.is_none());
106 assert!(round.description.is_none());
107 }
108
109 #[test]
110 fn route_template_spec_serializes() {
111 let tpl = RouteTemplateSpec {
112 id: "http-route".into(),
113 parameters: vec![TemplateParameterSpec {
114 name: "path".into(),
115 default_value: None,
116 description: None,
117 }],
118 route: serde_json::json!({"from": {"uri": "rest:{{path}}"}}),
119 };
120 let json = serde_json::to_string(&tpl).unwrap();
121 assert!(json.contains("http-route"));
122 assert!(json.contains("path"));
123 }
124
125 #[test]
126 fn templated_route_spec_serializes() {
127 let spec = TemplatedRouteSpec {
128 route_template_ref: "http-route".into(),
129 route_id: Some("my-route".into()),
130 parameters: [("path".into(), "/api".into())].into_iter().collect(),
131 };
132 let json = serde_json::to_string(&spec).unwrap();
133 assert!(json.contains("http-route"));
134 assert!(json.contains("/api"));
135 }
136
137 #[test]
138 fn template_instance_record_serializes() {
139 let instance = TemplateInstanceRecord {
140 template_id: "http-route".into(),
141 instance_id: Uuid::nil(),
142 route_id: "my-route".into(),
143 parameters: [("path".into(), "/api".into())].into_iter().collect(),
144 };
145 let json = serde_json::to_string(&instance).unwrap();
146 assert!(json.contains("http-route"));
147 assert!(json.contains("my-route"));
148 }
149
150 #[test]
151 fn template_error_display_messages() {
152 let cases: Vec<(TemplateError, &str)> = vec![
153 (
154 TemplateError::MissingParameter("x".into()),
155 "missing required parameter: x",
156 ),
157 (
158 TemplateError::UnknownParameter("y".into()),
159 "unknown parameter: y",
160 ),
161 (
162 TemplateError::AlreadyRegistered("z".into()),
163 "template already registered: z",
164 ),
165 (
166 TemplateError::InvalidBody("d".into()),
167 "invalid template body: d",
168 ),
169 ];
170 for (err, expected) in cases {
171 assert_eq!(format!("{err}"), expected);
172 }
173 }
174
175 #[test]
176 fn template_error_is_clone() {
177 let err = TemplateError::MissingParameter("host".into());
178 let cloned = err.clone();
179 assert!(matches!(cloned, TemplateError::MissingParameter(_)));
180 }
181}