From b16f72e797caedd02661a9f46c043976069ed938 Mon Sep 17 00:00:00 2001 From: skytz Date: Mon, 25 Nov 2024 04:42:06 +0200 Subject: [PATCH] Implemented nullable fields for openapi spec generation (#865) * Implemented nullable functionality for documentation generation * Fixed case where the field is all_off and nullable is not at the top level, but one of the variants --- poem-openapi-derive/src/object.rs | 6 ++++++ poem-openapi/src/registry/mod.rs | 9 +++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/poem-openapi-derive/src/object.rs b/poem-openapi-derive/src/object.rs index 20f54236fb..b411ff644e 100644 --- a/poem-openapi-derive/src/object.rs +++ b/poem-openapi-derive/src/object.rs @@ -31,6 +31,8 @@ struct ObjectField { #[darling(default)] write_only: bool, #[darling(default)] + nullable: bool, + #[darling(default)] read_only: bool, #[darling(default)] validator: Option, @@ -69,6 +71,8 @@ struct ObjectArgs { #[darling(default)] write_only_all: bool, #[darling(default)] + nullable_all: bool, + #[darling(default)] deny_unknown_fields: bool, #[darling(default)] example: bool, @@ -115,6 +119,7 @@ pub(crate) fn generate(args: DeriveInput) -> GeneratorResult { let field_ty = &field.ty; let read_only = args.read_only_all || field.read_only; let write_only = args.write_only_all || field.write_only; + let nullable = args.nullable_all || field.nullable; let skip_serializing_if_is_none = field.skip_serializing_if_is_none || args.skip_serializing_if_is_none; let skip_serializing_if_is_empty = @@ -287,6 +292,7 @@ pub(crate) fn generate(args: DeriveInput) -> GeneratorResult { let patch_schema = { let mut schema = #crate_name::registry::MetaSchema::ANY; schema.default = #field_meta_default; + schema.nullable = #nullable; schema.read_only = #read_only; schema.write_only = #write_only; diff --git a/poem-openapi/src/registry/mod.rs b/poem-openapi/src/registry/mod.rs index 01c7e0f224..19be678791 100644 --- a/poem-openapi/src/registry/mod.rs +++ b/poem-openapi/src/registry/mod.rs @@ -75,6 +75,8 @@ pub struct MetaSchema { pub enum_items: Vec, #[serde(skip_serializing_if = "is_false")] pub deprecated: bool, + #[serde(skip_serializing_if = "is_false")] + pub nullable: bool, #[serde(skip_serializing_if = "Vec::is_empty")] pub any_of: Vec, #[serde(skip_serializing_if = "Vec::is_empty")] @@ -150,6 +152,7 @@ impl MetaSchema { discriminator: None, read_only: false, write_only: false, + nullable: false, example: None, multiple_of: None, maximum: None, @@ -189,6 +192,7 @@ impl MetaSchema { default, read_only, write_only, + nullable, title, description, external_docs, @@ -213,6 +217,7 @@ impl MetaSchema { ) -> Self { self.read_only |= read_only; self.write_only |= write_only; + self.nullable |= nullable; macro_rules! merge_optional { ($($name:ident),*) => { @@ -333,9 +338,9 @@ impl MetaSchemaRef { MetaSchemaRef::Inline(Box::new(MetaSchema { all_of: vec![ MetaSchemaRef::Reference(name), - MetaSchemaRef::Inline(Box::new(other)), + MetaSchemaRef::Inline(Box::new(other.clone())), ], - ..MetaSchema::ANY + ..other })) } }