use actix_service::boxed::BoxServiceFactory;
use actix_web::{
Error,
body::BoxBody,
dev::{ServiceRequest, ServiceResponse},
};
use serde::{Deserialize, de};
pub(crate) type BoxedServiceFactory =
BoxServiceFactory<(), ServiceRequest, ServiceResponse<BoxBody>, Error, ()>;
#[inline]
pub(crate) fn sanitize_path(mut p: String) -> String {
if p.is_empty() {
return p;
}
if p.len() > 1 && !p.starts_with('/') {
p.insert(0, '/');
}
if p.ends_with('/') {
p.pop();
}
p.replace("_", "-")
.replace("..", "")
.replace("\\", "/")
.replace("//", "/")
.to_lowercase()
}
pub(crate) fn deserialize_one_or_many_strings<'de, D>(
deserializer: D,
) -> Result<Vec<String>, D::Error>
where
D: serde::Deserializer<'de>,
{
struct StringOrVecVisitor;
impl<'de> de::Visitor<'de> for StringOrVecVisitor {
type Value = Vec<String>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a string or a list of strings")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(vec![value.to_owned()])
}
fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
Vec::<String>::deserialize(de::value::SeqAccessDeserializer::new(seq))
}
}
deserializer.deserialize_any(StringOrVecVisitor)
}
#[cfg(test)]
mod tests {
use serde::Deserialize;
#[derive(Debug, Deserialize, PartialEq)]
struct TestStruct {
#[serde(deserialize_with = "super::deserialize_one_or_many_strings")]
field: Vec<String>,
}
#[test]
fn test_string_or_string_vector_deserializer() {
let input_str: TestStruct = serde_json::from_str(r#"{"field": "foo"}"#).unwrap();
let input_seq: TestStruct = serde_json::from_str(r#"{"field": ["foo"]}"#).unwrap();
let input_check = TestStruct {
field: vec!["foo".to_string()],
};
assert_eq!(input_str, input_check);
assert_eq!(input_seq, input_check);
}
}