ferron/config/adapters/
yaml_legacy.rs

1use std::{
2  collections::HashMap,
3  error::Error,
4  net::{IpAddr, SocketAddr},
5  path::Path,
6};
7
8use ferron_yaml2kdl_core::convert_yaml_to_kdl;
9use kdl::{KdlDocument, KdlNode, KdlValue};
10
11use crate::config::{
12  Conditions, ErrorHandlerStatus, ServerConfiguration, ServerConfigurationEntries, ServerConfigurationEntry,
13  ServerConfigurationFilters, ServerConfigurationValue,
14};
15
16use super::ConfigurationAdapter;
17
18fn kdl_node_to_configuration_entry(kdl_node: &KdlNode) -> ServerConfigurationEntry {
19  let mut values = Vec::new();
20  let mut props = HashMap::new();
21  for kdl_entry in kdl_node.iter() {
22    let value = match kdl_entry.value().to_owned() {
23      KdlValue::String(value) => ServerConfigurationValue::String(value),
24      KdlValue::Integer(value) => ServerConfigurationValue::Integer(value),
25      KdlValue::Float(value) => ServerConfigurationValue::Float(value),
26      KdlValue::Bool(value) => ServerConfigurationValue::Bool(value),
27      KdlValue::Null => ServerConfigurationValue::Null,
28    };
29    if let Some(prop_name) = kdl_entry.name() {
30      props.insert(prop_name.value().to_string(), value);
31    } else {
32      values.push(value);
33    }
34  }
35  if values.is_empty() {
36    // If KDL node doesn't have any arguments, add the "#true" KDL value
37    values.push(ServerConfigurationValue::Bool(true));
38  }
39  ServerConfigurationEntry { values, props }
40}
41
42/// A legacy YAML configuration adapter that utilizes `ferron-yaml2kdl-core` component
43pub struct YamlLegacyConfigurationAdapter;
44
45impl YamlLegacyConfigurationAdapter {
46  /// Creates a new configuration adapter
47  pub fn new() -> Self {
48    Self
49  }
50}
51
52impl ConfigurationAdapter for YamlLegacyConfigurationAdapter {
53  fn load_configuration(&self, path: &Path) -> Result<Vec<ServerConfiguration>, Box<dyn Error + Send + Sync>> {
54    // Read and parse the configuration file contents
55    let kdl_document: KdlDocument = match convert_yaml_to_kdl(path.to_path_buf()) {
56      Ok(document) => document,
57      Err(err) => Err(anyhow::anyhow!(
58        "Failed to read and parse the server configuration file: {}",
59        err
60      ))?,
61    };
62
63    // Loaded configuration vector
64    let mut configurations = Vec::new();
65
66    // Iterate over KDL nodes
67    for kdl_node in kdl_document {
68      let global_name = kdl_node.name().value();
69      let children = kdl_node.children();
70      if let Some(children) = children {
71        for global_name in global_name.split(",") {
72          let (hostname, ip, port, is_host) = if global_name == "globals" {
73            (None, None, None, false)
74          } else if let Ok(socket_addr) = global_name.parse::<SocketAddr>() {
75            (None, Some(socket_addr.ip()), Some(socket_addr.port()), true)
76          } else if let Some((address, port_str)) = global_name.rsplit_once(':') {
77            if let Ok(port) = port_str.parse::<u16>() {
78              if let Ok(ip_address) = address
79                .strip_prefix('[')
80                .and_then(|s| s.strip_suffix(']'))
81                .unwrap_or(address)
82                .parse::<IpAddr>()
83              {
84                (None, Some(ip_address), Some(port), true)
85              } else if address == "*" || address.is_empty() {
86                (None, None, Some(port), true)
87              } else {
88                (Some(address.to_string()), None, Some(port), true)
89              }
90            } else if port_str == "*" {
91              if let Ok(ip_address) = address
92                .strip_prefix('[')
93                .and_then(|s| s.strip_suffix(']'))
94                .unwrap_or(address)
95                .parse::<IpAddr>()
96              {
97                (None, Some(ip_address), None, true)
98              } else if address == "*" || address.is_empty() {
99                (None, None, None, true)
100              } else {
101                (Some(address.to_string()), None, None, true)
102              }
103            } else {
104              Err(anyhow::anyhow!("Invalid host specifier"))?
105            }
106          } else if let Ok(ip_address) = global_name
107            .strip_prefix('[')
108            .and_then(|s| s.strip_suffix(']'))
109            .unwrap_or(global_name)
110            .parse::<IpAddr>()
111          {
112            (None, Some(ip_address), None, true)
113          } else if global_name == "*" || global_name.is_empty() {
114            (None, None, None, true)
115          } else {
116            (Some(global_name.to_string()), None, None, true)
117          };
118
119          let mut configuration_entries: HashMap<String, ServerConfigurationEntries> = HashMap::new();
120          for kdl_node in children.nodes() {
121            let kdl_node_name = kdl_node.name().value();
122            let children = kdl_node.children();
123            if kdl_node_name == "location" {
124              let mut configuration_entries: HashMap<String, ServerConfigurationEntries> = HashMap::new();
125              if let Some(children) = children {
126                if let Some(location) = kdl_node.entry(0) {
127                  if let Some(location_str) = location.value().as_string() {
128                    for kdl_node in children.nodes() {
129                      let kdl_node_name = kdl_node.name().value();
130                      let children = kdl_node.children();
131                      if kdl_node_name == "error_config" {
132                        let mut configuration_entries: HashMap<String, ServerConfigurationEntries> = HashMap::new();
133                        if let Some(children) = children {
134                          if let Some(error_status_code) = kdl_node.entry(0) {
135                            if let Some(error_status_code) = error_status_code.value().as_integer() {
136                              for kdl_node in children.nodes() {
137                                let kdl_node_name = kdl_node.name().value();
138                                let value = kdl_node_to_configuration_entry(kdl_node);
139                                if let Some(entries) = configuration_entries.get_mut(kdl_node_name) {
140                                  entries.inner.push(value);
141                                } else {
142                                  configuration_entries.insert(
143                                    kdl_node_name.to_string(),
144                                    ServerConfigurationEntries { inner: vec![value] },
145                                  );
146                                }
147                              }
148                              configurations.push(ServerConfiguration {
149                                entries: configuration_entries,
150                                filters: ServerConfigurationFilters {
151                                  is_host,
152                                  hostname: hostname.clone(),
153                                  ip,
154                                  port,
155                                  condition: Some(Conditions {
156                                    location_prefix: location_str.to_string(),
157                                    conditionals: vec![],
158                                  }),
159                                  error_handler_status: Some(ErrorHandlerStatus::Status(error_status_code as u16)),
160                                },
161                                modules: vec![],
162                              });
163                            } else {
164                              Err(anyhow::anyhow!("Invalid error handler status code"))?
165                            }
166                          } else {
167                            for kdl_node in children.nodes() {
168                              let kdl_node_name = kdl_node.name().value();
169                              let value = kdl_node_to_configuration_entry(kdl_node);
170                              if let Some(entries) = configuration_entries.get_mut(kdl_node_name) {
171                                entries.inner.push(value);
172                              } else {
173                                configuration_entries.insert(
174                                  kdl_node_name.to_string(),
175                                  ServerConfigurationEntries { inner: vec![value] },
176                                );
177                              }
178                            }
179                            configurations.push(ServerConfiguration {
180                              entries: configuration_entries,
181                              filters: ServerConfigurationFilters {
182                                is_host,
183                                hostname: hostname.clone(),
184                                ip,
185                                port,
186                                condition: Some(Conditions {
187                                  location_prefix: location_str.to_string(),
188                                  conditionals: vec![],
189                                }),
190                                error_handler_status: Some(ErrorHandlerStatus::Any),
191                              },
192                              modules: vec![],
193                            });
194                          }
195                        } else {
196                          Err(anyhow::anyhow!(
197                            "Error handler blocks should have children, but they don't"
198                          ))?
199                        }
200                      } else {
201                        let value = kdl_node_to_configuration_entry(kdl_node);
202                        if let Some(entries) = configuration_entries.get_mut(kdl_node_name) {
203                          entries.inner.push(value);
204                        } else {
205                          configuration_entries.insert(
206                            kdl_node_name.to_string(),
207                            ServerConfigurationEntries { inner: vec![value] },
208                          );
209                        }
210                      }
211                    }
212                    if kdl_node
213                      .entry("remove_base")
214                      .and_then(|e| e.value().as_bool())
215                      .unwrap_or(false)
216                    {
217                      configuration_entries.insert(
218                        "UNDOCUMENTED_REMOVE_PATH_PREFIX".to_string(),
219                        ServerConfigurationEntries {
220                          inner: vec![ServerConfigurationEntry {
221                            values: vec![ServerConfigurationValue::String(location_str.to_string())],
222                            props: HashMap::new(),
223                          }],
224                        },
225                      );
226                    }
227                    configurations.push(ServerConfiguration {
228                      entries: configuration_entries,
229                      filters: ServerConfigurationFilters {
230                        is_host,
231                        hostname: hostname.clone(),
232                        ip,
233                        port,
234                        condition: Some(Conditions {
235                          location_prefix: location_str.to_string(),
236                          conditionals: vec![],
237                        }),
238                        error_handler_status: None,
239                      },
240                      modules: vec![],
241                    });
242                  } else {
243                    Err(anyhow::anyhow!("Invalid location path"))?
244                  }
245                } else {
246                  Err(anyhow::anyhow!("Invalid location"))?
247                }
248              } else {
249                Err(anyhow::anyhow!("Locations should have children, but they don't"))?
250              }
251            } else if kdl_node_name == "error_config" {
252              let mut configuration_entries: HashMap<String, ServerConfigurationEntries> = HashMap::new();
253              if let Some(children) = children {
254                if let Some(error_status_code) = kdl_node.entry(0) {
255                  if let Some(error_status_code) = error_status_code.value().as_integer() {
256                    for kdl_node in children.nodes() {
257                      let kdl_node_name = kdl_node.name().value();
258                      let value = kdl_node_to_configuration_entry(kdl_node);
259                      if let Some(entries) = configuration_entries.get_mut(kdl_node_name) {
260                        entries.inner.push(value);
261                      } else {
262                        configuration_entries.insert(
263                          kdl_node_name.to_string(),
264                          ServerConfigurationEntries { inner: vec![value] },
265                        );
266                      }
267                    }
268                    configurations.push(ServerConfiguration {
269                      entries: configuration_entries,
270                      filters: ServerConfigurationFilters {
271                        is_host,
272                        hostname: hostname.clone(),
273                        ip,
274                        port,
275                        condition: None,
276                        error_handler_status: Some(ErrorHandlerStatus::Status(error_status_code as u16)),
277                      },
278                      modules: vec![],
279                    });
280                  } else {
281                    Err(anyhow::anyhow!("Invalid error handler status code"))?
282                  }
283                } else {
284                  for kdl_node in children.nodes() {
285                    let kdl_node_name = kdl_node.name().value();
286                    let value = kdl_node_to_configuration_entry(kdl_node);
287                    if let Some(entries) = configuration_entries.get_mut(kdl_node_name) {
288                      entries.inner.push(value);
289                    } else {
290                      configuration_entries.insert(
291                        kdl_node_name.to_string(),
292                        ServerConfigurationEntries { inner: vec![value] },
293                      );
294                    }
295                  }
296                  configurations.push(ServerConfiguration {
297                    entries: configuration_entries,
298                    filters: ServerConfigurationFilters {
299                      is_host,
300                      hostname: hostname.clone(),
301                      ip,
302                      port,
303                      condition: None,
304                      error_handler_status: Some(ErrorHandlerStatus::Any),
305                    },
306                    modules: vec![],
307                  });
308                }
309              } else {
310                Err(anyhow::anyhow!(
311                  "Error handler blocks should have children, but they don't"
312                ))?
313              }
314            } else {
315              let value = kdl_node_to_configuration_entry(kdl_node);
316              if let Some(entries) = configuration_entries.get_mut(kdl_node_name) {
317                entries.inner.push(value);
318              } else {
319                configuration_entries.insert(
320                  kdl_node_name.to_string(),
321                  ServerConfigurationEntries { inner: vec![value] },
322                );
323              }
324            }
325          }
326          configurations.push(ServerConfiguration {
327            entries: configuration_entries,
328            filters: ServerConfigurationFilters {
329              is_host,
330              hostname,
331              ip,
332              port,
333              condition: None,
334              error_handler_status: None,
335            },
336            modules: vec![],
337          });
338        }
339      } else {
340        // "include" directives aren't generated by `ferron-yaml2kdl-core`
341        Err(anyhow::anyhow!("Invalid top-level directive"))?
342      }
343    }
344
345    Ok(configurations)
346  }
347}