use super::super::semantics::SourceBinding;
use super::super::semantics::{HttpStreamKind, has_annotation, has_optional_annotation};
use super::super::validate::{HttpParamDirection, default_param_source, param_direction};
use super::super::{HttpMethod, HttpParam, HttpParamKind, HttpRoute, RestHirResult};
use crate::hir;
use std::collections::HashSet;
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct ParameterSources {
default_source: HttpParamKind,
pub(super) route_path_names: Vec<HashSet<String>>,
route_query_names: Vec<HashSet<String>>,
}
impl ParameterSources {
pub(crate) fn new(
method: HttpMethod,
stream_kind: Option<HttpStreamKind>,
routes: &[HttpRoute],
) -> Self {
Self {
default_source: if matches!(stream_kind, Some(HttpStreamKind::Bidi)) {
HttpParamKind::Body
} else {
default_param_source(method)
},
route_path_names: routes
.iter()
.map(|route| route.path_params.iter().cloned().collect())
.collect(),
route_query_names: routes
.iter()
.map(|route| route.query_params.iter().cloned().collect())
.collect(),
}
}
pub(crate) fn request(&self, param: &hir::ParamDcl) -> RestHirResult<Option<HttpParam>> {
if matches!(
param_direction(param.attr.as_ref()),
HttpParamDirection::Out
) {
return Ok(None);
}
self.project(param).map(Some)
}
pub(crate) fn response(&self, param: &hir::ParamDcl) -> RestHirResult<Option<HttpParam>> {
if matches!(param_direction(param.attr.as_ref()), HttpParamDirection::In) {
return Ok(None);
}
self.project(param).map(Some)
}
pub(crate) fn project(&self, param: &hir::ParamDcl) -> RestHirResult<HttpParam> {
let binding = SourceBinding::read(¶m.annotations, ¶m.declarator.0)?;
let wire_name = binding.as_ref().map_or_else(
|| hir::field_rename(¶m.annotations).unwrap_or_else(|| param.declarator.0.clone()),
|binding| binding.wire_name(¶m.declarator.0, ¶m.annotations, &[]),
);
let kind = match param_direction(param.attr.as_ref()) {
HttpParamDirection::Out | HttpParamDirection::InOut => binding
.map(|value| value.source)
.unwrap_or(HttpParamKind::Body),
HttpParamDirection::In => binding.map(|value| value.source).unwrap_or_else(|| {
if self
.route_path_names
.iter()
.all(|set| set.contains(¶m.declarator.0))
{
HttpParamKind::Path
} else if self
.route_query_names
.iter()
.any(|set| set.contains(¶m.declarator.0))
{
HttpParamKind::Query
} else {
self.default_source
}
}),
};
Ok(HttpParam {
name: param.declarator.0.clone(),
wire_name,
ty: param.ty.clone(),
kind,
optional: has_optional_annotation(¶m.annotations),
flatten: has_annotation(¶m.annotations, "flatten"),
})
}
}