report which lines changed on up/down
set_local_db, toggle_after_line and set_dot_env now return a FileChange (path + before/after line pairs) instead of (), and main prints it after each toggle so 'up'/'down' shows exactly what the program rewrote in each env file.
This commit is contained in:
+287
-19
@@ -1,4 +1,5 @@
|
||||
use std::{
|
||||
fmt,
|
||||
fs::{self, File},
|
||||
io::{BufRead, BufReader},
|
||||
};
|
||||
@@ -9,8 +10,27 @@ use crate::env::config::DevToolsConf;
|
||||
pub const GLOBAL_CONNECTION_PATH: &str = "conf/global/connections.env";
|
||||
const LOCAL_CONNECTION_PATH: &str = "conf/local/.env";
|
||||
|
||||
/// The lines a toggle operation rewrote in a given file, as (before, after) pairs.
|
||||
pub struct FileChange {
|
||||
pub path: String,
|
||||
pub diffs: Vec<(String, String)>,
|
||||
}
|
||||
|
||||
impl fmt::Display for FileChange {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.diffs.is_empty() {
|
||||
return writeln!(f, "[!] {}: no changes", self.path);
|
||||
}
|
||||
writeln!(f, "[!] {} changed:", self.path)?;
|
||||
for (before, after) in &self.diffs {
|
||||
writeln!(f, " {} -> {}", before, after)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// writes global connections
|
||||
pub fn set_local_db(start: bool, config: &DevToolsConf) -> Result<()> {
|
||||
pub fn set_local_db(start: bool, config: &DevToolsConf) -> Result<FileChange> {
|
||||
let global_connections_env = format!(
|
||||
"{}{}",
|
||||
config.project_dir_as_string(),
|
||||
@@ -21,27 +41,39 @@ pub fn set_local_db(start: bool, config: &DevToolsConf) -> Result<()> {
|
||||
let reader: BufReader<File> = BufReader::new(file);
|
||||
|
||||
let mut modified_lines: Vec<String> = Vec::new();
|
||||
let mut diffs: Vec<(String, String)> = Vec::new();
|
||||
for line in reader.lines() {
|
||||
let line_content = line?;
|
||||
let modified = if start && connections_env_comment(&line_content) {
|
||||
format!("# {}", line_content)
|
||||
let modified: Option<String> = if start && connections_env_comment(&line_content) {
|
||||
Some(format!("# {}", line_content))
|
||||
} else if !start && should_commented_out(&line_content) {
|
||||
line_content[2..].to_string()
|
||||
Some(line_content[2..].to_string())
|
||||
} else {
|
||||
line_content
|
||||
None
|
||||
};
|
||||
modified_lines.push(modified);
|
||||
|
||||
match modified {
|
||||
Some(new_line) => {
|
||||
diffs.push((line_content, new_line.clone()));
|
||||
modified_lines.push(new_line);
|
||||
}
|
||||
None => modified_lines.push(line_content),
|
||||
}
|
||||
}
|
||||
|
||||
fs::write(global_connections_env, modified_lines.join("\n"))
|
||||
fs::write(&global_connections_env, modified_lines.join("\n"))
|
||||
.context("Cannot write global connections env")?;
|
||||
Ok(())
|
||||
Ok(FileChange {
|
||||
path: global_connections_env,
|
||||
diffs,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_dot_env(start: bool, config: &DevToolsConf) -> Result<()> {
|
||||
toggle_after_line("#docker", 4, start, config)?;
|
||||
toggle_after_line("#cidb", 2, !start, config)?;
|
||||
Ok(())
|
||||
pub fn set_dot_env(start: bool, config: &DevToolsConf) -> Result<FileChange> {
|
||||
let mut docker_change = toggle_after_line("#docker", 4, start, config)?;
|
||||
let cidb_change = toggle_after_line("#cidb", 2, !start, config)?;
|
||||
docker_change.diffs.extend(cidb_change.diffs);
|
||||
Ok(docker_change)
|
||||
}
|
||||
|
||||
const CI_DB_MASTER_IP: &str = "ci.db.master.ip";
|
||||
@@ -85,7 +117,7 @@ fn toggle_after_line(
|
||||
num_lines: usize,
|
||||
activate: bool,
|
||||
config: &DevToolsConf,
|
||||
) -> Result<()> {
|
||||
) -> Result<FileChange> {
|
||||
let local_connection_env = format!(
|
||||
"{}{}",
|
||||
config.project_dir_as_string(),
|
||||
@@ -96,6 +128,7 @@ fn toggle_after_line(
|
||||
let reader: BufReader<File> = BufReader::new(file);
|
||||
|
||||
let mut modified_lines: Vec<String> = Vec::new();
|
||||
let mut diffs: Vec<(String, String)> = Vec::new();
|
||||
let mut relevant = false;
|
||||
let mut counter: usize = 0;
|
||||
|
||||
@@ -111,21 +144,34 @@ fn toggle_after_line(
|
||||
continue;
|
||||
}
|
||||
|
||||
if relevant {
|
||||
modified_lines.push(toggle(&line_content, activate));
|
||||
let modified: Option<String> = if relevant {
|
||||
counter += 1;
|
||||
let new_line = toggle(&line_content, activate);
|
||||
(new_line != line_content).then_some(new_line)
|
||||
} else if let Some(key) = connection_key(&line_content) {
|
||||
let value = if activate { "staging" } else { "ci" };
|
||||
modified_lines.push(format!("{}='{}'", key, value));
|
||||
let new_line = format!("{}='{}'", key, value);
|
||||
(new_line != line_content).then_some(new_line)
|
||||
} else {
|
||||
modified_lines.push(line_content);
|
||||
None
|
||||
};
|
||||
|
||||
match modified {
|
||||
Some(new_line) => {
|
||||
diffs.push((line_content, new_line.clone()));
|
||||
modified_lines.push(new_line);
|
||||
}
|
||||
None => modified_lines.push(line_content),
|
||||
}
|
||||
}
|
||||
|
||||
fs::write(local_connection_env, modified_lines.join("\n"))
|
||||
fs::write(&local_connection_env, modified_lines.join("\n"))
|
||||
.context("Cannot write local connection env")?;
|
||||
|
||||
Ok(())
|
||||
Ok(FileChange {
|
||||
path: local_connection_env,
|
||||
diffs,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the `connection` key as it appears on the given line (either the
|
||||
@@ -156,6 +202,228 @@ pub(crate) fn toggle(line: &str, activate: bool) -> String {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::ops::Deref;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// An isolated `conf/global`/`conf/local` tree under the OS temp dir, for
|
||||
/// exercising `set_local_db`/`set_dot_env` against real files without
|
||||
/// touching this repo's own `conf/` directory. Namespaced by process id
|
||||
/// so concurrent `cargo test` runs can't collide on the same path, and
|
||||
/// removed on drop so no stale directories accumulate.
|
||||
struct TempProjectDir(PathBuf);
|
||||
|
||||
impl TempProjectDir {
|
||||
fn new(name: &str) -> Self {
|
||||
let dir = std::env::temp_dir().join(format!(
|
||||
"dev_tools_parse_test_{}_{}",
|
||||
std::process::id(),
|
||||
name
|
||||
));
|
||||
let _ = fs::remove_dir_all(&dir);
|
||||
fs::create_dir_all(dir.join("conf/global")).unwrap();
|
||||
fs::create_dir_all(dir.join("conf/local")).unwrap();
|
||||
Self(dir)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for TempProjectDir {
|
||||
type Target = Path;
|
||||
fn deref(&self) -> &Path {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TempProjectDir {
|
||||
fn drop(&mut self) {
|
||||
let _ = fs::remove_dir_all(&self.0);
|
||||
}
|
||||
}
|
||||
|
||||
fn config_for(dir: &Path) -> DevToolsConf {
|
||||
DevToolsConf {
|
||||
container_service: String::new(),
|
||||
container_dir: String::new(),
|
||||
project_directory: PathBuf::from(format!("{}/", dir.display())),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_local_db_records_diffs_when_commenting_out() {
|
||||
let dir = TempProjectDir::new("comment_out");
|
||||
fs::write(
|
||||
dir.join(GLOBAL_CONNECTION_PATH),
|
||||
"ci.db.master.ip=127.0.0.1\nci.db.master.port=5432\nci.db.master.user=admin\nRCC_CI_DB_MASTER_IP=127.0.0.1",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let change = set_local_db(true, &config_for(&dir)).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
change.diffs,
|
||||
vec![
|
||||
(
|
||||
"ci.db.master.ip=127.0.0.1".to_string(),
|
||||
"# ci.db.master.ip=127.0.0.1".to_string()
|
||||
),
|
||||
(
|
||||
"ci.db.master.port=5432".to_string(),
|
||||
"# ci.db.master.port=5432".to_string()
|
||||
),
|
||||
(
|
||||
"RCC_CI_DB_MASTER_IP=127.0.0.1".to_string(),
|
||||
"# RCC_CI_DB_MASTER_IP=127.0.0.1".to_string()
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_local_db_records_diffs_when_uncommenting() {
|
||||
let dir = TempProjectDir::new("uncomment");
|
||||
fs::write(
|
||||
dir.join(GLOBAL_CONNECTION_PATH),
|
||||
"# ci.db.master.ip=127.0.0.1\n# ci.db.master.port=5432\nci.db.master.user=admin",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let change = set_local_db(false, &config_for(&dir)).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
change.diffs,
|
||||
vec![
|
||||
(
|
||||
"# ci.db.master.ip=127.0.0.1".to_string(),
|
||||
"ci.db.master.ip=127.0.0.1".to_string()
|
||||
),
|
||||
(
|
||||
"# ci.db.master.port=5432".to_string(),
|
||||
"ci.db.master.port=5432".to_string()
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn toggle_after_line_records_diffs_for_toggled_section() {
|
||||
let dir = TempProjectDir::new("toggle_section");
|
||||
fs::write(
|
||||
dir.join(LOCAL_CONNECTION_PATH),
|
||||
"#docker\nDOCKER_HOST=tcp://localhost:2375\n#DOCKER_TLS_VERIFY=1\n#DOCKER_CERT_PATH=/certs\nDOCKER_API_VERSION=1.41\nother=1",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let change = toggle_after_line("#docker", 4, true, &config_for(&dir)).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
change.diffs,
|
||||
vec![
|
||||
(
|
||||
"#DOCKER_TLS_VERIFY=1".to_string(),
|
||||
"DOCKER_TLS_VERIFY=1".to_string()
|
||||
),
|
||||
(
|
||||
"#DOCKER_CERT_PATH=/certs".to_string(),
|
||||
"DOCKER_CERT_PATH=/certs".to_string()
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn toggle_after_line_records_diffs_for_both_connection_keys() {
|
||||
let dir = TempProjectDir::new("connection_keys");
|
||||
fs::write(
|
||||
dir.join(LOCAL_CONNECTION_PATH),
|
||||
"#docker\nline1\nline2\nline3\nline4\nconnection='staging'\nRCC_CONNECTION='staging'",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let change = toggle_after_line("#cidb", 2, false, &config_for(&dir)).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
change.diffs,
|
||||
vec![
|
||||
(
|
||||
"connection='staging'".to_string(),
|
||||
"connection='ci'".to_string()
|
||||
),
|
||||
(
|
||||
"RCC_CONNECTION='staging'".to_string(),
|
||||
"RCC_CONNECTION='ci'".to_string()
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_dot_env_merges_diffs_from_both_sections() {
|
||||
let dir = TempProjectDir::new("merge_sections");
|
||||
fs::write(
|
||||
dir.join(LOCAL_CONNECTION_PATH),
|
||||
"#docker\nDOCKER_HOST=tcp://localhost:2375\n#DOCKER_TLS_VERIFY=1\n#DOCKER_CERT_PATH=/certs\nDOCKER_API_VERSION=1.41\n#cidb\nci.db.master.ip=127.0.0.1\nci.db.master.port=5432\nconnection='staging'\nRCC_CONNECTION='staging'",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let change = set_dot_env(true, &config_for(&dir)).unwrap();
|
||||
|
||||
assert!(change.path.ends_with(LOCAL_CONNECTION_PATH));
|
||||
assert_eq!(change.diffs.len(), 6);
|
||||
for expected in [
|
||||
("#DOCKER_TLS_VERIFY=1", "DOCKER_TLS_VERIFY=1"),
|
||||
("#DOCKER_CERT_PATH=/certs", "DOCKER_CERT_PATH=/certs"),
|
||||
("ci.db.master.ip=127.0.0.1", "#ci.db.master.ip=127.0.0.1"),
|
||||
("ci.db.master.port=5432", "#ci.db.master.port=5432"),
|
||||
("connection='staging'", "connection='ci'"),
|
||||
("RCC_CONNECTION='staging'", "RCC_CONNECTION='ci'"),
|
||||
] {
|
||||
assert!(
|
||||
change
|
||||
.diffs
|
||||
.contains(&(expected.0.to_string(), expected.1.to_string())),
|
||||
"missing diff {:?}",
|
||||
expected
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file_change_display_no_diffs() {
|
||||
let change = FileChange {
|
||||
path: "some/path".to_string(),
|
||||
diffs: vec![],
|
||||
};
|
||||
assert_eq!(format!("{}", change), "[!] some/path: no changes\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file_change_display_with_diffs() {
|
||||
let change = FileChange {
|
||||
path: "some/path".to_string(),
|
||||
diffs: vec![(
|
||||
"connection='ci'".to_string(),
|
||||
"connection='staging'".to_string(),
|
||||
)],
|
||||
};
|
||||
assert_eq!(
|
||||
format!("{}", change),
|
||||
"[!] some/path changed:\n connection='ci' -> connection='staging'\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file_change_display_with_multiple_diffs() {
|
||||
let change = FileChange {
|
||||
path: "some/path".to_string(),
|
||||
diffs: vec![
|
||||
("a=1".to_string(), "#a=1".to_string()),
|
||||
("b=2".to_string(), "#b=2".to_string()),
|
||||
],
|
||||
};
|
||||
assert_eq!(
|
||||
format!("{}", change),
|
||||
"[!] some/path changed:\n a=1 -> #a=1\n b=2 -> #b=2\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn toggle_activate_removes_leading_hash() {
|
||||
|
||||
+2
-2
@@ -24,13 +24,13 @@ fn main() -> Result<()> {
|
||||
start_docker_compose(&config.container_service, &config.container_dir, start)?;
|
||||
if args.legacy {
|
||||
println!("[!] Legacy mode active.");
|
||||
set_local_db(start, &config)?;
|
||||
print!("{}", set_local_db(start, &config)?);
|
||||
toggle_index(start, &config)?;
|
||||
} else {
|
||||
println!("[!] Legacy mode deactivated.");
|
||||
}
|
||||
|
||||
set_dot_env(start, &config)?;
|
||||
print!("{}", set_dot_env(start, &config)?);
|
||||
|
||||
println!(
|
||||
"[!] Don't forget your interpreter! Project directory: {}",
|
||||
|
||||
Reference in New Issue
Block a user