utoipa_gen/component/schema/
xml.rs1use proc_macro2::Ident;
2use quote::{quote, ToTokens};
3use syn::{parenthesized, parse::Parse, token::Paren, Error, LitStr, Token};
4
5use crate::parse_utils;
6
7#[derive(Default, Clone)]
8#[cfg_attr(feature = "debug", derive(Debug))]
9pub struct XmlAttr {
10 pub name: Option<String>,
11 pub namespace: Option<String>,
12 pub prefix: Option<String>,
13 pub is_attribute: bool,
14 pub is_wrapped: Option<Ident>,
15 pub wrap_name: Option<String>,
16}
17
18impl XmlAttr {
19 pub fn with_wrapped(is_wrapped: Option<Ident>, wrap_name: Option<String>) -> Self {
20 Self {
21 is_wrapped,
22 wrap_name,
23 ..Default::default()
24 }
25 }
26}
27
28impl Parse for XmlAttr {
29 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
30 const EXPECTED_ATTRIBUTE_MESSAGE: &str =
31 "unexpected attribute, expected any of: name, namespace, prefix, attribute, wrapped";
32 let mut xml = XmlAttr::default();
33
34 while !input.is_empty() {
35 let attribute = input
36 .parse::<Ident>()
37 .map_err(|error| Error::new(error.span(), EXPECTED_ATTRIBUTE_MESSAGE))?;
38 let attribute_name = &*attribute.to_string();
39
40 match attribute_name {
41 "name" => {
42 xml.name =
43 Some(parse_utils::parse_next(input, || input.parse::<LitStr>())?.value())
44 }
45 "namespace" => {
46 xml.namespace =
47 Some(parse_utils::parse_next(input, || input.parse::<LitStr>())?.value())
48 }
49 "prefix" => {
50 xml.prefix =
51 Some(parse_utils::parse_next(input, || input.parse::<LitStr>())?.value())
52 }
53 "attribute" => xml.is_attribute = parse_utils::parse_bool_or_true(input)?,
54 "wrapped" => {
55 if input.peek(Paren) {
57 let group;
58 parenthesized!(group in input);
59
60 let wrapped_attribute = group.parse::<Ident>().map_err(|error| {
61 Error::new(
62 error.span(),
63 format!("unexpected attribute, expected: name, {error}"),
64 )
65 })?;
66 if wrapped_attribute != "name" {
67 return Err(Error::new(
68 wrapped_attribute.span(),
69 "unexpected wrapped attribute, expected: name",
70 ));
71 }
72 group.parse::<Token![=]>()?;
73 xml.wrap_name = Some(group.parse::<LitStr>()?.value());
74 }
75 xml.is_wrapped = Some(attribute);
76 }
77 _ => return Err(Error::new(attribute.span(), EXPECTED_ATTRIBUTE_MESSAGE)),
78 }
79
80 if !input.is_empty() {
81 input.parse::<Token![,]>()?;
82 }
83 }
84
85 Ok(xml)
86 }
87}
88
89impl ToTokens for XmlAttr {
90 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
91 tokens.extend(quote! {
92 utoipa::openapi::xml::XmlBuilder::new()
93 });
94
95 if let Some(ref name) = self.name {
96 tokens.extend(quote! {
97 .name(Some(#name))
98 })
99 }
100
101 if let Some(ref namespace) = self.namespace {
102 tokens.extend(quote! {
103 .namespace(Some(#namespace))
104 })
105 }
106
107 if let Some(ref prefix) = self.prefix {
108 tokens.extend(quote! {
109 .prefix(Some(#prefix))
110 })
111 }
112
113 if self.is_attribute {
114 tokens.extend(quote! {
115 .attribute(Some(true))
116 })
117 }
118
119 if self.is_wrapped.is_some() {
120 tokens.extend(quote! {
121 .wrapped(Some(true))
122 });
123
124 if let Some(ref wrap_name) = self.wrap_name {
126 tokens.extend(quote! {
127 .name(Some(#wrap_name))
128 })
129 }
130 }
131
132 tokens.extend(quote! { .build() })
133 }
134}