use std::str::FromStr;
use std::vec;
use crate::extract::RenameRule;
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
#[non_exhaustive]
pub enum SourceFrom {
Param,
Query,
Header,
#[cfg(feature = "cookie")]
Cookie,
Body,
}
impl FromStr for SourceFrom {
type Err = crate::Error;
fn from_str(input: &str) -> Result<Self, Self::Err> {
match input {
"param" => Ok(Self::Param),
"query" => Ok(Self::Query),
"header" => Ok(Self::Header),
#[cfg(feature = "cookie")]
"cookie" => Ok(Self::Cookie),
"body" => Ok(Self::Body),
_ => Err(crate::Error::Other(
format!("invalid source from `{input}`").into(),
)),
}
}
}
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
#[non_exhaustive]
pub enum SourceParser {
MultiMap,
Json,
Smart,
}
impl FromStr for SourceParser {
type Err = crate::Error;
fn from_str(input: &str) -> Result<Self, Self::Err> {
match input {
"multimap" => Ok(Self::MultiMap),
"json" => Ok(Self::Json),
"smart" => Ok(Self::Smart),
_ => Err(crate::Error::Other("invalid source format".into())),
}
}
}
#[derive(Default, Clone, Debug)]
#[non_exhaustive]
pub struct Metadata {
pub name: &'static str,
pub default_sources: Vec<Source>,
pub fields: Vec<Field>,
pub rename_all: Option<RenameRule>,
pub serde_rename_all: Option<RenameRule>,
}
impl Metadata {
pub const fn new(name: &'static str) -> Self {
Self {
name,
default_sources: vec![],
fields: vec![],
rename_all: None,
serde_rename_all: None,
}
}
pub fn default_sources(mut self, default_sources: Vec<Source>) -> Self {
self.default_sources = default_sources;
self
}
pub fn fields(mut self, fields: Vec<Field>) -> Self {
self.fields = fields;
self
}
pub fn add_default_source(mut self, source: Source) -> Self {
self.default_sources.push(source);
self
}
pub fn add_field(mut self, field: Field) -> Self {
self.fields.push(field);
self
}
pub fn rename_all(mut self, rename_all: impl Into<Option<RenameRule>>) -> Self {
self.rename_all = rename_all.into();
self
}
pub fn serde_rename_all(mut self, serde_rename_all: impl Into<Option<RenameRule>>) -> Self {
self.serde_rename_all = serde_rename_all.into();
self
}
pub(crate) fn has_body_required(&self) -> bool {
if self
.default_sources
.iter()
.any(|s| s.from == SourceFrom::Body)
{
return true;
}
self.fields.iter().any(|f| f.has_body_required())
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct Field {
pub decl_name: &'static str,
pub flatten: bool,
pub sources: Vec<Source>,
pub aliases: Vec<&'static str>,
pub rename: Option<&'static str>,
pub serde_rename: Option<&'static str>,
pub metadata: Option<&'static Metadata>,
}
impl Field {
pub fn new(decl_name: &'static str) -> Self {
Self::with_sources(decl_name, vec![])
}
pub fn with_sources(decl_name: &'static str, sources: Vec<Source>) -> Self {
Self {
decl_name,
flatten: false,
sources,
aliases: vec![],
rename: None,
serde_rename: None,
metadata: None,
}
}
pub fn flatten(mut self, flatten: bool) -> Self {
self.flatten = flatten;
self
}
pub fn metadata(mut self, metadata: &'static Metadata) -> Self {
self.metadata = Some(metadata);
self
}
pub fn add_source(mut self, source: Source) -> Self {
self.sources.push(source);
self
}
pub fn aliases(mut self, aliases: Vec<&'static str>) -> Self {
self.aliases = aliases;
self
}
pub fn add_alias(mut self, alias: &'static str) -> Self {
self.aliases.push(alias);
self
}
pub fn rename(mut self, rename: &'static str) -> Self {
self.rename = Some(rename);
self
}
pub fn serde_rename(mut self, serde_rename: &'static str) -> Self {
self.serde_rename = Some(serde_rename);
self
}
pub(crate) fn has_body_required(&self) -> bool {
self.sources.iter().any(|s| s.from == SourceFrom::Body)
}
}
#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
pub struct Source {
pub from: SourceFrom,
pub parser: SourceParser,
}
impl Source {
pub fn new(from: SourceFrom, parser: SourceParser) -> Self {
Self { from, parser }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_source_from() {
for (key, value) in [
("param", SourceFrom::Param),
("query", SourceFrom::Query),
("header", SourceFrom::Header),
#[cfg(feature = "cookie")]
("cookie", SourceFrom::Cookie),
("body", SourceFrom::Body),
] {
assert_eq!(key.parse::<SourceFrom>().unwrap(), value);
}
assert!("abcd".parse::<SourceFrom>().is_err());
}
#[test]
fn test_parse_source_format() {
for (key, value) in [
("multimap", SourceParser::MultiMap),
("json", SourceParser::Json),
] {
assert_eq!(key.parse::<SourceParser>().unwrap(), value);
}
assert!("abcd".parse::<SourceParser>().is_err());
}
}