poem_openapi/types/
mod.rsmod any;
mod base64_type;
mod binary;
mod error;
mod external;
mod maybe_undefined;
mod string_types;
pub mod multipart;
use std::{borrow::Cow, future::Future, sync::Arc};
pub use any::Any;
pub use base64_type::Base64;
pub use binary::Binary;
pub use error::{ParseError, ParseResult};
pub use maybe_undefined::MaybeUndefined;
use poem::{http::HeaderValue, web::Field as PoemField};
use serde_json::Value;
#[cfg(feature = "email")]
pub use string_types::Email;
#[cfg(feature = "hostname")]
pub use string_types::Hostname;
pub use string_types::Password;
use crate::registry::{MetaSchemaRef, Registry};
pub trait Type: Send + Sync {
const IS_REQUIRED: bool;
type RawValueType;
type RawElementValueType;
fn name() -> Cow<'static, str>;
fn schema_ref() -> MetaSchemaRef;
#[allow(unused_variables)]
fn register(registry: &mut Registry) {}
fn as_raw_value(&self) -> Option<&Self::RawValueType>;
fn raw_element_iter<'a>(
&'a self,
) -> Box<dyn Iterator<Item = &'a Self::RawElementValueType> + 'a>;
#[inline]
fn is_empty(&self) -> bool {
false
}
#[inline]
fn is_none(&self) -> bool {
false
}
}
pub trait IsObjectType: Type {}
pub trait ParseFromJSON: Sized + Type {
fn parse_from_json(value: Option<Value>) -> ParseResult<Self>;
fn parse_from_json_string(s: &str) -> ParseResult<Self> {
let value = serde_json::from_str(s).map_err(|err| ParseError::custom(err.to_string()))?;
Self::parse_from_json(value)
}
}
pub trait ParseFromXML: Sized + Type {
fn parse_from_xml(value: Option<Value>) -> ParseResult<Self>;
fn parse_from_xml_string(s: &str) -> ParseResult<Self> {
let value =
quick_xml::de::from_str(s).map_err(|err| ParseError::custom(err.to_string()))?;
Self::parse_from_xml(value)
}
}
pub trait ParseFromYAML: Sized + Type {
fn parse_from_yaml(value: Option<Value>) -> ParseResult<Self>;
fn parse_from_yaml_string(s: &str) -> ParseResult<Self> {
let value = serde_yaml::from_str(s).map_err(|err| ParseError::custom(err.to_string()))?;
Self::parse_from_yaml(value)
}
}
pub trait ParseFromParameter: Sized + Type {
fn parse_from_parameter(value: &str) -> ParseResult<Self>;
fn parse_from_parameters<I: IntoIterator<Item = A>, A: AsRef<str>>(
iter: I,
) -> ParseResult<Self> {
let mut iter = iter.into_iter();
match iter.next().as_ref().map(|item| item.as_ref()) {
Some(value) => Self::parse_from_parameter(value),
None => Err(ParseError::expected_input()),
}
}
}
pub trait ParseFromMultipartField: Sized + Type {
fn parse_from_multipart(
field: Option<PoemField>,
) -> impl Future<Output = ParseResult<Self>> + Send;
fn parse_from_repeated_field(
self,
_field: PoemField,
) -> impl Future<Output = ParseResult<Self>> + Send {
async move { Err(ParseError::<Self>::custom("repeated field")) }
}
}
pub trait ToJSON: Type {
fn to_json(&self) -> Option<Value>;
fn to_json_string(&self) -> String {
serde_json::to_string(&self.to_json()).unwrap_or_default()
}
}
pub trait ToXML: Type {
fn to_xml(&self) -> Option<Value>;
fn to_xml_string(&self) -> String {
quick_xml::se::to_string(&self.to_xml()).unwrap_or_default()
}
}
pub trait ToYAML: Type {
fn to_yaml(&self) -> Option<Value>;
fn to_yaml_string(&self) -> String {
serde_yaml::to_string(&self.to_yaml()).unwrap_or_default()
}
}
pub trait ToHeader: Type {
fn to_header(&self) -> Option<HeaderValue>;
}
impl<T: Type> Type for &T {
const IS_REQUIRED: bool = T::IS_REQUIRED;
type RawValueType = T::RawValueType;
type RawElementValueType = T::RawElementValueType;
fn name() -> Cow<'static, str> {
T::name()
}
fn schema_ref() -> MetaSchemaRef {
T::schema_ref()
}
fn register(registry: &mut Registry) {
T::register(registry);
}
fn as_raw_value(&self) -> Option<&Self::RawValueType> {
(*self).as_raw_value()
}
fn raw_element_iter<'a>(
&'a self,
) -> Box<dyn Iterator<Item = &'a Self::RawElementValueType> + 'a> {
(*self).raw_element_iter()
}
}
impl<T: ToJSON> ToJSON for &T {
fn to_json(&self) -> Option<Value> {
T::to_json(self)
}
}
impl<T: ToXML> ToXML for &T {
fn to_xml(&self) -> Option<Value> {
T::to_xml(self)
}
}
impl<T: ToYAML> ToYAML for &T {
fn to_yaml(&self) -> Option<Value> {
T::to_yaml(self)
}
}
impl<T: ToHeader> ToHeader for &T {
fn to_header(&self) -> Option<HeaderValue> {
T::to_header(self)
}
}
impl<T: Type> Type for Arc<T> {
const IS_REQUIRED: bool = T::IS_REQUIRED;
type RawValueType = T::RawValueType;
type RawElementValueType = T::RawElementValueType;
fn name() -> Cow<'static, str> {
T::name()
}
fn schema_ref() -> MetaSchemaRef {
T::schema_ref()
}
fn register(registry: &mut Registry) {
T::register(registry);
}
fn as_raw_value(&self) -> Option<&Self::RawValueType> {
self.as_ref().as_raw_value()
}
fn raw_element_iter<'a>(
&'a self,
) -> Box<dyn Iterator<Item = &'a Self::RawElementValueType> + 'a> {
self.as_ref().raw_element_iter()
}
}
impl<T: ParseFromJSON> ParseFromJSON for Arc<T> {
fn parse_from_json(value: Option<Value>) -> ParseResult<Self> {
T::parse_from_json(value)
.map_err(ParseError::propagate)
.map(Arc::new)
}
}
impl<T: ParseFromXML> ParseFromXML for Arc<T> {
fn parse_from_xml(value: Option<Value>) -> ParseResult<Self> {
T::parse_from_xml(value)
.map_err(ParseError::propagate)
.map(Arc::new)
}
}
impl<T: ParseFromParameter> ParseFromParameter for Arc<T> {
fn parse_from_parameter(_value: &str) -> ParseResult<Self> {
unreachable!()
}
fn parse_from_parameters<I: IntoIterator<Item = A>, A: AsRef<str>>(
iter: I,
) -> ParseResult<Self> {
T::parse_from_parameters(iter)
.map_err(ParseError::propagate)
.map(Arc::new)
}
}
impl<T: ToJSON> ToJSON for Arc<T> {
fn to_json(&self) -> Option<Value> {
self.as_ref().to_json()
}
}
impl<T: ToXML> ToXML for Arc<T> {
fn to_xml(&self) -> Option<Value> {
self.as_ref().to_xml()
}
}
impl<T: ToHeader> ToHeader for Arc<T> {
fn to_header(&self) -> Option<HeaderValue> {
self.as_ref().to_header()
}
}
impl<T: Type> Type for Box<T> {
const IS_REQUIRED: bool = T::IS_REQUIRED;
type RawValueType = T::RawValueType;
type RawElementValueType = T::RawElementValueType;
fn name() -> Cow<'static, str> {
T::name()
}
fn schema_ref() -> MetaSchemaRef {
T::schema_ref()
}
fn register(registry: &mut Registry) {
T::register(registry);
}
fn as_raw_value(&self) -> Option<&Self::RawValueType> {
self.as_ref().as_raw_value()
}
fn raw_element_iter<'a>(
&'a self,
) -> Box<dyn Iterator<Item = &'a Self::RawElementValueType> + 'a> {
self.as_ref().raw_element_iter()
}
}
impl<T: ParseFromJSON> ParseFromJSON for Box<T> {
fn parse_from_json(value: Option<Value>) -> ParseResult<Self> {
T::parse_from_json(value)
.map_err(ParseError::propagate)
.map(Box::new)
}
}
impl<T: ParseFromXML> ParseFromXML for Box<T> {
fn parse_from_xml(value: Option<Value>) -> ParseResult<Self> {
T::parse_from_xml(value)
.map_err(ParseError::propagate)
.map(Box::new)
}
}
impl<T: ParseFromParameter> ParseFromParameter for Box<T> {
fn parse_from_parameter(_value: &str) -> ParseResult<Self> {
unreachable!()
}
fn parse_from_parameters<I: IntoIterator<Item = A>, A: AsRef<str>>(
iter: I,
) -> ParseResult<Self> {
T::parse_from_parameters(iter)
.map_err(ParseError::propagate)
.map(Box::new)
}
}
impl<T: ParseFromMultipartField> ParseFromMultipartField for Box<T> {
async fn parse_from_multipart(field: Option<PoemField>) -> ParseResult<Self> {
T::parse_from_multipart(field)
.await
.map_err(ParseError::propagate)
.map(Box::new)
}
async fn parse_from_repeated_field(self, field: PoemField) -> ParseResult<Self> {
T::parse_from_repeated_field(*self, field)
.await
.map_err(ParseError::propagate)
.map(Box::new)
}
}
impl<T: ToJSON> ToJSON for Box<T> {
fn to_json(&self) -> Option<Value> {
self.as_ref().to_json()
}
}
impl<T: ToXML> ToXML for Box<T> {
fn to_xml(&self) -> Option<Value> {
self.as_ref().to_xml()
}
}
impl<T: ToYAML> ToYAML for Box<T> {
fn to_yaml(&self) -> Option<Value> {
self.as_ref().to_yaml()
}
}
impl<T: ToHeader> ToHeader for Box<T> {
fn to_header(&self) -> Option<HeaderValue> {
self.as_ref().to_header()
}
}
pub trait Example {
fn example() -> Self;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[allow(clippy::assertions_on_constants)]
fn arc_type() {
assert!(Arc::<i32>::IS_REQUIRED);
assert_eq!(Arc::<i32>::name(), "integer_int32");
assert_eq!(Arc::new(100).as_raw_value(), Some(&100));
let value: Arc<i32> =
ParseFromJSON::parse_from_json(Some(Value::Number(100.into()))).unwrap();
assert_eq!(value, Arc::new(100));
let value: Arc<i32> =
ParseFromXML::parse_from_xml(Some(Value::Number(100.into()))).unwrap();
assert_eq!(value, Arc::new(100));
let value: Arc<i32> =
ParseFromParameter::parse_from_parameters(std::iter::once("100")).unwrap();
assert_eq!(value, Arc::new(100));
assert_eq!(
ToJSON::to_json(&Arc::new(100)),
Some(Value::Number(100.into()))
);
assert_eq!(
ToXML::to_xml(&Arc::new(100)),
Some(Value::Number(100.into()))
);
}
#[test]
#[allow(clippy::assertions_on_constants)]
#[allow(unused_allocation)]
fn box_type() {
assert!(Box::<i32>::IS_REQUIRED);
assert_eq!(Box::<i32>::name(), "integer_int32");
assert_eq!(Box::new(100).as_raw_value(), Some(&100));
let value: Box<i32> =
ParseFromJSON::parse_from_json(Some(Value::Number(100.into()))).unwrap();
assert_eq!(value, Box::new(100));
let value: Box<i32> =
ParseFromJSON::parse_from_json(Some(Value::Number(100.into()))).unwrap();
assert_eq!(value, Box::new(100));
let value: Box<i32> =
ParseFromXML::parse_from_xml(Some(Value::Number(100.into()))).unwrap();
assert_eq!(value, Box::new(100));
let value: Box<i32> =
ParseFromParameter::parse_from_parameters(std::iter::once("100")).unwrap();
assert_eq!(value, Box::new(100));
assert_eq!(
ToJSON::to_json(&Box::new(100)),
Some(Value::Number(100.into()))
);
assert_eq!(
ToXML::to_xml(&Box::new(100)),
Some(Value::Number(100.into()))
);
}
}