Spaces:
Runtime error
Runtime error
File size: 6,208 Bytes
9552aa0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | use std::fs;
use std::path::PathBuf;
use std::str::FromStr;
use std::{collections::HashSet, error::Error};
use glob::glob;
use yaml_rust2::{Yaml, YamlLoader};
pub fn load_config(path: PathBuf) -> Result<Yaml, Box<dyn Error + Send + Sync>> {
load_config_inner(path, &mut HashSet::new())
}
fn load_config_inner(
path: PathBuf,
loaded_paths: &mut HashSet<PathBuf>,
) -> Result<Yaml, Box<dyn Error + Send + Sync>> {
// Canonicalize the path
let canonical_pathbuf = fs::canonicalize(&path).unwrap_or_else(|_| path.clone());
// Check if the path is duplicate. If it's not, add it to loaded paths.
if loaded_paths.contains(&canonical_pathbuf) {
let canonical_path = canonical_pathbuf.to_string_lossy().into_owned();
Err(anyhow::anyhow!(
"Detected the server configuration file include loop while attempting to load \"{}\"",
canonical_path
))?
} else {
loaded_paths.insert(canonical_pathbuf.clone());
}
// Read the configuration file
let file_contents = match fs::read_to_string(&path) {
Ok(file) => file,
Err(err) => {
let canonical_path = canonical_pathbuf.to_string_lossy().into_owned();
Err(anyhow::anyhow!(
"Failed to read from the server configuration file at \"{}\": {}",
canonical_path,
err
))?
}
};
// Load YAML configuration from the file contents
let yaml_configs = match YamlLoader::load_from_str(&file_contents) {
Ok(yaml_configs) => yaml_configs,
Err(err) => Err(anyhow::anyhow!(
"Failed to parse the server configuration file: {}",
err
))?,
};
// Ensure the YAML file is not empty
if yaml_configs.is_empty() {
Err(anyhow::anyhow!(
"No YAML documents detected in the server configuration file."
))?;
}
let mut yaml_config = yaml_configs[0].clone(); // Clone the first YAML document
if yaml_config.is_hash() {
// Get the list of included files
let mut include_files = Vec::new();
if let Some(include_yaml) = yaml_config["include"].as_vec() {
for include_one_yaml in include_yaml.iter() {
if let Some(include_glob) = include_one_yaml.as_str() {
let include_glob_pathbuf = match PathBuf::from_str(include_glob) {
Ok(pathbuf) => pathbuf,
Err(err) => {
let canonical_path = canonical_pathbuf.to_string_lossy().into_owned();
Err(anyhow::anyhow!(
"Failed to determine includes for the server configuration file at \"{}\": {}",
canonical_path,
err
))?
}
};
let include_glob_pathbuf_canonicalized = if include_glob_pathbuf.is_absolute() {
include_glob_pathbuf
} else {
let mut canonical_dirname = canonical_pathbuf.clone();
canonical_dirname.pop();
canonical_dirname.join(include_glob_pathbuf)
};
let files_globbed = match glob(&include_glob_pathbuf_canonicalized.to_string_lossy()) {
Ok(files_globbed) => files_globbed,
Err(err) => {
let canonical_path = canonical_pathbuf.to_string_lossy().into_owned();
Err(anyhow::anyhow!(
"Failed to determine includes for the server configuration file at \"{}\": {}",
canonical_path,
err
))?
}
};
for file_globbed_result in files_globbed {
let file_globbed = match file_globbed_result {
Ok(file_globbed) => file_globbed,
Err(err) => {
let canonical_path = canonical_pathbuf.to_string_lossy().into_owned();
Err(anyhow::anyhow!(
"Failed to determine includes for the server configuration file at \"{}\": {}",
canonical_path,
err
))?
}
};
include_files
.push(fs::canonicalize(&file_globbed).unwrap_or_else(|_| file_globbed.clone()));
}
}
}
}
// Delete included configuration from YAML configuration
if let Some(yaml_config_hash) = yaml_config.as_mut_hash() {
yaml_config_hash.remove(&Yaml::String("include".to_string()));
// Merge included configuration
for included_file in include_files {
let yaml_to_include = load_config_inner(included_file, loaded_paths)?;
if let Some(yaml_to_include_hashmap) = yaml_to_include.as_hash() {
for (key, value) in yaml_to_include_hashmap.iter() {
if let Some(key) = key.as_str() {
if key != "include" {
match value {
Yaml::Array(host_array) => {
yaml_config_hash
.entry(Yaml::String(key.to_string()))
.and_modify(|global_val| {
if let Yaml::Array(global_array) = global_val {
global_array.extend(host_array.clone());
} else {
*global_val = Yaml::Array(host_array.clone());
}
})
.or_insert_with(|| Yaml::Array(host_array.clone()));
}
Yaml::Hash(host_hash) => {
yaml_config_hash
.entry(Yaml::String(key.to_string()))
.and_modify(|global_val| {
if let Yaml::Hash(global_hash) = global_val {
for (k, v) in host_hash {
global_hash.insert(k.clone(), v.clone());
}
} else {
*global_val = Yaml::Hash(host_hash.clone());
}
})
.or_insert_with(|| Yaml::Hash(host_hash.clone()));
}
_ => {
yaml_config_hash.insert(Yaml::String(key.to_string()), value.clone());
}
}
}
}
}
}
}
}
}
// Return the server configuration
Ok(yaml_config)
}
|