use crate::metadata::{
CodeMetadata, ErrorMetadata, FieldMetadata, RegularVariantMetadata, VariantMetadata,
};
use crate::registry::ERROR_REGISTRY;
use serde::{Serialize, Serializer};
fn serialize_status_code<S: Serializer>(
status: &http::StatusCode,
serializer: S,
) -> Result<S::Ok, S::Error> {
serializer.serialize_u16(status.as_u16())
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CatalogErrorEntry {
pub type_name: &'static str,
pub variants: Vec<CatalogVariantEntry>,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CatalogVariantEntry {
pub name: &'static str,
pub message: &'static str,
pub code: CodeMetadata,
#[serde(serialize_with = "serialize_status_code")]
pub http_status: http::StatusCode,
#[serde(skip_serializing_if = "Option::is_none")]
pub help: Option<&'static str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<&'static str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub severity: Option<&'static str>,
pub fields: &'static [FieldMetadata],
}
impl From<&RegularVariantMetadata> for CatalogVariantEntry {
fn from(v: &RegularVariantMetadata) -> Self {
Self {
name: v.name,
message: v.message,
code: CodeMetadata {
default: v.code,
screaming_snake: v.code_screaming_snake,
camel: v.code_camel,
pascal: v.code_pascal,
kebab: v.code_kebab,
},
http_status: crate::private::http_status_from_u16(v.http_status),
help: v.help,
url: v.url,
severity: v.severity,
fields: v.fields,
}
}
}
fn find_error_by_type_name(type_name: &str) -> Option<&'static ErrorMetadata> {
ERROR_REGISTRY
.iter()
.find(|entry| entry.metadata.type_name == type_name)
.map(|entry| entry.metadata)
}
fn expand_variants(variants: &[VariantMetadata]) -> Vec<CatalogVariantEntry> {
let mut result = Vec::new();
for variant in variants {
match variant {
VariantMetadata::Regular(regular) => {
result.push(CatalogVariantEntry::from(regular));
}
VariantMetadata::Transparent(transparent) => {
if let Some(inner_metadata) = find_error_by_type_name(transparent.forward_to) {
result.extend(expand_variants(inner_metadata.variants));
}
}
}
}
result
}
pub fn error_catalog() -> Vec<CatalogErrorEntry> {
ERROR_REGISTRY
.iter()
.map(|entry| {
let metadata = entry.metadata;
CatalogErrorEntry {
type_name: metadata.type_name,
variants: expand_variants(metadata.variants),
}
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_catalog_returns_entries() {
let catalog = error_catalog();
assert!(catalog.is_empty() || !catalog.is_empty());
}
}