1use crate::metadata::{
7 CodeMetadata, ErrorMetadata, FieldMetadata, RegularVariantMetadata, VariantMetadata,
8};
9use crate::registry::ERROR_REGISTRY;
10use serde::{Serialize, Serializer};
11
12fn serialize_status_code<S: Serializer>(
14 status: &http::StatusCode,
15 serializer: S,
16) -> Result<S::Ok, S::Error> {
17 serializer.serialize_u16(status.as_u16())
18}
19
20#[derive(Debug, Clone, Serialize)]
22#[serde(rename_all = "camelCase")]
23pub struct CatalogErrorEntry {
24 pub type_name: &'static str,
26
27 pub variants: Vec<CatalogVariantEntry>,
29}
30
31#[derive(Debug, Clone, Serialize)]
36#[serde(rename_all = "camelCase")]
37pub struct CatalogVariantEntry {
38 pub name: &'static str,
40
41 pub message: &'static str,
43
44 pub code: CodeMetadata,
46
47 #[serde(serialize_with = "serialize_status_code")]
49 pub http_status: http::StatusCode,
50
51 #[serde(skip_serializing_if = "Option::is_none")]
53 pub help: Option<&'static str>,
54
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub url: Option<&'static str>,
58
59 #[serde(skip_serializing_if = "Option::is_none")]
61 pub severity: Option<&'static str>,
62
63 pub fields: &'static [FieldMetadata],
65}
66
67impl From<&RegularVariantMetadata> for CatalogVariantEntry {
68 fn from(v: &RegularVariantMetadata) -> Self {
69 Self {
70 name: v.name,
71 message: v.message,
72 code: CodeMetadata {
73 default: v.code,
74 screaming_snake: v.code_screaming_snake,
75 camel: v.code_camel,
76 pascal: v.code_pascal,
77 kebab: v.code_kebab,
78 },
79 http_status: crate::private::http_status_from_u16(v.http_status),
80 help: v.help,
81 url: v.url,
82 severity: v.severity,
83 fields: v.fields,
84 }
85 }
86}
87
88fn find_error_by_type_name(type_name: &str) -> Option<&'static ErrorMetadata> {
90 ERROR_REGISTRY
91 .iter()
92 .find(|entry| entry.metadata.type_name == type_name)
93 .map(|entry| entry.metadata)
94}
95
96fn expand_variants(variants: &[VariantMetadata]) -> Vec<CatalogVariantEntry> {
98 let mut result = Vec::new();
99
100 for variant in variants {
101 match variant {
102 VariantMetadata::Regular(regular) => {
103 result.push(CatalogVariantEntry::from(regular));
104 }
105 VariantMetadata::Transparent(transparent) => {
106 if let Some(inner_metadata) = find_error_by_type_name(transparent.forward_to) {
108 result.extend(expand_variants(inner_metadata.variants));
110 }
111 }
114 }
115 }
116
117 result
118}
119
120pub fn error_catalog() -> Vec<CatalogErrorEntry> {
136 ERROR_REGISTRY
137 .iter()
138 .map(|entry| {
139 let metadata = entry.metadata;
140 CatalogErrorEntry {
141 type_name: metadata.type_name,
142 variants: expand_variants(metadata.variants),
143 }
144 })
145 .collect()
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151
152 #[test]
153 fn test_catalog_returns_entries() {
154 let catalog = error_catalog();
155 assert!(catalog.is_empty() || !catalog.is_empty());
158 }
159}