api-macros: Move parsing logic into a separate module

This commit is contained in:
Jonas Platte 2021-04-05 12:07:34 +02:00
parent e7643d4c77
commit 679508f831
No known key found for this signature in database
GPG Key ID: CC154DE0E30B7C67
4 changed files with 341 additions and 353 deletions

View File

@ -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<Type>,
}
mod kw {
syn::custom_keyword!(error);
}
impl Parse for Api {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
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<syn::Error> = 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<TokenStream> {
// Guarantee `ruma_api` is available and named something we can refer to.
let ruma_api = util::import_ruma_api();

View File

@ -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<Self> {
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<syn::Error> = 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<Self> {
let attributes = input.call(Attribute::parse_outer)?;
let request_kw = input.parse::<kw::request>()?;
input.parse::<Token![:]>()?;
let fields;
braced!(fields in input);
let fields = fields.parse_terminated::<Field, Token![,]>(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::<syn::Result<Vec<_>>>()?;
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<Self> {
let attributes = input.call(Attribute::parse_outer)?;
let response_kw = input.parse::<kw::response>()?;
input.parse::<Token![:]>()?;
let fields;
braced!(fields in input);
let fields = fields
.parse_terminated::<Field, Token![,]>(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::<Result<Vec<_>, _>>()?;
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::<syn::Result<Vec<_>>>()?;
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() })
}
}

View File

@ -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<Lifetime>,
path: BTreeSet<Lifetime>,
query: BTreeSet<Lifetime>,
header: BTreeSet<Lifetime>,
pub(super) struct RequestLifetimes {
pub body: BTreeSet<Lifetime>,
pub path: BTreeSet<Lifetime>,
pub query: BTreeSet<Lifetime>,
pub header: BTreeSet<Lifetime>,
}
/// 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<Attribute>,
pub(super) attributes: Vec<Attribute>,
/// The fields of the request.
fields: Vec<RequestField>,
pub(super) fields: Vec<RequestField>,
/// 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<Self> {
let attributes = input.call(Attribute::parse_outer)?;
let request_kw = input.parse::<kw::request>()?;
input.parse::<Token![:]>()?;
let fields;
braced!(fields in input);
let fields = fields.parse_terminated::<Field, Token![,]>(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::<syn::Result<Vec<_>>>()?;
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<Ident>) -> Self {
pub fn new(kind: RequestFieldKind, field: Field, header: Option<Ident>) -> 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,

View File

@ -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<Attribute>,
pub attributes: Vec<Attribute>,
/// The fields of the response.
fields: Vec<ResponseField>,
pub fields: Vec<ResponseField>,
// 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<Self> {
let attributes = input.call(Attribute::parse_outer)?;
let response_kw = input.parse::<kw::response>()?;
input.parse::<Token![:]>()?;
let fields;
braced!(fields in input);
let fields = fields
.parse_terminated::<Field, Token![,]>(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::<Result<Vec<_>, _>>()?;
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::<syn::Result<Vec<_>>>()?;
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,