263 lines
7.5 KiB
Rust
263 lines
7.5 KiB
Rust
use std::io::{BufWriter, Write};
|
|
|
|
use super::Section;
|
|
|
|
#[derive(Debug)]
|
|
pub enum Error {
|
|
WriteData,
|
|
}
|
|
|
|
impl std::fmt::Display for Error {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Error::WriteData => f.write_str("Cannot write data"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for Error {}
|
|
|
|
pub struct Request<'args, W>
|
|
where
|
|
W: Write,
|
|
{
|
|
pub content: &'args str,
|
|
pub writer: W,
|
|
pub sections: &'args [String],
|
|
}
|
|
|
|
pub fn execute<W>(req: Request<W>) -> Result<(), Error>
|
|
where
|
|
W: Write,
|
|
{
|
|
let mut writer = BufWriter::new(req.writer);
|
|
|
|
let choose_sections = req
|
|
.sections
|
|
.iter()
|
|
.map(|s| Section::parse(s.as_str()))
|
|
.collect::<Vec<_>>();
|
|
|
|
let mut current_sections: Option<Vec<Section>> = None;
|
|
|
|
for line in req.content.split_inclusive('\n') {
|
|
let new_line = if is_section_end(line) {
|
|
current_sections = None;
|
|
line.to_string()
|
|
} else if let Some(section_info) = line.strip_prefix("### ") {
|
|
current_sections = section_info
|
|
.split_whitespace()
|
|
.next()
|
|
.map(|r| r.split(',').map(Section::parse).collect());
|
|
line.to_string()
|
|
} else if let Some(cur_sections) = current_sections.clone() {
|
|
let trimmed_line = line.trim_start_matches(['#', ' ']);
|
|
if should_enable_variable(&choose_sections, &cur_sections) {
|
|
String::from(trimmed_line)
|
|
} else if should_disable_variable(&choose_sections, &cur_sections) {
|
|
format!("# {}", trimmed_line)
|
|
} else {
|
|
line.to_string()
|
|
}
|
|
} else {
|
|
line.to_string()
|
|
};
|
|
|
|
writer
|
|
.write_all(new_line.as_bytes())
|
|
.map_err(|_| Error::WriteData)?;
|
|
}
|
|
|
|
writer.flush().map_err(|_| Error::WriteData)
|
|
}
|
|
|
|
fn is_section_end(line: &str) -> bool {
|
|
line.trim().is_empty()
|
|
}
|
|
|
|
fn should_enable_variable(choose_sections: &[Section], current_sections: &[Section]) -> bool {
|
|
let cross_sections = choose_sections
|
|
.iter()
|
|
.filter(|s| {
|
|
s.namespace.is_some()
|
|
&& current_sections
|
|
.iter()
|
|
.any(|s2| s.namespace == s2.namespace)
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
if cross_sections.is_empty() {
|
|
choose_sections.iter().any(|s| {
|
|
if s.namespace.is_none() {
|
|
current_sections.iter().any(|s2| s.name == s2.name)
|
|
} else {
|
|
current_sections.contains(s)
|
|
}
|
|
})
|
|
} else {
|
|
cross_sections.iter().any(|s| current_sections.contains(s))
|
|
}
|
|
}
|
|
|
|
fn should_disable_variable(choose_sections: &[Section], current_sections: &[Section]) -> bool {
|
|
choose_sections.is_empty()
|
|
|| choose_sections.iter().any(|s| s.namespace.is_none())
|
|
|| choose_sections
|
|
.iter()
|
|
.filter(|s| s.namespace.is_some())
|
|
.any(|s| {
|
|
current_sections
|
|
.iter()
|
|
.any(|s2| s.namespace == s2.namespace && s.name != s2.name)
|
|
})
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use std::io::Cursor;
|
|
|
|
const BASE_ENV: &str = include_str!("../../test_data/base_env");
|
|
|
|
fn make_test(input: &str, expected_output: &str, sections: Vec<&str>) {
|
|
let mut output_data = vec![];
|
|
let writer = Cursor::new(&mut output_data);
|
|
|
|
match execute(Request {
|
|
content: input,
|
|
writer,
|
|
sections: §ions.into_iter().map(String::from).collect::<Vec<_>>(),
|
|
}) {
|
|
Ok(()) => {
|
|
let output = String::from_utf8(output_data).unwrap();
|
|
assert_eq!(
|
|
output.lines().collect::<Vec<_>>(),
|
|
expected_output.lines().collect::<Vec<_>>()
|
|
);
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn should_disable_all_sections() {
|
|
make_test(include_str!("../../test_data/all_env"), BASE_ENV, vec![]);
|
|
}
|
|
|
|
#[test]
|
|
fn should_enable_local_sections() {
|
|
make_test(
|
|
BASE_ENV,
|
|
include_str!("../../test_data/should_enable_local"),
|
|
vec!["local"],
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn should_enable_staging_sections() {
|
|
make_test(
|
|
BASE_ENV,
|
|
include_str!("../../test_data/should_enable_staging"),
|
|
vec!["staging"],
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn should_use_debug_in_staging_section() {
|
|
make_test(
|
|
BASE_ENV,
|
|
include_str!("../../test_data/should_use_debug_in_staging"),
|
|
vec!["staging", "debug:on"],
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn should_use_staging_db_in_local_section() {
|
|
make_test(
|
|
BASE_ENV,
|
|
include_str!("../../test_data/should_use_staging_db_in_local"),
|
|
vec!["local", "db:staging"],
|
|
);
|
|
}
|
|
|
|
mod utils {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn should_not_enable_variables() {
|
|
assert!(!should_enable_variable(
|
|
&[Section::new("local")],
|
|
&[Section::new("staging")]
|
|
));
|
|
assert!(!should_enable_variable(
|
|
&[Section::with_namespace("db", "local")],
|
|
&[Section::new("local")]
|
|
));
|
|
assert!(!should_enable_variable(
|
|
&[Section::with_namespace("db", "local")],
|
|
&[Section::with_namespace("db", "staging")]
|
|
));
|
|
assert!(!should_enable_variable(
|
|
&[
|
|
Section::new("staging"),
|
|
Section::with_namespace("debug", "on")
|
|
],
|
|
&[
|
|
Section::with_namespace("debug", "off"),
|
|
Section::new("staging")
|
|
]
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn should_enable_variables() {
|
|
assert!(should_enable_variable(
|
|
&[Section::new("local")],
|
|
&[Section::new("local")]
|
|
));
|
|
assert!(should_enable_variable(
|
|
&[Section::new("local")],
|
|
&[Section::with_namespace("db", "local")]
|
|
));
|
|
assert!(should_enable_variable(
|
|
&[Section::with_namespace("db", "local")],
|
|
&[Section::with_namespace("db", "local")]
|
|
));
|
|
assert!(should_enable_variable(
|
|
&[
|
|
Section::new("local"),
|
|
Section::with_namespace("debug", "on")
|
|
],
|
|
&[
|
|
Section::with_namespace("debug", "on"),
|
|
Section::new("staging")
|
|
]
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn should_not_disable_variables() {
|
|
assert!(!should_disable_variable(
|
|
&[Section::with_namespace("debug", "on")],
|
|
&[Section::new("local")]
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn should_disable_variables() {
|
|
assert!(should_disable_variable(
|
|
&[Section::new("local")],
|
|
&[Section::with_namespace("debug", "off")]
|
|
));
|
|
assert!(should_disable_variable(
|
|
&[],
|
|
&[Section::with_namespace("debug", "off")]
|
|
));
|
|
assert!(should_disable_variable(
|
|
&[Section::with_namespace("debug", "on")],
|
|
&[Section::with_namespace("debug", "off")]
|
|
));
|
|
}
|
|
}
|
|
}
|