oas3-gen 0.16.0

A rust type generator for OpenAPI v3.1.x specification.
use std::collections::BTreeMap;

use crate::generator::{
  ast::{
    FieldDef, PathSegment, QueryParameter, ResponseVariant, RustType, StructDef, StructKind, StructMethod,
    StructMethodKind, TypeRef,
  },
  codegen::{self, Visibility, structs},
};

fn base_struct(kind: StructKind) -> StructDef {
  StructDef {
    name: "Sample".to_string(),
    docs: vec!["/// Sample struct".to_string()],
    fields: vec![FieldDef {
      name: "field".to_string(),
      rust_type: TypeRef::new("String"),
      serde_attrs: vec![],
      extra_attrs: vec![],
      validation_attrs: vec!["length(min = 1)".to_string()],
      regex_validation: None,
      default_value: None,
      ..Default::default()
    }],
    derives: vec!["Debug".to_string(), "Clone".to_string()],
    serde_attrs: vec![],
    outer_attrs: vec![],
    methods: vec![],
    kind,
  }
}

#[test]
fn generates_struct_with_supplied_derives() {
  let def = StructDef {
    derives: vec!["Debug".into(), "Clone".into(), "Serialize".into()],
    ..base_struct(StructKind::Schema)
  };
  let tokens = structs::generate_struct(&def, &BTreeMap::new(), Visibility::Public);
  let code = tokens.to_string();
  assert!(code.contains("derive"));
  assert!(code.contains("Debug"));
  assert!(code.contains("Clone"));
  assert!(code.contains("Serialize"));
  assert!(code.contains("pub struct Sample"));
}

#[test]
fn emits_validation_attributes_when_present() {
  let def = base_struct(StructKind::Schema);
  let tokens = structs::generate_struct(&def, &BTreeMap::new(), Visibility::Public);
  let code = tokens.to_string();
  assert!(code.contains("# [validate"));
}

#[test]
fn skips_validation_attributes_when_absent() {
  let mut def = base_struct(StructKind::Schema);
  def.fields[0].validation_attrs.clear();
  let tokens = structs::generate_struct(&def, &BTreeMap::new(), Visibility::Public);
  let code = tokens.to_string();
  assert!(!code.contains("validate"));
}

#[test]
fn renders_struct_methods() {
  let mut def = base_struct(StructKind::OperationRequest);
  def.methods.push(StructMethod {
    name: "render_path".to_string(),
    docs: vec!["/// Render path".to_string()],
    kind: StructMethodKind::RenderPath {
      segments: vec![
        PathSegment::Literal("/users/".to_string()),
        PathSegment::Parameter {
          field: "field".to_string(),
        },
      ],
      query_params: vec![QueryParameter {
        field: "field".to_string(),
        encoded_name: "field".to_string(),
        explode: false,
        optional: false,
        is_array: false,
      }],
    },
    attrs: vec![],
  });
  let tokens = structs::generate_struct(&def, &BTreeMap::new(), Visibility::Public);
  let code = tokens.to_string();
  assert!(code.contains("impl Sample"));
  assert!(code.contains("fn render_path"));
}

#[test]
fn renders_response_parser_method() {
  let mut def = base_struct(StructKind::OperationRequest);
  def.methods.push(StructMethod {
    name: "parse_response".to_string(),
    docs: vec!["/// Parse response".to_string()],
    kind: StructMethodKind::ParseResponse {
      response_enum: "ResponseEnum".to_string(),
      variants: vec![ResponseVariant {
        status_code: "200".to_string(),
        variant_name: "Ok".to_string(),
        description: None,
        schema_type: None,
      }],
    },
    attrs: vec![],
  });
  let tokens = structs::generate_struct(&def, &BTreeMap::new(), Visibility::Public);
  let code = tokens.to_string();
  assert!(code.contains("fn parse_response"));
  assert!(code.contains("ResponseEnum"));
}

#[test]
fn codegen_emits_only_deserialize_when_needed() {
  let def = StructDef {
    derives: vec!["Debug".into(), "Deserialize".into()],
    ..base_struct(StructKind::Schema)
  };
  let errors = std::collections::HashSet::new();
  let tokens = codegen::generate(&[RustType::Struct(def)], &errors, Visibility::Public);
  let code = tokens.to_string();
  assert!(code.contains("use serde :: Deserialize"));
  assert!(!code.contains("Serialize"));
}

#[test]
fn codegen_emits_both_serde_traits_when_required() {
  let def = StructDef {
    derives: vec!["Debug".into(), "Serialize".into(), "Deserialize".into()],
    ..base_struct(StructKind::Schema)
  };
  let errors = std::collections::HashSet::new();
  let tokens = codegen::generate(&[RustType::Struct(def)], &errors, Visibility::Public);
  let code = tokens.to_string();
  assert!(code.contains("use serde :: { Deserialize , Serialize }"));
}