use super::resolver::{Resolvable, Resolver};
use super::{ResolvedSchema, ResolvedSchemaData, ResolvedSchemaKind, ResolvedType};
use crate::{Component, ResolveError};
use openapiv3::{
BooleanType, Callback, Example, Header, Link, Parameter, PathItem, RequestBody, Response,
Schema, SecurityScheme,
};
use std::collections::HashMap;
use std::sync::{Arc, Weak};
pub(super) trait Cached: Resolvable + Component {
fn cache(caches: &mut Caches) -> &mut Cache<Self::Resolved>;
fn resolve_shared(
cx: &mut Resolver<'_>,
name: &str,
item: &Self,
) -> Result<Arc<Self::Resolved>, ResolveError> {
Self::cache(&mut cx.caches)
.0
.insert(name.to_owned(), Slot::InProgress(Weak::new()));
item.resolve_inline(cx).map(Arc::new)
}
}
pub(super) struct Cache<R>(pub(super) HashMap<String, Slot<R>>);
impl<R> Default for Cache<R> {
fn default() -> Self {
Self(HashMap::new())
}
}
pub(super) enum Slot<R> {
InProgress(Weak<R>),
Done(Arc<R>),
}
impl<R> Clone for Slot<R> {
fn clone(&self) -> Self {
match self {
Self::InProgress(weak) => Self::InProgress(Weak::clone(weak)),
Self::Done(arc) => Self::Done(Arc::clone(arc)),
}
}
}
#[derive(Default)]
pub(super) struct Caches {
callbacks: Cache<<Callback as Resolvable>::Resolved>,
examples: Cache<Example>,
headers: Cache<<Header as Resolvable>::Resolved>,
links: Cache<Link>,
parameters: Cache<<Parameter as Resolvable>::Resolved>,
request_bodies: Cache<<RequestBody as Resolvable>::Resolved>,
responses: Cache<<Response as Resolvable>::Resolved>,
schemas: Cache<ResolvedSchema>,
security_schemes: Cache<SecurityScheme>,
paths: Cache<<PathItem as Resolvable>::Resolved>,
}
macro_rules! cached {
($( $field:ident : $ty:ty ),+ $(,)?) => {
$(
impl Cached for $ty {
fn cache(caches: &mut Caches) -> &mut Cache<Self::Resolved> {
&mut caches.$field
}
}
)+
};
}
cached! {
callbacks: Callback,
examples: Example,
headers: Header,
links: Link,
parameters: Parameter,
request_bodies: RequestBody,
responses: Response,
security_schemes: SecurityScheme,
paths: PathItem,
}
impl Cached for Schema {
fn cache(caches: &mut Caches) -> &mut Cache<Self::Resolved> {
&mut caches.schemas
}
fn resolve_shared(
cx: &mut Resolver<'_>,
name: &str,
item: &Self,
) -> Result<Arc<ResolvedSchema>, ResolveError> {
let mut failure = None;
let resolved = Arc::new_cyclic(|weak| {
Self::cache(&mut cx.caches)
.0
.insert(name.to_owned(), Slot::InProgress(Weak::clone(weak)));
match item.resolve_inline(cx) {
Ok(resolved) => resolved,
Err(error) => {
failure = Some(error);
ResolvedSchema {
schema_data: ResolvedSchemaData::default(),
schema_kind: ResolvedSchemaKind::Type(ResolvedType::Boolean(
BooleanType::default(),
)),
}
}
}
});
match failure {
Some(error) => Err(error),
None => Ok(resolved),
}
}
}