Skip to content

Commit

Permalink
Merge pull request #8 from mirdaki/reorg
Browse files Browse the repository at this point in the history
  • Loading branch information
mirdaki authored Sep 26, 2022
2 parents bd22141 + f588397 commit 68602d0
Show file tree
Hide file tree
Showing 5 changed files with 491 additions and 482 deletions.
File renamed without changes.
File renamed without changes.
297 changes: 297 additions & 0 deletions src/environment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
use std::{collections::HashMap, ffi::OsString, str::FromStr};

use axum::http::Uri;
use regex::Regex;
use substring::Substring;

/// Extract the configured port number, if one is there, from the environmental variables
pub fn extract_port_number<I>(env_vars: I, env_var_prefix: &str) -> Option<u16>
where
I: IntoIterator<Item = (OsString, OsString)>,
{
env_vars
.into_iter()
.find_map(|(x, y)| match (x.into_string(), y.into_string()) {
(Ok(x), Ok(y)) if x.eq(env_var_prefix) => {
if let Ok(y) = y.parse::<u16>() {
return Some(y);
}
None
}
_ => None,
})
}

/// Extract all available standard URIs from the environmental variables
pub fn extract_standard_uris<I>(env_vars: I, env_var_prefix: &str) -> HashMap<String, Uri>
where
I: IntoIterator<Item = (OsString, OsString)>,
{
env_vars
.into_iter()
.filter_map(|(x, y)| match (x.into_string(), y.into_string()) {
(Ok(x), Ok(y)) if x.starts_with(env_var_prefix) => match Uri::from_str(&y) {
Ok(y) => {
let x = x.substring(env_var_prefix.len(), x.len()).to_owned();
Some((x, y))
}
_ => None,
},
_ => None,
})
.collect()
}

/// Extract all available pattern URIs from the environmental variables
pub fn extract_pattern_uris<I>(
env_vars: I,
env_var_uri_prefix: &str,
env_var_regex_prefix: &str,
) -> Vec<(Regex, String)>
where
I: IntoIterator<Item = (OsString, OsString)>,
{
// Partition is used, because env_vars needs to be split into multiple collections since it's consumed upon iteration
let (uri_list, everything_else): (Vec<_>, Vec<_>) = env_vars.into_iter().partition(
|(x, _)| matches!(x.clone().into_string(), Ok(x) if x.starts_with(env_var_uri_prefix)),
);

let (regex_list, _): (Vec<_>, _) = everything_else.into_iter().partition(
|(x, _)| matches!(x.clone().into_string(), Ok(x) if x.starts_with(env_var_regex_prefix)),
);

let uri_length = uri_list.len();
let uri_list = uri_list
.into_iter()
.filter_map(|(x, y)| match (x.into_string(), y.into_string()) {
(Ok(x), Ok(y)) => match x[env_var_uri_prefix.len()..].parse::<usize>() {
Ok(x) => Some((x, y)),
_ => None,
},
_ => None,
})
.fold(
vec![String::new(); uri_length],
|mut list: Vec<String>, (x, y)| {
list[x] = y;
list
},
);

let regex_length = regex_list.len();
let regex_list = regex_list
.into_iter()
.filter_map(|(x, y)| match (x.into_string(), y.into_string()) {
(Ok(x), Ok(y)) => {
match (
x[env_var_regex_prefix.len()..].parse::<usize>(),
Regex::from_str(&y),
) {
(Ok(x), Ok(y)) => Some((x, y)),
_ => None,
}
}
_ => None,
})
.fold(
vec![Regex::new("").unwrap(); regex_length],
|mut list: Vec<Regex>, (x, y)| {
list[x] = y;
list
},
);

regex_list
.into_iter()
.zip(uri_list)
.collect::<Vec<(Regex, String)>>()
}

