openapi_type 0.6.0

OpenAPI type information for Rust structs and enums
Documentation
#![cfg_attr(not(feature = "_all_features"), allow(rustdoc::broken_intra_doc_links))]
//! This crate gives static type information for primitives and commonly used types from
//! the standard library and other commonly used libraries (see [features](#features))
//! when the according feature is enabled. Please refer to the [`Cargo.toml`] for a list
//! of all available feature flags and optional dependencies. Also, it provides a derive
//! macro for structs and enums to gain access to their static type information at
//! runtime.
//!
//! The core of this crate is the [`OpenapiType`] trait. It has three static function,
//! where [`schema`](OpenapiType::schema), which returns an [`OpenapiSchema`], is the one
//! you are probably looking for. It assembles the static type information in a way that
//! is convenient to use for a generated OpenAPI specification, but can also be utilized
//! in other use cases as well.
//!
//! # Custom Types
//!
//! To gain access to the static type information of your custom types at runtime, the
//! easiest way is to use the derive macro:
//!
//! ```rust
//! # use openapi_type::OpenapiType;
//! #[derive(OpenapiType)]
//! struct FooBar {
//! 	foo: Option<String>,
//! 	bar: u64
//! }
//! #
//! # // Test that the OpenAPI schema is the one we display below
//! # let schema = FooBar::schema();
//! # let schema_yaml = serde_saphyr::to_string_with_options(
//! # 	&schema.schema,
//! # 	serde_saphyr::ser::options::SerializerOptions {
//! # 		compact_list_indent: false,
//! # 		..Default::default()
//! # }).unwrap();
//! # pretty_assertions::assert_eq!(include_str!("lib.rs_schema.yml"), schema_yaml);
//! #
//! # // Test that the Path Params is the one we display below
//! # let path_params = FooBar::path_params();
//! # let path_params_yaml = serde_saphyr::to_string_with_options(
//! # 	&path_params.params,
//! # 	serde_saphyr::ser::options::SerializerOptions {
//! # 		compact_list_indent: false,
//! # 		..Default::default()
//! # }).unwrap();
//! # pretty_assertions::assert_eq!(include_str!("lib.rs_path_params.yml"), path_params_yaml);
//! #
//! # // Test that the Query Params is the one we display below
//! # let query_params = FooBar::query_params();
//! # let query_params_yaml = serde_saphyr::to_string_with_options(
//! # 	&query_params.params,
//! # 	serde_saphyr::ser::options::SerializerOptions {
//! # 		compact_list_indent: false,
//! # 		..Default::default()
//! # }).unwrap();
//! # pretty_assertions::assert_eq!(include_str!("lib.rs_query_params.yml"), query_params_yaml);
//! ```
//!
//! # OpenAPI specification
//!
//! Using above type, running `FooBar::schema().into_schema()` yields
//!
//! ```yaml
#![doc = include_str!("lib.rs_schema.yml")]
//! ```
//! 
//! Note, however, that this is not sufficient for more complex types. If one of your
//! structs fields is a type that has a name (that is, `Type::schema().name` is not
//! `None`), above schema will contain a reference to that schema. Therefore, always
//! remember to put the [`dependencies`](OpenapiSchema::dependencies) into the
//! specification alongside the type you are interested in.
//!
//! ## Path Parameters
//!
//! We can also treat the `FooBar` struct from above as a struct containing path
//! parameters, if we construct an [`Operation`](oas3::spec::Operation) for a path like
//! `/foo/{foo}/bar/{bar}`. Running `FooBar::path_params()` yields
//! ```yaml
#![doc = include_str!("lib.rs_path_params.yml")]
//! ```
//! 
//! ## Query Parameters
//!
//! Likewise, we can treat the `FooBar` struct from above as a struct containing query
//! parameters. Running `FooBar::query_params()` yields
//! ```yaml
#![doc = include_str!("lib.rs_query_params.yml")]
//! ```
//! 
//! # Features
//!
//! The following cargo features may be enabled for this crate to unlock additional
//! implementations for larger tuples. For example, the `tuples32` feature would unlock
//! the implementation for tuples with sizes up to 32.
//!
//!  - `tuples16` (enabled by default)
//!  - `tuples32`
//!  - `tuples48`
//!  - `tuples64`
//!
//! The following cargo features may be enabled for this crate to unlock additional
//! implementations for external crates:
//!
//!  - `chrono`: Enable all of the below:
//!    - `chrono04`: Enable implementations for [`chrono` 0.4](chrono04)
//!  - `hashbrown`: Enable all of the below:
//!    - `hashbrown016`: Enable implementations for [`hashbrown` 0.16](hashbrown016)
//!    - `hashbrown017`: Enable implementations for [`hashbrown` 0.17](hashbrown017)
//!  - `jiff`: Enable all of the below:
//!    - `jiff02`: Enable implementations for [`jiff` 0.2](jiff02)
//!  - `linked-hash-map`: Enable all of the below:
//!    - `linked-hash-map05`: Enable implementations for [`linked-hash-map` 0.5](linked_hash_map05)
//!  - `time`: Enable all of the below:
//!    - `time03`: Enable implementations for [`time` 0.3](time03)
//!  - `url`: Enable all of the below:
//!    - `url2`: Enable implementations for [`url` 2.x](url2)
//!  - `uuid`: Enable all of the below:
//!    - `uuid1`: Enable implementations for [`uuid` 1.x](uuid1)
//!
//! Note that support for [`indexmap` 2.x](indexmap) is enabled by default.
//!
//!  [`Cargo.toml`]: https://docs.rs/crate/openapi_type/latest/source/Cargo.toml.orig

