diff --git a/ruma-api-macros/src/api.rs b/ruma-api-macros/src/api.rs index 6feb7e1f..6fe890e0 100644 --- a/ruma-api-macros/src/api.rs +++ b/ruma-api-macros/src/api.rs @@ -2,13 +2,11 @@ use proc_macro2::TokenStream; use quote::quote; -use syn::{ - parse::{Parse, ParseStream}, - Token, Type, -}; +use syn::Type; pub(crate) mod attribute; pub(crate) mod metadata; +pub(crate) mod parse; pub(crate) mod request; pub(crate) mod response; @@ -30,53 +28,6 @@ pub struct Api { error_ty: Option, } -mod kw { - syn::custom_keyword!(error); -} - -impl Parse for Api { - fn parse(input: ParseStream<'_>) -> syn::Result { - let metadata: Metadata = input.parse()?; - let request: Request = input.parse()?; - let response: Response = input.parse()?; - - // TODO: Use `bool::then` when MSRV >= 1.50 - let error_ty = if input.peek(kw::error) { - let _: kw::error = input.parse()?; - let _: Token![:] = input.parse()?; - - Some(input.parse()?) - } else { - None - }; - - let newtype_body_field = request.newtype_body_field(); - if metadata.method == "GET" && (request.has_body_fields() || newtype_body_field.is_some()) { - let mut combined_error: Option = None; - let mut add_error = |field| { - let error = syn::Error::new_spanned(field, "GET endpoints can't have body fields"); - if let Some(combined_error_ref) = &mut combined_error { - combined_error_ref.combine(error); - } else { - combined_error = Some(error); - } - }; - - for field in request.body_fields() { - add_error(field); - } - - if let Some(field) = newtype_body_field { - add_error(field); - } - - return Err(combined_error.unwrap()); - } - - Ok(Self { metadata, request, response, error_ty }) - } -} - pub fn expand_all(api: Api) -> syn::Result { // Guarantee `ruma_api` is available and named something we can refer to. let ruma_api = util::import_ruma_api(); diff --git a/ruma-api-macros/src/api/parse.rs b/ruma-api-macros/src/api/parse.rs new file mode 100644 index 00000000..fad0dcc7 --- /dev/null +++ b/ruma-api-macros/src/api/parse.rs @@ -0,0 +1,296 @@ +use std::mem; + +use syn::{ + braced, + parse::{Parse, ParseStream}, + spanned::Spanned, + Attribute, Field, Token, +}; + +use super::{ + attribute::{Meta, MetaNameValue}, + request::{RequestField, RequestFieldKind, RequestLifetimes}, + response::{ResponseField, ResponseFieldKind}, + Api, Metadata, Request, Response, +}; +use crate::util; + +mod kw { + syn::custom_keyword!(error); + syn::custom_keyword!(request); + syn::custom_keyword!(response); +} + +impl Parse for Api { + fn parse(input: ParseStream<'_>) -> syn::Result { + let metadata: Metadata = input.parse()?; + let request: Request = input.parse()?; + let response: Response = input.parse()?; + + // TODO: Use `bool::then` when MSRV >= 1.50 + let error_ty = if input.peek(kw::error) { + let _: kw::error = input.parse()?; + let _: Token![:] = input.parse()?; + + Some(input.parse()?) + } else { + None + }; + + let newtype_body_field = request.newtype_body_field(); + if metadata.method == "GET" && (request.has_body_fields() || newtype_body_field.is_some()) { + let mut combined_error: Option = None; + let mut add_error = |field| { + let error = syn::Error::new_spanned(field, "GET endpoints can't have body fields"); + if let Some(combined_error_ref) = &mut combined_error { + combined_error_ref.combine(error); + } else { + combined_error = Some(error); + } + }; + + for field in request.body_fields() { + add_error(field); + } + + if let Some(field) = newtype_body_field { + add_error(field); + } + + return Err(combined_error.unwrap()); + } + + Ok(Self { metadata, request, response, error_ty }) + } +} + +impl Parse for Request { + fn parse(input: ParseStream<'_>) -> syn::Result { + let attributes = input.call(Attribute::parse_outer)?; + let request_kw = input.parse::()?; + input.parse::()?; + let fields; + braced!(fields in input); + + let fields = fields.parse_terminated::(Field::parse_named)?; + + let mut newtype_body_field = None; + let mut query_map_field = None; + let mut lifetimes = RequestLifetimes::default(); + + let fields = fields + .into_iter() + .map(|mut field| { + let mut field_kind = None; + let mut header = None; + + for attr in mem::replace(&mut field.attrs, Vec::new()) { + let meta = match Meta::from_attribute(&attr)? { + Some(m) => m, + None => { + field.attrs.push(attr); + continue; + } + }; + + if field_kind.is_some() { + return Err(syn::Error::new_spanned( + attr, + "There can only be one field kind attribute", + )); + } + + field_kind = Some(match meta { + Meta::Word(ident) => match &ident.to_string()[..] { + attr @ "body" | attr @ "raw_body" => util::req_res_meta_word( + attr, + &field, + &mut newtype_body_field, + RequestFieldKind::NewtypeBody, + RequestFieldKind::NewtypeRawBody, + )?, + "path" => RequestFieldKind::Path, + "query" => RequestFieldKind::Query, + "query_map" => { + if let Some(f) = &query_map_field { + let mut error = syn::Error::new_spanned( + field, + "There can only be one query map field", + ); + error.combine(syn::Error::new_spanned( + f, + "Previous query map field", + )); + return Err(error); + } + + query_map_field = Some(field.clone()); + RequestFieldKind::QueryMap + } + _ => { + return Err(syn::Error::new_spanned( + ident, + "Invalid #[ruma_api] argument, expected one of \ + `body`, `path`, `query`, `query_map`", + )); + } + }, + Meta::NameValue(MetaNameValue { name, value }) => util::req_res_name_value( + name, + value, + &mut header, + RequestFieldKind::Header, + )?, + }); + } + + match field_kind.unwrap_or(RequestFieldKind::Body) { + RequestFieldKind::Header => { + util::collect_lifetime_ident(&mut lifetimes.header, &field.ty) + } + RequestFieldKind::Body => { + util::collect_lifetime_ident(&mut lifetimes.body, &field.ty) + } + RequestFieldKind::NewtypeBody => { + util::collect_lifetime_ident(&mut lifetimes.body, &field.ty) + } + RequestFieldKind::NewtypeRawBody => { + util::collect_lifetime_ident(&mut lifetimes.body, &field.ty) + } + RequestFieldKind::Path => { + util::collect_lifetime_ident(&mut lifetimes.path, &field.ty) + } + RequestFieldKind::Query => { + util::collect_lifetime_ident(&mut lifetimes.query, &field.ty) + } + RequestFieldKind::QueryMap => { + util::collect_lifetime_ident(&mut lifetimes.query, &field.ty) + } + } + + Ok(RequestField::new(field_kind.unwrap_or(RequestFieldKind::Body), field, header)) + }) + .collect::>>()?; + + if newtype_body_field.is_some() && fields.iter().any(|f| f.is_body()) { + // TODO: highlight conflicting fields, + return Err(syn::Error::new_spanned( + request_kw, + "Can't have both a newtype body field and regular body fields", + )); + } + + if query_map_field.is_some() && fields.iter().any(|f| f.is_query()) { + return Err(syn::Error::new_spanned( + // TODO: raw, + request_kw, + "Can't have both a query map field and regular query fields", + )); + } + + // TODO when/if `&[(&str, &str)]` is supported remove this + if query_map_field.is_some() && !lifetimes.query.is_empty() { + return Err(syn::Error::new_spanned( + request_kw, + "Lifetimes are not allowed for query_map fields", + )); + } + + Ok(Self { attributes, fields, lifetimes, ruma_api_import: util::import_ruma_api() }) + } +} + +impl Parse for Response { + fn parse(input: ParseStream<'_>) -> syn::Result { + let attributes = input.call(Attribute::parse_outer)?; + let response_kw = input.parse::()?; + input.parse::()?; + let fields; + braced!(fields in input); + + let fields = fields + .parse_terminated::(Field::parse_named)? + .into_iter() + .map(|f| { + if util::has_lifetime(&f.ty) { + Err(syn::Error::new( + f.ident.span(), + "Lifetimes on Response fields cannot be supported until GAT are stable", + )) + } else { + Ok(f) + } + }) + .collect::, _>>()?; + + let mut newtype_body_field = None; + + let fields = fields + .into_iter() + .map(|mut field| { + let mut field_kind = None; + let mut header = None; + + for attr in mem::replace(&mut field.attrs, Vec::new()) { + let meta = match Meta::from_attribute(&attr)? { + Some(m) => m, + None => { + field.attrs.push(attr); + continue; + } + }; + + if field_kind.is_some() { + return Err(syn::Error::new_spanned( + attr, + "There can only be one field kind attribute", + )); + } + + field_kind = Some(match meta { + Meta::Word(ident) => match &ident.to_string()[..] { + s @ "body" | s @ "raw_body" => util::req_res_meta_word( + s, + &field, + &mut newtype_body_field, + ResponseFieldKind::NewtypeBody, + ResponseFieldKind::NewtypeRawBody, + )?, + _ => { + return Err(syn::Error::new_spanned( + ident, + "Invalid #[ruma_api] argument with value, expected `body`", + )); + } + }, + Meta::NameValue(MetaNameValue { name, value }) => util::req_res_name_value( + name, + value, + &mut header, + ResponseFieldKind::Header, + )?, + }); + } + + Ok(match field_kind.unwrap_or(ResponseFieldKind::Body) { + ResponseFieldKind::Body => ResponseField::Body(field), + ResponseFieldKind::Header => { + ResponseField::Header(field, header.expect("missing header name")) + } + ResponseFieldKind::NewtypeBody => ResponseField::NewtypeBody(field), + ResponseFieldKind::NewtypeRawBody => ResponseField::NewtypeRawBody(field), + }) + }) + .collect::>>()?; + + if newtype_body_field.is_some() && fields.iter().any(|f| f.is_body()) { + // TODO: highlight conflicting fields, + return Err(syn::Error::new_spanned( + response_kw, + "Can't have both a newtype body field and regular body fields", + )); + } + + Ok(Self { attributes, fields, ruma_api_import: util::import_ruma_api() }) + } +} diff --git a/ruma-api-macros/src/api/request.rs b/ruma-api-macros/src/api/request.rs index 82265615..9f8b1ee4 100644 --- a/ruma-api-macros/src/api/request.rs +++ b/ruma-api-macros/src/api/request.rs @@ -1,46 +1,34 @@ //! Details of the `request` section of the procedural macro. -use std::{collections::BTreeSet, mem}; +use std::collections::BTreeSet; use proc_macro2::TokenStream; use quote::{quote, quote_spanned, ToTokens}; -use syn::{ - braced, - parse::{Parse, ParseStream}, - spanned::Spanned, - Attribute, Field, Ident, Lifetime, Token, -}; +use syn::{spanned::Spanned, Attribute, Field, Ident, Lifetime}; -use crate::{ - api::attribute::{Meta, MetaNameValue}, - util, -}; - -mod kw { - syn::custom_keyword!(request); -} +use crate::util; #[derive(Debug, Default)] -pub struct RequestLifetimes { - body: BTreeSet, - path: BTreeSet, - query: BTreeSet, - header: BTreeSet, +pub(super) struct RequestLifetimes { + pub body: BTreeSet, + pub path: BTreeSet, + pub query: BTreeSet, + pub header: BTreeSet, } /// The result of processing the `request` section of the macro. -pub struct Request { +pub(crate) struct Request { /// The attributes that will be applied to the struct definition. - attributes: Vec, + pub(super) attributes: Vec, /// The fields of the request. - fields: Vec, + pub(super) fields: Vec, /// The collected lifetime identifiers from the declared fields. - lifetimes: RequestLifetimes, + pub(super) lifetimes: RequestLifetimes, // Guarantee `ruma_api` is available and named something we can refer to. - ruma_api_import: TokenStream, + pub(super) ruma_api_import: TokenStream, } impl Request { @@ -300,142 +288,6 @@ impl Request { } } -impl Parse for Request { - fn parse(input: ParseStream<'_>) -> syn::Result { - let attributes = input.call(Attribute::parse_outer)?; - let request_kw = input.parse::()?; - input.parse::()?; - let fields; - braced!(fields in input); - - let fields = fields.parse_terminated::(Field::parse_named)?; - - let mut newtype_body_field = None; - let mut query_map_field = None; - let mut lifetimes = RequestLifetimes::default(); - - let fields = fields - .into_iter() - .map(|mut field| { - let mut field_kind = None; - let mut header = None; - - for attr in mem::replace(&mut field.attrs, Vec::new()) { - let meta = match Meta::from_attribute(&attr)? { - Some(m) => m, - None => { - field.attrs.push(attr); - continue; - } - }; - - if field_kind.is_some() { - return Err(syn::Error::new_spanned( - attr, - "There can only be one field kind attribute", - )); - } - - field_kind = Some(match meta { - Meta::Word(ident) => match &ident.to_string()[..] { - attr @ "body" | attr @ "raw_body" => util::req_res_meta_word( - attr, - &field, - &mut newtype_body_field, - RequestFieldKind::NewtypeBody, - RequestFieldKind::NewtypeRawBody, - )?, - "path" => RequestFieldKind::Path, - "query" => RequestFieldKind::Query, - "query_map" => { - if let Some(f) = &query_map_field { - let mut error = syn::Error::new_spanned( - field, - "There can only be one query map field", - ); - error.combine(syn::Error::new_spanned( - f, - "Previous query map field", - )); - return Err(error); - } - - query_map_field = Some(field.clone()); - RequestFieldKind::QueryMap - } - _ => { - return Err(syn::Error::new_spanned( - ident, - "Invalid #[ruma_api] argument, expected one of \ - `body`, `path`, `query`, `query_map`", - )); - } - }, - Meta::NameValue(MetaNameValue { name, value }) => util::req_res_name_value( - name, - value, - &mut header, - RequestFieldKind::Header, - )?, - }); - } - - match field_kind.unwrap_or(RequestFieldKind::Body) { - RequestFieldKind::Header => { - util::collect_lifetime_ident(&mut lifetimes.header, &field.ty) - } - RequestFieldKind::Body => { - util::collect_lifetime_ident(&mut lifetimes.body, &field.ty) - } - RequestFieldKind::NewtypeBody => { - util::collect_lifetime_ident(&mut lifetimes.body, &field.ty) - } - RequestFieldKind::NewtypeRawBody => { - util::collect_lifetime_ident(&mut lifetimes.body, &field.ty) - } - RequestFieldKind::Path => { - util::collect_lifetime_ident(&mut lifetimes.path, &field.ty) - } - RequestFieldKind::Query => { - util::collect_lifetime_ident(&mut lifetimes.query, &field.ty) - } - RequestFieldKind::QueryMap => { - util::collect_lifetime_ident(&mut lifetimes.query, &field.ty) - } - } - - Ok(RequestField::new(field_kind.unwrap_or(RequestFieldKind::Body), field, header)) - }) - .collect::>>()?; - - if newtype_body_field.is_some() && fields.iter().any(|f| f.is_body()) { - // TODO: highlight conflicting fields, - return Err(syn::Error::new_spanned( - request_kw, - "Can't have both a newtype body field and regular body fields", - )); - } - - if query_map_field.is_some() && fields.iter().any(|f| f.is_query()) { - return Err(syn::Error::new_spanned( - // TODO: raw, - request_kw, - "Can't have both a query map field and regular query fields", - )); - } - - // TODO when/if `&[(&str, &str)]` is supported remove this - if query_map_field.is_some() && !lifetimes.query.is_empty() { - return Err(syn::Error::new_spanned( - request_kw, - "Lifetimes are not allowed for query_map fields", - )); - } - - Ok(Self { attributes, fields, lifetimes, ruma_api_import: util::import_ruma_api() }) - } -} - impl ToTokens for Request { fn to_tokens(&self, tokens: &mut TokenStream) { let ruma_api = &self.ruma_api_import; @@ -552,7 +404,7 @@ impl ToTokens for Request { } /// The types of fields that a request can have. -pub enum RequestField { +pub(crate) enum RequestField { /// JSON data in the body of the request. Body(Field), @@ -577,7 +429,7 @@ pub enum RequestField { impl RequestField { /// Creates a new `RequestField`. - fn new(kind: RequestFieldKind, field: Field, header: Option) -> Self { + pub fn new(kind: RequestFieldKind, field: Field, header: Option) -> Self { match kind { RequestFieldKind::Body => RequestField::Body(field), RequestFieldKind::Header => { @@ -592,7 +444,7 @@ impl RequestField { } /// Gets the kind of the request field. - fn kind(&self) -> RequestFieldKind { + pub fn kind(&self) -> RequestFieldKind { match self { RequestField::Body(..) => RequestFieldKind::Body, RequestField::Header(..) => RequestFieldKind::Header, @@ -605,57 +457,57 @@ impl RequestField { } /// Whether or not this request field is a body kind. - fn is_body(&self) -> bool { + pub fn is_body(&self) -> bool { self.kind() == RequestFieldKind::Body } /// Whether or not this request field is a header kind. - fn is_header(&self) -> bool { + pub fn is_header(&self) -> bool { self.kind() == RequestFieldKind::Header } /// Whether or not this request field is a newtype body kind. - fn is_newtype_body(&self) -> bool { + pub fn is_newtype_body(&self) -> bool { self.kind() == RequestFieldKind::NewtypeBody } /// Whether or not this request field is a path kind. - fn is_path(&self) -> bool { + pub fn is_path(&self) -> bool { self.kind() == RequestFieldKind::Path } /// Whether or not this request field is a query string kind. - fn is_query(&self) -> bool { + pub fn is_query(&self) -> bool { self.kind() == RequestFieldKind::Query } /// Return the contained field if this request field is a body kind. - fn as_body_field(&self) -> Option<&Field> { + pub fn as_body_field(&self) -> Option<&Field> { self.field_of_kind(RequestFieldKind::Body) } /// Return the contained field if this request field is a body kind. - fn as_newtype_body_field(&self) -> Option<&Field> { + pub fn as_newtype_body_field(&self) -> Option<&Field> { self.field_of_kind(RequestFieldKind::NewtypeBody) } /// Return the contained field if this request field is a raw body kind. - fn as_newtype_raw_body_field(&self) -> Option<&Field> { + pub fn as_newtype_raw_body_field(&self) -> Option<&Field> { self.field_of_kind(RequestFieldKind::NewtypeRawBody) } /// Return the contained field if this request field is a query kind. - fn as_query_field(&self) -> Option<&Field> { + pub fn as_query_field(&self) -> Option<&Field> { self.field_of_kind(RequestFieldKind::Query) } /// Return the contained field if this request field is a query map kind. - fn as_query_map_field(&self) -> Option<&Field> { + pub fn as_query_map_field(&self) -> Option<&Field> { self.field_of_kind(RequestFieldKind::QueryMap) } /// Gets the inner `Field` value. - fn field(&self) -> &Field { + pub fn field(&self) -> &Field { match self { RequestField::Body(field) | RequestField::Header(field, _) @@ -668,7 +520,7 @@ impl RequestField { } /// Gets the inner `Field` value if it's of the provided kind. - fn field_of_kind(&self, kind: RequestFieldKind) -> Option<&Field> { + pub fn field_of_kind(&self, kind: RequestFieldKind) -> Option<&Field> { if self.kind() == kind { Some(self.field()) } else { @@ -679,7 +531,7 @@ impl RequestField { /// The types of fields that a request can have, without their values. #[derive(Clone, Copy, PartialEq, Eq)] -enum RequestFieldKind { +pub(crate) enum RequestFieldKind { /// See the similarly named variant of `RequestField`. Body, diff --git a/ruma-api-macros/src/api/response.rs b/ruma-api-macros/src/api/response.rs index 8738013b..1f923067 100644 --- a/ruma-api-macros/src/api/response.rs +++ b/ruma-api-macros/src/api/response.rs @@ -1,35 +1,19 @@ //! Details of the `response` section of the procedural macro. -use std::mem; - use proc_macro2::TokenStream; use quote::{quote, quote_spanned, ToTokens}; -use syn::{ - braced, - parse::{Parse, ParseStream}, - spanned::Spanned, - Attribute, Field, Ident, Token, -}; - -use crate::{ - api::attribute::{Meta, MetaNameValue}, - util, -}; - -mod kw { - syn::custom_keyword!(response); -} +use syn::{spanned::Spanned, Attribute, Field, Ident}; /// The result of processing the `response` section of the macro. -pub struct Response { +pub(crate) struct Response { /// The attributes that will be applied to the struct definition. - attributes: Vec, + pub attributes: Vec, /// The fields of the response. - fields: Vec, + pub fields: Vec, // Guarantee `ruma_api` is available and named something we can refer to. - ruma_api_import: TokenStream, + pub ruma_api_import: TokenStream, } impl Response { @@ -211,101 +195,6 @@ impl Response { } } -impl Parse for Response { - fn parse(input: ParseStream<'_>) -> syn::Result { - let attributes = input.call(Attribute::parse_outer)?; - let response_kw = input.parse::()?; - input.parse::()?; - let fields; - braced!(fields in input); - - let fields = fields - .parse_terminated::(Field::parse_named)? - .into_iter() - .map(|f| { - if util::has_lifetime(&f.ty) { - Err(syn::Error::new( - f.ident.span(), - "Lifetimes on Response fields cannot be supported until GAT are stable", - )) - } else { - Ok(f) - } - }) - .collect::, _>>()?; - - let mut newtype_body_field = None; - - let fields = fields - .into_iter() - .map(|mut field| { - let mut field_kind = None; - let mut header = None; - - for attr in mem::replace(&mut field.attrs, Vec::new()) { - let meta = match Meta::from_attribute(&attr)? { - Some(m) => m, - None => { - field.attrs.push(attr); - continue; - } - }; - - if field_kind.is_some() { - return Err(syn::Error::new_spanned( - attr, - "There can only be one field kind attribute", - )); - } - - field_kind = Some(match meta { - Meta::Word(ident) => match &ident.to_string()[..] { - s @ "body" | s @ "raw_body" => util::req_res_meta_word( - s, - &field, - &mut newtype_body_field, - ResponseFieldKind::NewtypeBody, - ResponseFieldKind::NewtypeRawBody, - )?, - _ => { - return Err(syn::Error::new_spanned( - ident, - "Invalid #[ruma_api] argument with value, expected `body`", - )); - } - }, - Meta::NameValue(MetaNameValue { name, value }) => util::req_res_name_value( - name, - value, - &mut header, - ResponseFieldKind::Header, - )?, - }); - } - - Ok(match field_kind.unwrap_or(ResponseFieldKind::Body) { - ResponseFieldKind::Body => ResponseField::Body(field), - ResponseFieldKind::Header => { - ResponseField::Header(field, header.expect("missing header name")) - } - ResponseFieldKind::NewtypeBody => ResponseField::NewtypeBody(field), - ResponseFieldKind::NewtypeRawBody => ResponseField::NewtypeRawBody(field), - }) - }) - .collect::>>()?; - - if newtype_body_field.is_some() && fields.iter().any(|f| f.is_body()) { - // TODO: highlight conflicting fields, - return Err(syn::Error::new_spanned( - response_kw, - "Can't have both a newtype body field and regular body fields", - )); - } - - Ok(Self { attributes, fields, ruma_api_import: util::import_ruma_api() }) - } -} - impl ToTokens for Response { fn to_tokens(&self, tokens: &mut TokenStream) { let ruma_api = &self.ruma_api_import; @@ -356,7 +245,7 @@ impl ToTokens for Response { } /// The types of fields that a response can have. -pub enum ResponseField { +pub(crate) enum ResponseField { /// JSON data in the body of the response. Body(Field), @@ -372,7 +261,7 @@ pub enum ResponseField { impl ResponseField { /// Gets the inner `Field` value. - fn field(&self) -> &Field { + pub fn field(&self) -> &Field { match self { ResponseField::Body(field) | ResponseField::Header(field, _) @@ -382,22 +271,22 @@ impl ResponseField { } /// Whether or not this response field is a body kind. - fn is_body(&self) -> bool { + pub fn is_body(&self) -> bool { self.as_body_field().is_some() } /// Whether or not this response field is a header kind. - fn is_header(&self) -> bool { + pub fn is_header(&self) -> bool { matches!(self, ResponseField::Header(..)) } /// Whether or not this response field is a newtype body kind. - fn is_newtype_body(&self) -> bool { + pub fn is_newtype_body(&self) -> bool { self.as_newtype_body_field().is_some() } /// Return the contained field if this response field is a body kind. - fn as_body_field(&self) -> Option<&Field> { + pub fn as_body_field(&self) -> Option<&Field> { match self { ResponseField::Body(field) => Some(field), _ => None, @@ -405,7 +294,7 @@ impl ResponseField { } /// Return the contained field if this response field is a newtype body kind. - fn as_newtype_body_field(&self) -> Option<&Field> { + pub fn as_newtype_body_field(&self) -> Option<&Field> { match self { ResponseField::NewtypeBody(field) => Some(field), _ => None, @@ -413,7 +302,7 @@ impl ResponseField { } /// Return the contained field if this response field is a newtype raw body kind. - fn as_newtype_raw_body_field(&self) -> Option<&Field> { + pub fn as_newtype_raw_body_field(&self) -> Option<&Field> { match self { ResponseField::NewtypeRawBody(field) => Some(field), _ => None, @@ -422,7 +311,7 @@ impl ResponseField { } /// The types of fields that a response can have, without their values. -enum ResponseFieldKind { +pub(crate) enum ResponseFieldKind { /// See the similarly named variant of `ResponseField`. Body,