mod tests {
#![allow(clippy::unnecessary_wraps)]

// Unclear why this is treated as an unused import, but this patches the problem
#[allow(unused_imports)]
use super::*;

#[test]
fn load_port_env_var() -> Result<(), ()> {
const PORT_ENV_NAME: &str = "TEST_PORT_ENV_NAME";
let port_to_pass = 8080;

let unrelated_key = "test";
let unrelated_value = "test";
let not_number = "notANumber";
let signed_number = "-3000";
let valid_value = port_to_pass.to_string();
let not_the_first_valid_value = "8000";

let variables_from_environment = vec![
(
OsString::from_str(unrelated_key).unwrap(),
OsString::from_str(unrelated_value).unwrap(),
),
(
OsString::from_str(PORT_ENV_NAME).unwrap(),
OsString::from_str(not_number).unwrap(),
),
(
OsString::from_str(PORT_ENV_NAME).unwrap(),
OsString::from_str(signed_number).unwrap(),
),
(
OsString::from_str(PORT_ENV_NAME).unwrap(),
OsString::from_str(valid_value.as_str()).unwrap(),
),
(
OsString::from_str(PORT_ENV_NAME).unwrap(),
OsString::from_str(not_the_first_valid_value).unwrap(),
),
];

let result = extract_port_number(variables_from_environment.into_iter(), PORT_ENV_NAME);

assert_eq!(result, Some(port_to_pass));

Ok(())
}

#[test]
fn load_standard_env_var() -> Result<(), ()> {
const STANDARD_URI_ENV_NAME: &str = "TEST_STANDARD_URI_ENV_NAME";

let simple_key = "test";
let simple_value = "https://example.com/";
let unused_key = "unused";
let unused_value = "https://example.com/unused";
let empty_key = "";
let empty_value = "https://example.com/empty";
let overridden_duplicate_key = "override";
let overridden_duplicate_value = "https://example.com/overridden";
let override_duplicate_key = "override";
let override_duplicate_value = "https://example.com/override";

let variables_from_environment = vec![
(
OsString::from_str(format!("{}{}", STANDARD_URI_ENV_NAME, simple_key).as_str())
.unwrap(),
OsString::from_str(simple_value).unwrap(),
),
(
OsString::from_str(unused_key).unwrap(),
OsString::from_str(unused_value).unwrap(),
),
(
OsString::from_str(format!("{}{}", STANDARD_URI_ENV_NAME, empty_key).as_str())
.unwrap(),
OsString::from_str(empty_value).unwrap(),
),
(
OsString::from_str(
format!("{}{}", STANDARD_URI_ENV_NAME, overridden_duplicate_key).as_str(),
)
.unwrap(),
OsString::from_str(overridden_duplicate_value).unwrap(),
),
(
OsString::from_str(
format!("{}{}", STANDARD_URI_ENV_NAME, override_duplicate_key).as_str(),
)
.unwrap(),
OsString::from_str(override_duplicate_value).unwrap(),
),
];

let result = extract_standard_uris(
variables_from_environment.into_iter(),
STANDARD_URI_ENV_NAME,
);

assert_eq!(
result.get(simple_key).unwrap(),
&Uri::from_str(simple_value).unwrap()
);
assert!(result.get(unused_key).is_none());
assert_eq!(
result.get(empty_key).unwrap(),
&Uri::from_str(empty_value).unwrap()
);
assert_eq!(
result.get(override_duplicate_key).unwrap(),
&Uri::from_str(override_duplicate_value).unwrap()
);

Ok(())
}

#[test]
fn load_pattern_env_var() -> Result<(), ()> {
const PATTERN_URI_ENV_NAME: &str = "TEST_PATTERN_URI_ENV_NAME";
const PATTERN_REGEX_ENV_NAME: &str = "TEST_PATTERN_REGEX_ENV_NAME";

let regex_0 = "a*";
let value_0 = "https://example.com/";
let regex_1 = r"^i(a+)$";
let value_1 = "https://example.com/a";
let regex_2 = r"^i(d+)$";
let value_2 = "https://example.com/$1";
let regex_3 = r"^i(?P<index>\d+)$";
let value_3 = "https://example.com/$index";

let variables_from_environment = vec![
(
OsString::from_str(format!("{}{}", PATTERN_REGEX_ENV_NAME, 1).as_str()).unwrap(),
OsString::from_str(regex_1).unwrap(),
),
(
OsString::from_str(format!("{}{}", PATTERN_REGEX_ENV_NAME, 0).as_str()).unwrap(),
OsString::from_str(regex_0).unwrap(),
),
(
OsString::from_str(format!("{}{}", PATTERN_URI_ENV_NAME, 0).as_str()).unwrap(),
OsString::from_str(value_0).unwrap(),
),
(
OsString::from_str(format!("{}{}", PATTERN_URI_ENV_NAME, 1).as_str()).unwrap(),
OsString::from_str(value_1).unwrap(),
),
(
OsString::from_str(format!("{}{}", PATTERN_REGEX_ENV_NAME, 2).as_str()).unwrap(),
OsString::from_str(regex_2).unwrap(),
),
(
OsString::from_str(format!("{}{}", PATTERN_URI_ENV_NAME, 2).as_str()).unwrap(),
OsString::from_str(value_2).unwrap(),
),
(
OsString::from_str(format!("{}{}", PATTERN_REGEX_ENV_NAME, 3).as_str()).unwrap(),
OsString::from_str(regex_3).unwrap(),
),
(
OsString::from_str(format!("{}{}", PATTERN_URI_ENV_NAME, 3).as_str()).unwrap(),
OsString::from_str(value_3).unwrap(),
),
];

let result = extract_pattern_uris(
variables_from_environment,
PATTERN_URI_ENV_NAME,
PATTERN_REGEX_ENV_NAME,
);

assert_eq!(result[0].0.to_string(), regex_0);
assert_eq!(result[0].1, value_0);

// Testing that patterns can be added in any order
assert_eq!(result[1].0.to_string(), regex_1);
assert_eq!(result[1].1, value_1);

assert_eq!(result[2].0.to_string(), regex_2);
assert_eq!(result[2].1, value_2);

assert_eq!(result[3].0.to_string(), regex_3);
assert_eq!(result[3].1, value_3);

Ok(())
}
}
Loading

0 comments on commit 68602d0

Please sign in to comment.