use crate::body::Body;
use crate::request::RequestExt;
use crate::Error;
use anyhow::anyhow;
use http::{Request, Response};
use lazy_static::lazy_static;
use regex::Regex;
use std::str::FromStr;
pub(crate) mod error_messages {
pub(crate) const EMPTY_BACKEND_ERROR_MSG: &str = "an empty string is not a valid backend";
pub(crate) const INVALID_CHARS_ERROR_MSG: &str =
"valid backend strings only contain ASCII characters";
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Backend(String);
impl Backend {
pub fn new(s: &str) -> Result<Self, <Self as FromStr>::Err> {
s.parse()
}
pub fn send(&self, req: Request<Body>) -> Result<Response<Body>, Error> {
req.send(self.0.as_str())
}
}
impl FromStr for Backend {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
validate_backend(s)?;
Ok(Self(s.into()))
}
}
pub fn validate_backend(backend: &str) -> Result<(), <Backend as FromStr>::Err> {
lazy_static! {
static ref VALID_BACKEND_RE: Regex = Regex::new("^[a-zA-Z][a-zA-Z0-9_]*$").unwrap();
}
match backend {
b if b.is_empty() => Err(anyhow!(error_messages::EMPTY_BACKEND_ERROR_MSG)),
b if !VALID_BACKEND_RE.is_match(b) => Err(anyhow!(error_messages::INVALID_CHARS_ERROR_MSG)),
_ => Ok(()),
}
}
#[cfg(test)]
mod validate_backend_tests {
use crate::backend::{error_messages::*, *};
#[test]
fn valid_backend_name_is_accepted() -> Result<(), Error> {
validate_backend("valid_backend_1")?;
Ok(())
}
#[test]
fn empty_str_is_not_accepted() -> Result<(), Error> {
let invalid_backend = "";
match validate_backend(invalid_backend).map_err(|e| e.to_string()) {
Err(msg) if msg == EMPTY_BACKEND_ERROR_MSG => Ok(()),
x => panic!("unexpected result: {:?}", x),
}
}
#[test]
fn unicode_is_not_accepted() -> Result<(), Error> {
let invalid_backend = "♓";
match validate_backend(invalid_backend).map_err(|e| e.to_string()) {
Err(msg) if msg == INVALID_CHARS_ERROR_MSG => Ok(()),
x => panic!("unexpected result: {:?}", x),
}
}
#[test]
fn unprintable_characters_are_not_accepted() -> Result<(), Error> {
let invalid_backend = "\n";
match validate_backend(invalid_backend).map_err(|e| e.to_string()) {
Err(msg) if msg == INVALID_CHARS_ERROR_MSG => Ok(()),
x => panic!("unexpected result: {:?}", x),
}
}
#[test]
fn dashes_are_not_accepted() -> Result<(), Error> {
let invalid_backend = "kebab-case";
match validate_backend(invalid_backend).map_err(|e| e.to_string()) {
Err(msg) if msg == INVALID_CHARS_ERROR_MSG => Ok(()),
x => panic!("unexpected result: {:?}", x),
}
}
#[test]
fn leading_integers_are_not_accepted() -> Result<(), Error> {
let invalid_backend = "1_invalid_backend";
match validate_backend(invalid_backend).map_err(|e| e.to_string()) {
Err(msg) if msg == INVALID_CHARS_ERROR_MSG => Ok(()),
x => panic!("unexpected result: {:?}", x),
}
}
}