use console::style;
use serde::Deserialize;
use strategy::Strategy;
use util::canonicalized_fallback_respond_dir_to_print;
use std::path::Path;
pub mod strategy;
mod util;
use super::constant::{PRINT_DELIMITER, SERVICE_DEFAULT_FALLBACK_RESPOND_DIR};
use crate::core::server::{
middleware::middleware_handler::MiddlewareHandler, parsed_request::ParsedRequest,
routing::rule_set::RuleSet, types::BoxBody,
};
#[derive(Clone, Deserialize)]
pub struct ServiceConfig {
pub strategy: Option<Strategy>,
#[serde(rename = "rule_sets")]
pub rule_sets_file_paths: Option<Vec<String>>,
#[serde(skip)]
pub rule_sets: Vec<RuleSet>,
#[serde(rename = "middlewares")]
pub middlewares_file_paths: Option<Vec<String>>,
#[serde(skip)]
pub middlewares: Vec<MiddlewareHandler>,
pub fallback_respond_dir: String,
}
impl ServiceConfig {
pub async fn middleware_response(
&self,
parsed_request: &ParsedRequest,
) -> Option<Result<hyper::Response<BoxBody>, hyper::http::Error>> {
for middleware_handler in self.middlewares.iter() {
match middleware_handler
.handle(
parsed_request.url_path.as_str(),
parsed_request.body_json.as_ref(),
&parsed_request.component_parts.headers,
)
.await
{
Some(x) => return Some(x),
None => continue,
};
}
None
}
pub async fn rule_set_response(
&self,
parsed_request: &ParsedRequest,
) -> Option<Result<hyper::Response<BoxBody>, hyper::http::Error>> {
for (rule_set_idx, rule_set) in self.rule_sets.iter().enumerate() {
match rule_set.find_matched(parsed_request, self.strategy.as_ref(), rule_set_idx) {
Some(respond) => {
let dir_prefix = rule_set.dir_prefix();
let response = respond.response(dir_prefix.as_str(), &parsed_request).await;
return Some(response);
}
None => (),
}
}
None
}
pub fn validate(&self) -> bool {
let rule_sets_validate =
self.rule_sets
.iter()
.enumerate()
.all(|(rule_set_idx, rule_set)| {
let prefix_validate = rule_set.prefix.is_none()
|| rule_set.prefix.as_ref().unwrap().validate(rule_set_idx);
let default_validate =
rule_set.default.is_none() || rule_set.default.as_ref().unwrap().validate();
let guard_validate =
rule_set.guard.is_none() || rule_set.guard.as_ref().unwrap().validate();
let dir_prefix = rule_set.dir_prefix();
let rules_validate =
rule_set.rules.iter().enumerate().all(|(rule_idx, rule)| {
rule.when.validate(rule_idx, rule_set_idx)
&& rule.respond.validate(
dir_prefix.as_str(),
rule_idx,
rule_set_idx,
)
});
prefix_validate && default_validate && guard_validate && rules_validate
});
if !rule_sets_validate {
log::error!("something wrong in rule sets");
}
let fallback_respond_dir_validate = Path::new(self.fallback_respond_dir.as_str()).exists();
if !fallback_respond_dir_validate {
log::error!(
"{} fallback_respond_dir: {}",
style("invalid").red(),
self.fallback_respond_dir
);
}
rule_sets_validate && fallback_respond_dir_validate
}
}
impl Default for ServiceConfig {
fn default() -> Self {
ServiceConfig {
strategy: Some(Strategy::default()),
rule_sets_file_paths: None,
rule_sets: vec![],
middlewares_file_paths: None,
middlewares: vec![],
fallback_respond_dir: SERVICE_DEFAULT_FALLBACK_RESPOND_DIR.to_owned(),
}
}
}
impl std::fmt::Display for ServiceConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let has_rule_sets = !self.rule_sets.is_empty();
if has_rule_sets {
let _ = writeln!(
f,
"[rule_sets.strategy] {}",
self.strategy.clone().unwrap_or_default()
);
let _ = writeln!(f, "");
}
for (idx, rule_set) in self.rule_sets.iter().enumerate() {
let _ = writeln!(
f,
"@ rule_set #{} ({})\n",
idx + 1,
style(rule_set.file_path.as_str()).green()
);
let _ = write!(f, "{}\n", rule_set);
}
if has_rule_sets {
let _ = writeln!(f, "{}", PRINT_DELIMITER);
}
let _ = writeln!(
f,
"[fallback_respond_dir] {}",
canonicalized_fallback_respond_dir_to_print(self.fallback_respond_dir.as_str())
);
Ok(())
}
}