1use crate::metadata::{ErrorMetadata, FieldMetadata, RegularVariantMetadata, VariantMetadata};
7use crate::registry::ERROR_REGISTRY;
8use serde::{Serialize, Serializer};
9
10fn serialize_status_code<S: Serializer>(
12 status: &http::StatusCode,
13 serializer: S,
14) -> Result<S::Ok, S::Error> {
15 serializer.serialize_u16(status.as_u16())
16}
17
18#[derive(Debug, Clone, Serialize)]
20#[serde(rename_all = "camelCase")]
21pub struct CatalogErrorEntry {
22 pub type_name: &'static str,
24
25 pub variants: Vec<CatalogVariantEntry>,
27}
28
29#[derive(Debug, Clone, Serialize)]
34#[serde(rename_all = "camelCase")]
35pub struct CatalogVariantEntry {
36 pub name: &'static str,
38
39 pub message: &'static str,
41
42 pub code: &'static str,
44
45 #[serde(serialize_with = "serialize_status_code")]
47 pub http_status: http::StatusCode,
48
49 #[serde(skip_serializing_if = "Option::is_none")]
51 pub help: Option<&'static str>,
52
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub url: Option<&'static str>,
56
57 #[serde(skip_serializing_if = "Option::is_none")]
59 pub severity: Option<&'static str>,
60
61 pub fields: &'static [FieldMetadata],
63}
64
65impl From<&RegularVariantMetadata> for CatalogVariantEntry {
66 fn from(v: &RegularVariantMetadata) -> Self {
67 Self {
68 name: v.name,
69 message: v.message,
70 code: v.code,
71 http_status: http::StatusCode::from_u16(v.http_status)
72 .expect("validated at compile-time by the derive macro"),
73 help: v.help,
74 url: v.url,
75 severity: v.severity,
76 fields: v.fields,
77 }
78 }
79}
80
81fn find_error_by_type_name(type_name: &str) -> Option<&'static ErrorMetadata> {
83 ERROR_REGISTRY
84 .iter()
85 .find(|entry| entry.metadata.type_name == type_name)
86 .map(|entry| entry.metadata)
87}
88
89fn expand_variants(variants: &[VariantMetadata]) -> Vec<CatalogVariantEntry> {
91 let mut result = Vec::new();
92
93 for variant in variants {
94 match variant {
95 VariantMetadata::Regular(regular) => {
96 result.push(CatalogVariantEntry::from(regular));
97 }
98 VariantMetadata::Transparent(transparent) => {
99 if let Some(inner_metadata) = find_error_by_type_name(transparent.forward_to) {
101 result.extend(expand_variants(inner_metadata.variants));
103 }
104 }
107 }
108 }
109
110 result
111}
112
113pub fn error_catalog() -> Vec<CatalogErrorEntry> {
129 ERROR_REGISTRY
130 .iter()
131 .map(|entry| {
132 let metadata = entry.metadata;
133 CatalogErrorEntry {
134 type_name: metadata.type_name,
135 variants: expand_variants(metadata.variants),
136 }
137 })
138 .collect()
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 #[test]
146 fn test_catalog_returns_entries() {
147 let catalog = error_catalog();
148 assert!(catalog.is_empty() || !catalog.is_empty());
151 }
152}