oas3-gen 0.17.2

A rust type generator for OpenAPI v3.1.x specification.
use proc_macro2::TokenStream;
use quote::quote;

use crate::generator::ast::FieldDef;

pub(crate) fn generate_docs(docs: &[String]) -> TokenStream {
  if docs.is_empty() {
    return quote! {};
  }
  let doc_lines: Vec<TokenStream> = docs
    .iter()
    .map(|line| {
      let clean = line.strip_prefix("/// ").unwrap_or(line);
      quote! { #[doc = #clean] }
    })
    .collect();
  quote! { #(#doc_lines)* }
}

pub(crate) fn generate_docs_for_field(field: &FieldDef) -> TokenStream {
  let mut docs = field.docs.clone();
  if let Some(ref multiple_of) = field.multiple_of {
    docs.push(format!("/// Validation: Must be a multiple of {multiple_of}"));
  }
  generate_docs(&docs)
}

pub(crate) fn generate_derives_from_slice(derives: &[String]) -> TokenStream {
  if derives.is_empty() {
    return quote! {};
  }
  let derive_idents = derives.iter().filter_map(|d| d.parse::<TokenStream>().ok());
  quote! { #[derive(#(#derive_idents),*)] }
}

pub(crate) fn generate_outer_attrs(attrs: &[String]) -> TokenStream {
  if attrs.is_empty() {
    return quote! {};
  }
  let attr_tokens: Vec<TokenStream> = attrs
    .iter()
    .filter_map(|attr| {
      let trimmed = attr.trim();
      if trimmed.is_empty() {
        return None;
      }
      let source = if trimmed.starts_with("#[") {
        trimmed.to_string()
      } else {
        format!("#[{trimmed}]")
      };
      source.parse::<TokenStream>().ok()
    })
    .collect();
  quote! { #(#attr_tokens)* }
}

pub(crate) fn generate_serde_attrs(attrs: &[String]) -> TokenStream {
  if attrs.is_empty() {
    return quote! {};
  }
  let attr_tokens: Vec<TokenStream> = attrs
    .iter()
    .filter_map(|attr| {
      let tokens: TokenStream = attr.as_str().parse().ok()?;
      Some(quote! { #[serde(#tokens)] })
    })
    .collect();
  quote! { #(#attr_tokens)* }
}

pub(crate) fn generate_validation_attrs(regex_const: Option<&str>, attrs: &[String]) -> TokenStream {
  if attrs.is_empty() && regex_const.is_none() {
    return quote! {};
  }

  let mut combined = attrs.to_owned();

  if let Some(const_name) = regex_const {
    combined.push(format!("regex(path = \"{const_name}\")"));
  }

  let attr_tokens: Vec<TokenStream> = combined.iter().filter_map(|attr| attr.parse().ok()).collect();

  quote! { #[validate(#(#attr_tokens),*)] }
}

pub(crate) fn generate_deprecated_attr(deprecated: bool) -> TokenStream {
  if deprecated {
    quote! { #[deprecated] }
  } else {
    quote! {}
  }
}