ferron/util/
obtain_config_struct_vec.rs

1use std::error::Error;
2use std::net::IpAddr;
3use std::slice::Iter;
4use std::sync::Arc;
5
6use crate::ferron_common::ServerConfig;
7use crate::ferron_util::ip_match::ip_match;
8use crate::ferron_util::match_hostname::match_hostname;
9use crate::ferron_util::match_location::match_location;
10
11pub struct ObtainConfigStructVec<T> {
12  global: Arc<Vec<T>>,
13  host: Arc<Vec<ObtainConfigStructVecHost<T>>>,
14}
15
16struct ObtainConfigStructVecHost<T> {
17  domain: Option<String>,
18  ip: Option<String>,
19  data: Vec<T>,
20  locations: Vec<ObtainConfigStructVecLocation<T>>,
21  error_configs: Vec<ObtainConfigStructVecErrorConfig<T>>,
22}
23
24struct ObtainConfigStructVecLocation<T> {
25  path: String,
26  data: Vec<T>,
27}
28
29struct ObtainConfigStructVecErrorConfig<T> {
30  scode: Option<u16>,
31  data: Vec<T>,
32}
33
34impl<T> ObtainConfigStructVec<T> {
35  pub fn new(
36    config: &ServerConfig,
37    mut execute_fn: impl FnMut(&ServerConfig) -> Result<Vec<T>, Box<dyn Error + Send + Sync>>,
38  ) -> Result<Self, Box<dyn Error + Send + Sync>> {
39    let global_struct = execute_fn(&config["global"])?;
40    let mut host_structs = Vec::new();
41
42    if let Some(hosts) = config["hosts"].as_vec() {
43      for host_yaml in hosts.iter() {
44        let domain = host_yaml["domain"].as_str().map(String::from);
45        let ip = host_yaml["ip"].as_str().map(String::from);
46        let mut error_configs = Vec::new();
47        let mut locations = Vec::new();
48        if let Some(error_configs_yaml) = host_yaml["errorConfig"].as_vec() {
49          for error_config_yaml in error_configs_yaml.iter() {
50            let scode = error_config_yaml["scode"].as_i64().map(|s| s as u16);
51            error_configs.push(ObtainConfigStructVecErrorConfig {
52              scode,
53              data: execute_fn(error_config_yaml)?,
54            });
55          }
56        }
57        if let Some(locations_yaml) = host_yaml["locations"].as_vec() {
58          for location_yaml in locations_yaml.iter() {
59            if let Some(path_str) = location_yaml["path"].as_str() {
60              let path = String::from(path_str);
61              locations.push(ObtainConfigStructVecLocation {
62                path,
63                data: execute_fn(location_yaml)?,
64              });
65            }
66          }
67        }
68        host_structs.push(ObtainConfigStructVecHost {
69          domain,
70          ip,
71          data: execute_fn(host_yaml)?,
72          locations,
73          error_configs,
74        });
75      }
76    }
77
78    Ok(Self {
79      global: Arc::new(global_struct),
80      host: Arc::new(host_structs),
81    })
82  }
83}
84
85impl<'a, T> ObtainConfigStructVec<T>
86where
87  T: 'a,
88{
89  pub fn obtain(
90    &'a self,
91    hostname: Option<&str>,
92    ip: IpAddr,
93    request_url: &str,
94    status_code: Option<u16>,
95  ) -> Vec<&'a T> {
96    let data_iter: Iter<'a, T> = self.global.iter();
97    let mut host_data_iter: Box<dyn Iterator<Item = &'a T>> = Box::new(vec![].into_iter());
98    let mut error_config_or_location_data_iter: Box<dyn Iterator<Item = &'a T>> =
99      Box::new(vec![].into_iter());
100
101    // Should have used a HashMap instead of iterating over an array for better performance...
102    for host in self.host.iter() {
103      if match_hostname(
104        match &host.domain {
105          Some(value) => Some(value as &str),
106          None => None,
107        },
108        hostname,
109      ) && match &host.ip {
110        Some(value) => ip_match(value as &str, ip),
111        None => true,
112      } {
113        host_data_iter = Box::new(host.data.iter());
114        let mut error_config_used = false;
115        if let Some(status_code) = status_code {
116          for error_config in host.error_configs.iter() {
117            if error_config.scode.is_none() || error_config.scode == Some(status_code) {
118              error_config_or_location_data_iter = Box::new(error_config.data.iter());
119              error_config_used = true;
120              break;
121            }
122          }
123        }
124        if !error_config_used {
125          if let Ok(path_decoded) = urlencoding::decode(request_url) {
126            for location in host.locations.iter() {
127              if match_location(&location.path, &path_decoded) {
128                error_config_or_location_data_iter = Box::new(location.data.iter());
129                break;
130              }
131            }
132          }
133        }
134        break;
135      }
136    }
137
138    data_iter
139      .chain(host_data_iter)
140      .chain(error_config_or_location_data_iter)
141      .collect()
142  }
143}
144
145impl<T> Clone for ObtainConfigStructVec<T> {
146  fn clone(&self) -> Self {
147    Self {
148      global: self.global.clone(),
149      host: self.host.clone(),
150    }
151  }
152}