226 lines
6.2 KiB
Rust
226 lines
6.2 KiB
Rust
use std::cell::RefCell;
|
|
use std::io::{BufWriter, Read, Write};
|
|
|
|
use super::Section;
|
|
|
|
#[derive(Debug)]
|
|
pub enum Error {
|
|
ReadData,
|
|
WriteData,
|
|
}
|
|
|
|
pub struct Request<'args, R, W>
|
|
where
|
|
R: Read,
|
|
W: Write,
|
|
{
|
|
pub reader: RefCell<R>,
|
|
pub writer: W,
|
|
pub sections: &'args [String],
|
|
}
|
|
|
|
pub fn execute<R, W>(req: Request<R, W>) -> Result<(), Error>
|
|
where
|
|
R: Read,
|
|
W: Write,
|
|
{
|
|
let mut content = String::new();
|
|
req.reader
|
|
.borrow_mut()
|
|
.read_to_string(&mut content)
|
|
.map_err(|_| Error::ReadData)?;
|
|
|
|
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 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 {
|
|
format!("# {}", trimmed_line)
|
|
}
|
|
} 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))
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use std::cell::RefCell;
|
|
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 reader = RefCell::new(Cursor::new(input));
|
|
let mut output_data = vec![];
|
|
let writer = Cursor::new(&mut output_data);
|
|
|
|
match execute(Request {
|
|
reader,
|
|
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")
|
|
]
|
|
));
|
|
}
|
|
}
|
|
}
|