use std::mem;
use proc_macro2::{Ident, TokenStream};
use quote::ToTokens;
use syn::parse::ParseStream;
use syn::punctuated::Punctuated;
use syn::token::Paren;
use syn::{Error, LitStr, Token, TypePath, WherePredicate};
use crate::component::serde::RenameRule;
use crate::component::{schema, GenericType, TypeTree};
use crate::parse_utils::{LitBoolOrExprPath, LitStrOrExpr};
use crate::path::parameter::{self, ParameterStyle};
use crate::schema_type::KnownFormat;
use crate::{parse_utils, AnyValue, Array, Diagnostics};
use super::{impl_feature, Feature, Parse};
use quote::quote;
impl_feature! {
#[derive(Clone)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct Default(pub(crate) Option<AnyValue>);
}
impl Default {
pub fn new_default_trait(struct_ident: Ident, field_ident: syn::Member) -> Self {
Self(Some(AnyValue::new_default_trait(struct_ident, field_ident)))
}
}
impl Parse for Default {
fn parse(input: syn::parse::ParseStream, _: proc_macro2::Ident) -> syn::Result<Self> {
if input.peek(syn::Token![=]) {
parse_utils::parse_next(input, || AnyValue::parse_any(input)).map(|any| Self(Some(any)))
} else {
Ok(Self(None))
}
}
}
impl ToTokens for Default {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
match &self.0 {
Some(inner) => tokens.extend(quote! {Some(#inner)}),
None => tokens.extend(quote! {None}),
}
}
}
impl From<self::Default> for Feature {
fn from(value: self::Default) -> Self {
Feature::Default(value)
}
}
impl_feature! {
#[derive(Clone)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct Example(AnyValue);
}
impl Parse for Example {
fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
parse_utils::parse_next(input, || AnyValue::parse_any(input)).map(Self)
}
}
impl ToTokens for Example {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
tokens.extend(self.0.to_token_stream())
}
}
impl From<Example> for Feature {
fn from(value: Example) -> Self {
Feature::Example(value)
}
}
impl_feature! {
#[derive(Clone)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct Examples(Vec<AnyValue>);
}
impl Parse for Examples {
fn parse(input: ParseStream, _: Ident) -> syn::Result<Self>
where
Self: std::marker::Sized,
{
let examples;
syn::parenthesized!(examples in input);
Ok(Self(
Punctuated::<AnyValue, Token![,]>::parse_terminated_with(
&examples,
AnyValue::parse_any,
)?
.into_iter()
.collect(),
))
}
}
impl ToTokens for Examples {
fn to_tokens(&self, tokens: &mut TokenStream) {
if !self.0.is_empty() {
let examples = Array::Borrowed(&self.0).to_token_stream();
examples.to_tokens(tokens);
}
}
}
impl From<Examples> for Feature {
fn from(value: Examples) -> Self {
Feature::Examples(value)
}
}
impl_feature! {"xml" =>
#[derive(Default, Clone)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct XmlAttr(schema::xml::XmlAttr);
}
impl XmlAttr {
pub fn split_for_vec(
&mut self,
type_tree: &TypeTree,
) -> Result<(Option<XmlAttr>, Option<XmlAttr>), Diagnostics> {
if matches!(type_tree.generic_type, Some(GenericType::Vec)) {
let mut value_xml = mem::take(self);
let vec_xml = schema::xml::XmlAttr::with_wrapped(
mem::take(&mut value_xml.0.is_wrapped),
mem::take(&mut value_xml.0.wrap_name),
);
Ok((Some(XmlAttr(vec_xml)), Some(value_xml)))
} else {
self.validate_xml(&self.0)?;
Ok((None, Some(mem::take(self))))
}
}
#[inline]
fn validate_xml(&self, xml: &schema::xml::XmlAttr) -> Result<(), Diagnostics> {
if let Some(wrapped_ident) = xml.is_wrapped.as_ref() {
Err(Diagnostics::with_span(
wrapped_ident.span(),
"cannot use `wrapped` attribute in non slice field type",
)
.help("Try removing `wrapped` attribute or make your field `Vec`"))
} else {
Ok(())
}
}
}
impl Parse for XmlAttr {
fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
let xml;
syn::parenthesized!(xml in input);
xml.parse::<schema::xml::XmlAttr>().map(Self)
}
}
impl ToTokens for XmlAttr {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
tokens.extend(self.0.to_token_stream())
}
}
impl From<XmlAttr> for Feature {
fn from(value: XmlAttr) -> Self {
Feature::XmlAttr(value)
}
}
impl_feature! {
#[derive(Clone)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct Format(KnownFormat);
}
impl Parse for Format {
fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
parse_utils::parse_next(input, || input.parse::<KnownFormat>()).map(Self)
}
}
impl ToTokens for Format {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
tokens.extend(self.0.to_token_stream())
}
}
impl From<Format> for Feature {
fn from(value: Format) -> Self {
Feature::Format(value)
}
}
impl_feature! {
#[derive(Clone, Copy)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct WriteOnly(bool);
}
impl Parse for WriteOnly {
fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
parse_utils::parse_bool_or_true(input).map(Self)
}
}
impl ToTokens for WriteOnly {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
tokens.extend(self.0.to_token_stream())
}
}
impl From<WriteOnly> for Feature {
fn from(value: WriteOnly) -> Self {
Feature::WriteOnly(value)
}
}
impl_feature! {
#[derive(Clone, Copy)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct ReadOnly(bool);
}
impl Parse for ReadOnly {
fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
parse_utils::parse_bool_or_true(input).map(Self)
}
}
impl ToTokens for ReadOnly {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
tokens.extend(self.0.to_token_stream())
}
}
impl From<ReadOnly> for Feature {
fn from(value: ReadOnly) -> Self {
Feature::ReadOnly(value)
}
}
impl_feature! {
#[derive(Clone)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct Title(String);
}
impl Parse for Title {
fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
parse_utils::parse_next_literal_str(input).map(Self)
}
}
impl ToTokens for Title {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
tokens.extend(self.0.to_token_stream())
}
}
impl From<Title> for Feature {
fn from(value: Title) -> Self {
Feature::Title(value)
}
}
impl_feature! {
#[derive(Clone, Copy)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct Nullable(bool);
}
impl Nullable {
pub fn new() -> Self {
Self(true)
}
pub fn value(&self) -> bool {
self.0
}
pub fn into_schema_type_token_stream(self) -> proc_macro2::TokenStream {
if self.0 {
quote! {utoipa::openapi::schema::Type::Null}
} else {
proc_macro2::TokenStream::new()
}
}
}
impl Parse for Nullable {
fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
parse_utils::parse_bool_or_true(input).map(Self)
}
}
impl ToTokens for Nullable {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
tokens.extend(self.0.to_token_stream())
}
}
impl From<Nullable> for Feature {
fn from(value: Nullable) -> Self {
Feature::Nullable(value)
}
}
impl_feature! {
#[cfg_attr(feature = "debug", derive(Debug))]
#[derive(Clone)]
pub struct Rename(String);
}
impl Rename {
pub fn into_value(self) -> String {
self.0
}
}
impl Parse for Rename {
fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
parse_utils::parse_next_literal_str(input).map(Self)
}
}
impl ToTokens for Rename {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.extend(self.0.to_token_stream())
}
}
impl From<Rename> for Feature {
fn from(value: Rename) -> Self {
Feature::Rename(value)
}
}
impl_feature! {
#[cfg_attr(feature = "debug", derive(Debug))]
#[derive(Clone)]
pub struct RenameAll(RenameRule);
}
impl RenameAll {
pub fn as_rename_rule(&self) -> &RenameRule {
&self.0
}
}
impl Parse for RenameAll {
fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
let litstr = parse_utils::parse_next(input, || input.parse::<LitStr>())?;
litstr
.value()
.parse::<RenameRule>()
.map_err(|error| syn::Error::new(litstr.span(), error.to_string()))
.map(Self)
}
}
impl From<RenameAll> for Feature {
fn from(value: RenameAll) -> Self {
Feature::RenameAll(value)
}
}
impl_feature! {
#[cfg_attr(feature = "debug", derive(Debug))]
#[derive(Clone)]
pub struct Style(ParameterStyle);
}
impl From<ParameterStyle> for Style {
fn from(style: ParameterStyle) -> Self {
Self(style)
}
}
impl Parse for Style {
fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
parse_utils::parse_next(input, || input.parse::<ParameterStyle>().map(Self))
}
}
impl ToTokens for Style {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.0.to_tokens(tokens)
}
}
impl From<Style> for Feature {
fn from(value: Style) -> Self {
Feature::Style(value)
}
}
impl_feature! {
#[cfg_attr(feature = "debug", derive(Debug))]
#[derive(Clone)]
pub struct ParameterIn(parameter::ParameterIn);
}
impl ParameterIn {
pub fn is_query(&self) -> bool {
matches!(self.0, parameter::ParameterIn::Query)
}
}
impl Parse for ParameterIn {
fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
parse_utils::parse_next(input, || input.parse::<parameter::ParameterIn>().map(Self))
}
}
impl ToTokens for ParameterIn {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.0.to_tokens(tokens);
}
}
impl From<ParameterIn> for Feature {
fn from(value: ParameterIn) -> Self {
Feature::ParameterIn(value)
}
}
impl_feature! {
#[cfg_attr(feature = "debug", derive(Debug))]
#[derive(Clone)]
pub struct AllowReserved(bool);
}
impl Parse for AllowReserved {
fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
parse_utils::parse_bool_or_true(input).map(Self)
}
}
impl ToTokens for AllowReserved {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.0.to_tokens(tokens)
}
}
impl From<AllowReserved> for Feature {
fn from(value: AllowReserved) -> Self {
Feature::AllowReserved(value)
}
}
impl_feature! {
#[cfg_attr(feature = "debug", derive(Debug))]
#[derive(Clone)]
pub struct Explode(bool);
}
impl Parse for Explode {
fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
parse_utils::parse_bool_or_true(input).map(Self)
}
}
impl ToTokens for Explode {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.0.to_tokens(tokens)
}
}
impl From<Explode> for Feature {
fn from(value: Explode) -> Self {
Feature::Explode(value)
}
}
impl_feature! {
#[derive(Clone)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct ValueType(syn::Type);
}
impl ValueType {
pub fn as_type_tree(&self) -> Result<TypeTree, Diagnostics> {
TypeTree::from_type(&self.0)
}
}
impl Parse for ValueType {
fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
parse_utils::parse_next(input, || input.parse::<syn::Type>()).map(Self)
}
}
impl From<ValueType> for Feature {
fn from(value: ValueType) -> Self {
Feature::ValueType(value)
}
}
impl_feature! {
#[derive(Clone)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct Inline(pub(super) bool);
}
impl Parse for Inline {
fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
parse_utils::parse_bool_or_true(input).map(Self)
}
}
impl From<bool> for Inline {
fn from(value: bool) -> Self {
Inline(value)
}
}
impl From<Inline> for Feature {
fn from(value: Inline) -> Self {
Feature::Inline(value)
}
}
impl_feature! {"names" =>
#[cfg_attr(feature = "debug", derive(Debug))]
#[derive(Clone)]
pub struct IntoParamsNames(Vec<String>);
}
impl IntoParamsNames {
pub fn into_values(self) -> Vec<String> {
self.0
}
}
impl Parse for IntoParamsNames {
fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
Ok(Self(
parse_utils::parse_comma_separated_within_parenthesis::<LitStr>(input)?
.iter()
.map(LitStr::value)
.collect(),
))
}
}
impl From<IntoParamsNames> for Feature {
fn from(value: IntoParamsNames) -> Self {
Feature::IntoParamsNames(value)
}
}
impl_feature! {
#[cfg_attr(feature = "debug", derive(Debug))]
#[derive(Clone)]
pub struct SchemaWith(TypePath);
}
impl Parse for SchemaWith {
fn parse(input: ParseStream, _: Ident) -> syn::Result<Self> {
parse_utils::parse_next(input, || input.parse::<TypePath>().map(Self))
}
}
impl ToTokens for SchemaWith {
fn to_tokens(&self, tokens: &mut TokenStream) {
let path = &self.0;
tokens.extend(quote! {
#path()
})
}
}
impl From<SchemaWith> for Feature {
fn from(value: SchemaWith) -> Self {
Feature::SchemaWith(value)
}
}
impl_feature! {
#[cfg_attr(feature = "debug", derive(Debug))]
#[derive(Clone)]
pub struct Description(parse_utils::LitStrOrExpr);
}
impl Parse for Description {
fn parse(input: ParseStream, _: Ident) -> syn::Result<Self>
where
Self: std::marker::Sized,
{
parse_utils::parse_next_literal_str_or_expr(input).map(Self)
}
}
impl ToTokens for Description {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.0.to_tokens(tokens);
}
}
impl From<String> for Description {
fn from(value: String) -> Self {
Self(value.into())
}
}
impl From<Description> for Feature {
fn from(value: Description) -> Self {
Self::Description(value)
}
}
impl_feature! {
#[cfg_attr(feature = "debug", derive(Debug))]
#[derive(Clone)]
pub struct Deprecated(bool);
}
impl Parse for Deprecated {
fn parse(input: ParseStream, _: Ident) -> syn::Result<Self>
where
Self: std::marker::Sized,
{
parse_utils::parse_bool_or_true(input).map(Self)
}
}
impl ToTokens for Deprecated {
fn to_tokens(&self, tokens: &mut TokenStream) {
let deprecated: crate::Deprecated = self.0.into();
deprecated.to_tokens(tokens);
}
}
impl From<Deprecated> for Feature {
fn from(value: Deprecated) -> Self {
Self::Deprecated(value)
}
}
impl From<bool> for Deprecated {
fn from(value: bool) -> Self {
Self(value)
}
}
impl_feature! {
#[cfg_attr(feature = "debug", derive(Debug))]
#[derive(Clone)]
pub struct As(pub TypePath);
}
impl As {
pub fn to_schema_formatted_string(&self) -> String {
self.0
.path
.segments
.iter()
.map(|segment| segment.ident.to_string())
.collect::<Vec<_>>()
.join(".")
}
}
impl Parse for As {
fn parse(input: ParseStream, _: Ident) -> syn::Result<Self>
where
Self: std::marker::Sized,
{
parse_utils::parse_next(input, || input.parse()).map(Self)
}
}
impl From<As> for Feature {
fn from(value: As) -> Self {
Self::As(value)
}
}
impl_feature! {
#[cfg_attr(feature = "debug", derive(Debug))]
#[derive(Clone)]
pub struct AdditionalProperties(bool);
}
impl Parse for AdditionalProperties {
fn parse(input: ParseStream, _: Ident) -> syn::Result<Self>
where
Self: std::marker::Sized,
{
parse_utils::parse_bool_or_true(input).map(Self)
}
}
impl ToTokens for AdditionalProperties {
fn to_tokens(&self, tokens: &mut TokenStream) {
let additional_properties = &self.0;
tokens.extend(quote!(
utoipa::openapi::schema::AdditionalProperties::FreeForm(
#additional_properties
)
))
}
}
impl From<AdditionalProperties> for Feature {
fn from(value: AdditionalProperties) -> Self {
Self::AdditionalProperties(value)
}
}
impl_feature! {
#[derive(Clone)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct Required(pub bool);
}
impl Required {
pub fn is_true(&self) -> bool {
self.0
}
}
impl Parse for Required {
fn parse(input: ParseStream, _: Ident) -> syn::Result<Self>
where
Self: std::marker::Sized,
{
parse_utils::parse_bool_or_true(input).map(Self)
}
}
impl ToTokens for Required {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.0.to_tokens(tokens)
}
}
impl From<crate::Required> for Required {
fn from(value: crate::Required) -> Self {
if value == crate::Required::True {
Self(true)
} else {
Self(false)
}
}
}
impl From<bool> for Required {
fn from(value: bool) -> Self {
Self(value)
}
}
impl From<Required> for Feature {
fn from(value: Required) -> Self {
Self::Required(value)
}
}
impl_feature! {
#[derive(Clone)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct ContentEncoding(String);
}
impl Parse for ContentEncoding {
fn parse(input: ParseStream, _: Ident) -> syn::Result<Self>
where
Self: std::marker::Sized,
{
parse_utils::parse_next_literal_str(input).map(Self)
}
}
impl ToTokens for ContentEncoding {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.0.to_tokens(tokens);
}
}
impl From<ContentEncoding> for Feature {
fn from(value: ContentEncoding) -> Self {
Self::ContentEncoding(value)
}
}
impl_feature! {
#[derive(Clone)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct ContentMediaType(String);
}
impl Parse for ContentMediaType {
fn parse(input: ParseStream, _: Ident) -> syn::Result<Self>
where
Self: std::marker::Sized,
{
parse_utils::parse_next_literal_str(input).map(Self)
}
}
impl ToTokens for ContentMediaType {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.0.to_tokens(tokens);
}
}
impl From<ContentMediaType> for Feature {
fn from(value: ContentMediaType) -> Self {
Self::ContentMediaType(value)
}
}
impl_feature! {
#[derive(Clone)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct Discriminator(LitStrOrExpr, Punctuated<(LitStrOrExpr, LitStrOrExpr), Token![,]>, Ident);
}
impl Discriminator {
fn new(attribute: Ident) -> Self {
Self(LitStrOrExpr::default(), Punctuated::default(), attribute)
}
pub fn get_attribute(&self) -> &Ident {
&self.2
}
}
impl Parse for Discriminator {
fn parse(input: ParseStream, attribute: Ident) -> syn::Result<Self>
where
Self: std::marker::Sized,
{
let lookahead = input.lookahead1();
if lookahead.peek(Token![=]) {
parse_utils::parse_next_literal_str_or_expr(input)
.map(|property_name| Self(property_name, Punctuated::new(), attribute))
} else if lookahead.peek(Paren) {
let discriminator_stream;
syn::parenthesized!(discriminator_stream in input);
let mut discriminator = Discriminator::new(attribute);
while !discriminator_stream.is_empty() {
let property = discriminator_stream.parse::<Ident>()?;
let name = &*property.to_string();
match name {
"property_name" => {
discriminator.0 =
parse_utils::parse_next_literal_str_or_expr(&discriminator_stream)?
}
"mapping" => {
let mapping_stream;
syn::parenthesized!(mapping_stream in &discriminator_stream);
let mappings: Punctuated<(LitStrOrExpr, LitStrOrExpr), Token![,]> =
Punctuated::parse_terminated_with(&mapping_stream, |input| {
let inner;
syn::parenthesized!(inner in input);
let key = inner.parse::<LitStrOrExpr>()?;
inner.parse::<Token![=]>()?;
let value = inner.parse::<LitStrOrExpr>()?;
Ok((key, value))
})?;
discriminator.1 = mappings;
}
unexpected => {
return Err(Error::new(
property.span(),
format!(
"unexpected identifier {}, expected any of: property_name, mapping",
unexpected
),
))
}
}
if !discriminator_stream.is_empty() {
discriminator_stream.parse::<Token![,]>()?;
}
}
Ok(discriminator)
} else {
Err(lookahead.error())
}
}
}
impl ToTokens for Discriminator {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Discriminator(property_name, mapping, _) = self;
struct Mapping<'m>(&'m LitStrOrExpr, &'m LitStrOrExpr);
impl ToTokens for Mapping<'_> {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Mapping(property_name, value) = *self;
tokens.extend(quote! {
(#property_name, #value)
})
}
}
let discriminator = if !mapping.is_empty() {
let mapping = mapping
.iter()
.map(|(key, value)| Mapping(key, value))
.collect::<Array<Mapping>>();
quote! {
utoipa::openapi::schema::Discriminator::with_mapping(#property_name, #mapping)
}
} else {
quote! {
utoipa::openapi::schema::Discriminator::new(#property_name)
}
};
discriminator.to_tokens(tokens);
}
}
impl From<Discriminator> for Feature {
fn from(value: Discriminator) -> Self {
Self::Discriminator(value)
}
}
impl_feature! {
#[derive(Clone)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct Bound(pub(crate) Punctuated<WherePredicate, Token![,]>);
}
impl Parse for Bound {
fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
let litstr = parse_utils::parse_next(input, || input.parse::<LitStr>())?;
let bounds =
syn::parse::Parser::parse_str(<Punctuated<_, _>>::parse_terminated, &litstr.value())
.map_err(|err| syn::Error::new(litstr.span(), err.to_string()))?;
Ok(Self(bounds))
}
}
impl ToTokens for Bound {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
tokens.extend(self.0.to_token_stream())
}
}
impl From<Bound> for Feature {
fn from(value: Bound) -> Self {
Feature::Bound(value)
}
}
impl_feature! {
#[derive(Clone)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct Ignore(pub LitBoolOrExprPath);
}
impl Parse for Ignore {
fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self>
where
Self: std::marker::Sized,
{
parse_utils::parse_next_literal_bool_or_call(input).map(Self)
}
}
impl ToTokens for Ignore {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
tokens.extend(self.0.to_token_stream())
}
}
impl From<Ignore> for Feature {
fn from(value: Ignore) -> Self {
Self::Ignore(value)
}
}
impl From<bool> for Ignore {
fn from(value: bool) -> Self {
Self(value.into())
}
}
impl_feature! {
#[derive(Clone)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct NoRecursion;
}
impl Parse for NoRecursion {
fn parse(_: ParseStream, _: Ident) -> syn::Result<Self>
where
Self: std::marker::Sized,
{
Ok(Self)
}
}
impl From<NoRecursion> for Feature {
fn from(value: NoRecursion) -> Self {
Self::NoRecursion(value)
}
}