1use std::marker::PhantomData;
8
9use utoipa::openapi::path::{Parameter, ParameterBuilder, ParameterIn};
10use utoipa::openapi::{Object, RefOr, Required, Schema, Type};
11use utoipa::IntoParams;
12
13use crate::headers::DocumentedHeader;
14
15pub struct DocHeaderEntry<H: DocumentedHeader>(PhantomData<H>);
32
33impl<H: DocumentedHeader> IntoParams for DocHeaderEntry<H> {
34 fn into_params(_parameter_in_provider: impl Fn() -> Option<ParameterIn>) -> Vec<Parameter> {
35 let desc = H::description();
36 let mut b = ParameterBuilder::new()
37 .name(H::name())
38 .parameter_in(ParameterIn::Header)
39 .required(Required::True)
40 .schema(Some(RefOr::T(Schema::Object(Object::with_type(
41 Type::String,
42 )))));
43 if !desc.is_empty() {
44 b = b.description(Some(desc.to_string()));
45 }
46 if let Some(ex) = H::example() {
47 b = b.example(Some(serde_json::Value::String(ex.to_string())));
48 }
49 vec![b.build()]
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 struct XApiKey;
58 impl DocumentedHeader for XApiKey {
59 fn name() -> &'static str {
60 "X-Api-Key"
61 }
62 fn description() -> &'static str {
63 "Tenant API key"
64 }
65 fn example() -> Option<&'static str> {
66 Some("ak_live_42")
67 }
68 }
69
70 struct BareHeader;
71 impl DocumentedHeader for BareHeader {
72 fn name() -> &'static str {
73 "X-Bare"
74 }
75 }
76
77 #[test]
78 fn doc_header_entry_into_params_uses_runtime_name() {
79 let params = DocHeaderEntry::<XApiKey>::into_params(|| None);
80 assert_eq!(params.len(), 1);
81 assert_eq!(params[0].name, "X-Api-Key");
82 assert!(matches!(params[0].parameter_in, ParameterIn::Header));
83 assert!(matches!(params[0].required, Required::True));
84 }
85
86 #[test]
87 fn doc_header_entry_into_params_emits_description_when_provided() {
88 let params = DocHeaderEntry::<XApiKey>::into_params(|| None);
89 assert_eq!(params[0].description.as_deref(), Some("Tenant API key"));
90 }
91
92 #[test]
93 fn doc_header_entry_into_params_omits_description_when_empty() {
94 let params = DocHeaderEntry::<BareHeader>::into_params(|| None);
95 assert!(params[0].description.is_none());
96 }
97
98 #[test]
99 fn doc_header_entry_into_params_emits_example_when_provided() {
100 let params = DocHeaderEntry::<XApiKey>::into_params(|| None);
101 let json = serde_json::to_value(¶ms[0]).unwrap();
103 assert_eq!(json["example"], "ak_live_42");
104 }
105}