mod impls;
mod visitor;

#[doc(inline)]
pub use self::visitor::{
	AlternativesVisitor, ObjectVisitor, OpenapiParams, OpenapiParamsVisitor,
	OpenapiSchema, OpenapiVisitor, PathParamsVisitor, QueryParamsVisitor, TupleVisitor,
	Visitor
};
#[doc(no_inline)]
pub use indexmap::{IndexMap, IndexSet};
#[doc(no_inline)]
pub use oas3::{Map, spec};
#[doc(inline)]
pub use openapi_type_derive::OpenapiType;

/// This trait needs to be implemented by every type that is being used in the OpenAPI Spec. It gives
/// access to the [OpenapiSchema] of this type. It is provided for primitive types, String and the
/// like. For use on your own types, there is a derive macro:
///
/// ```
/// # #[macro_use] extern crate openapi_type_derive;
/// #
/// #[derive(OpenapiType)]
/// struct MyResponse {
/// 	message: String
/// }
/// ```
pub trait OpenapiType {
	/// Visit the type description of this type.
	///
	/// You are unlikely to ever need this method directly. Instead, you are probably
	/// looking for the [`schema()`](Self::schema) method, or one of the other methods
	/// that directly return the desired OpenAPI schema type.
	///
	/// When implementing this method by hand, you must make sure to always call exactly
	/// one visit method.
	fn visit_type<V: Visitor>(visitor: &mut V);

	/// Return the [`OpenapiSchema`] for this type.
	///
	/// This schema is designed to be compatible with the way [`serde_json`] would
	/// (de)serialize this type. If you find a discrepancy between the OpenAPI schema
	/// and the way [`serde_json`] serializes this type, please [open an issue].
	///
	///  [`serde_json`]: https://crates.io/crates/serde_json
	///  [open an issue]: https://codeberg.org/msrd0/openapi_type/issues/new
	fn schema() -> OpenapiSchema {
		let mut visitor = OpenapiVisitor::new();
		Self::visit_type(&mut visitor);
		visitor
			.into_schema()
			.expect("The OpenapiType implementation failed to call the visitor")
	}

	/// Interpret this type as a collection of path parameters, and return the
	/// path params as [`OpenapiParams`] for this type.
	///
	/// # Panics
	///
	/// This will panic if this type is anything but an object, or if the fields of this
	/// object are not either “primitive” types or flattened objects.
	fn path_params() -> OpenapiParams {
		let mut visitor = OpenapiParamsVisitor::new(PathParamsVisitor::new());
		Self::visit_type(&mut visitor);
		visitor.into_inner().into_params()
	}

	/// Interpret this type as a collection of query parameters, and return the
	/// query params as [`OpenapiParams`] for this type.
	///
	/// This schema is desigend to be compatible with the way [`serde_html_form`] would
	/// deserialize this type. If you find a discrepancy between the OpenAPI query params
	/// and the way [`serde_html_form`] deserializes this type, please [open an issue].
	///
	/// # Panics
	///
	/// This will panic if this type is anything but an object, or if the fields of this
	/// object are not either “primitive” types, arrays of “primitive” types, or flattened
	/// objects.
	///
	///  [`serde_html_form`]: https://codeberg.org/jplatte/serde_html_form
	///  [open an issue]: https://codeberg.org/msrd0/openapi_type/issues/new
	fn query_params() -> OpenapiParams {
		let mut visitor = OpenapiParamsVisitor::new(QueryParamsVisitor::new());
		Self::visit_type(&mut visitor);
		visitor.into_inner().into_params()
	}
}

impl<T: ?Sized + OpenapiType> OpenapiType for &T {
	fn visit_type<V: Visitor>(visitor: &mut V) {
		T::visit_type(visitor)
	}
}