1use serde::Serialize;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum FieldCase {
14 SnakeCase,
16 CamelCase,
18 PascalCase,
20 ScreamingSnakeCase,
22 KebabCase,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum CodeCase {
34 Default,
36 ScreamingSnakeCase,
38 CamelCase,
40 PascalCase,
42 KebabCase,
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub struct FormatConfig {
49 pub field_case: FieldCase,
50 pub code_case: CodeCase,
51}
52
53impl Default for FormatConfig {
54 fn default() -> Self {
55 Self {
56 field_case: FieldCase::SnakeCase,
57 code_case: CodeCase::Default,
58 }
59 }
60}
61
62#[derive(Debug, Clone, Serialize)]
64#[serde(rename_all = "camelCase")]
65pub struct FieldMetadata {
66 pub rust_name: &'static str,
68
69 pub output_name: &'static str,
71
72 pub snake_case: &'static str,
74
75 pub camel_case: &'static str,
77
78 pub pascal_case: &'static str,
80
81 pub screaming_snake_case: &'static str,
83
84 pub kebab_case: &'static str,
86
87 pub ty: &'static str,
89
90 pub is_extension: bool,
92
93 #[serde(skip_serializing_if = "Option::is_none")]
95 pub http_header: Option<&'static str>,
96}
97
98impl FieldMetadata {
99 pub fn name_for(&self, case: FieldCase) -> &'static str {
101 match case {
102 FieldCase::SnakeCase => self.snake_case,
103 FieldCase::CamelCase => self.camel_case,
104 FieldCase::PascalCase => self.pascal_case,
105 FieldCase::ScreamingSnakeCase => self.screaming_snake_case,
106 FieldCase::KebabCase => self.kebab_case,
107 }
108 }
109}
110
111#[derive(Debug, Clone, Serialize)]
113#[serde(rename_all = "camelCase")]
114pub struct CodeMetadata {
115 pub default: &'static str,
117 pub screaming_snake: &'static str,
119 pub camel: &'static str,
121 pub pascal: &'static str,
123 pub kebab: &'static str,
125}
126
127#[derive(Debug, Clone, Serialize)]
129#[serde(rename_all = "camelCase")]
130pub struct RegularVariantMetadata {
131 pub name: &'static str,
133
134 pub message: &'static str,
136
137 pub code: &'static str,
139
140 pub code_screaming_snake: &'static str,
142
143 pub code_camel: &'static str,
145
146 pub code_pascal: &'static str,
148
149 pub code_kebab: &'static str,
151
152 pub http_status: u16,
154
155 pub jsonrpc_code: i32,
157
158 #[serde(skip_serializing_if = "Option::is_none")]
160 pub help: Option<&'static str>,
161
162 #[serde(skip_serializing_if = "Option::is_none")]
164 pub url: Option<&'static str>,
165
166 #[serde(skip_serializing_if = "Option::is_none")]
168 pub severity: Option<&'static str>,
169
170 pub fields: &'static [FieldMetadata],
172}
173
174impl RegularVariantMetadata {
175 pub fn code_for(&self, case: CodeCase) -> &'static str {
177 match case {
178 CodeCase::Default => self.code,
179 CodeCase::ScreamingSnakeCase => self.code_screaming_snake,
180 CodeCase::CamelCase => self.code_camel,
181 CodeCase::PascalCase => self.code_pascal,
182 CodeCase::KebabCase => self.code_kebab,
183 }
184 }
185}
186
187#[derive(Debug, Clone, Serialize)]
189#[serde(rename_all = "camelCase")]
190pub struct TransparentVariantMetadata {
191 pub name: &'static str,
193
194 pub forward_to: &'static str,
196}
197
198#[derive(Debug, Clone, Serialize)]
200#[serde(untagged)]
201pub enum VariantMetadata {
202 Regular(RegularVariantMetadata),
204
205 Transparent(TransparentVariantMetadata),
207}
208
209#[derive(Debug, Clone, Serialize)]
211#[serde(rename_all = "camelCase")]
212pub struct ErrorMetadata {
213 pub type_name: &'static str,
215
216 pub variants: &'static [VariantMetadata],
218}
219
220#[cfg(test)]
221mod tests {
222 use super::{
223 CodeCase, ErrorMetadata, FieldCase, FieldMetadata, RegularVariantMetadata, VariantMetadata,
224 };
225
226 #[test]
227 fn test_serialize_error_metadata() {
228 static FIELDS: &[FieldMetadata] = &[];
229 static VARIANTS: &[VariantMetadata] = &[VariantMetadata::Regular(RegularVariantMetadata {
230 name: "TestVariant",
231 message: "test message",
232 code: "test::code",
233 code_screaming_snake: "TEST_CODE",
234 code_camel: "testCode",
235 code_pascal: "TestCode",
236 code_kebab: "test-code",
237 http_status: 200,
238 jsonrpc_code: -32000,
239 help: None,
240 url: None,
241 severity: None,
242 fields: FIELDS,
243 })];
244
245 let metadata = ErrorMetadata {
246 type_name: "TestError",
247 variants: VARIANTS,
248 };
249
250 let json = serde_json::to_value(&metadata).unwrap();
251 assert_eq!(json["typeName"], "TestError");
252 let variants_json = json["variants"].as_array().unwrap();
253 assert_eq!(variants_json.len(), 1);
254 assert_eq!(variants_json[0]["name"], "TestVariant");
255 assert_eq!(variants_json[0]["code"], "test::code");
256 }
257
258 #[test]
259 fn test_field_metadata_name_for() {
260 let field_metadata = FieldMetadata {
261 rust_name: "my_field",
262 output_name: "my_field",
263 snake_case: "my_field",
264 camel_case: "myField",
265 pascal_case: "MyField",
266 screaming_snake_case: "MY_FIELD",
267 kebab_case: "my-field",
268 ty: "String",
269 is_extension: false,
270 http_header: None,
271 };
272 assert_eq!(field_metadata.name_for(FieldCase::SnakeCase), "my_field");
273 assert_eq!(field_metadata.name_for(FieldCase::CamelCase), "myField");
274 assert_eq!(field_metadata.name_for(FieldCase::PascalCase), "MyField");
275 assert_eq!(
276 field_metadata.name_for(FieldCase::ScreamingSnakeCase),
277 "MY_FIELD"
278 );
279 assert_eq!(field_metadata.name_for(FieldCase::KebabCase), "my-field");
280 }
281
282 #[test]
283 fn test_field_metadata_name_for_with_rename() {
284 let field = FieldMetadata {
285 rust_name: "my_field",
286 output_name: "myRenamedField",
287 snake_case: "my_renamed_field",
288 camel_case: "myRenamedField",
289 pascal_case: "MyRenamedField",
290 screaming_snake_case: "MY_RENAMED_FIELD",
291 kebab_case: "my-renamed-field",
292 ty: "String",
293 is_extension: true,
294 http_header: None,
295 };
296 assert_eq!(field.name_for(FieldCase::SnakeCase), "my_renamed_field");
297 assert_eq!(field.name_for(FieldCase::CamelCase), "myRenamedField");
298 assert_eq!(field.name_for(FieldCase::PascalCase), "MyRenamedField");
299 assert_eq!(
300 field.name_for(FieldCase::ScreamingSnakeCase),
301 "MY_RENAMED_FIELD"
302 );
303 assert_eq!(field.name_for(FieldCase::KebabCase), "my-renamed-field");
304 }
305
306 #[test]
307 fn test_regular_variant_metadata_code_for() {
308 let regular_variant_metadata = RegularVariantMetadata {
309 name: "MyError",
310 message: "My error message",
311 code: "my_error",
312 code_screaming_snake: "MY_ERROR",
313 code_camel: "myError",
314 code_pascal: "MyError",
315 code_kebab: "my-error",
316 http_status: 500,
317 jsonrpc_code: 1000,
318 help: None,
319 url: None,
320 severity: None,
321 fields: &[],
322 };
323 assert_eq!(
324 regular_variant_metadata.code_for(CodeCase::Default),
325 "my_error"
326 );
327 assert_eq!(
328 regular_variant_metadata.code_for(CodeCase::ScreamingSnakeCase),
329 "MY_ERROR"
330 );
331 assert_eq!(
332 regular_variant_metadata.code_for(CodeCase::CamelCase),
333 "myError"
334 );
335 assert_eq!(
336 regular_variant_metadata.code_for(CodeCase::PascalCase),
337 "MyError"
338 );
339 assert_eq!(
340 regular_variant_metadata.code_for(CodeCase::KebabCase),
341 "my-error"
342 );
343 }
344}