csv_converter 1.0.1

A vault GUI for your offline passwords
Documentation
use std::borrow::Cow;

use crate::{
	cli::{exit_with_error, ErrorStages},
	config::{Item, OutputConfig},
};

/// This function takes the [OutputConfig] and applies it to the input file line by line
pub fn run(input_line: &[String], output_config: &OutputConfig) -> Vec<Vec<String>> {
	let mut new_lines = Vec::new();
	let mut skip_line = false;
	for items in &output_config.lines {
		let mut line: Vec<String> = Vec::new();
		for item in items {
			match item {
				Item::Cell(i, filters) => match input_line.get(*i) {
					Some(v) => {
						let mut value: Cow<str> = Cow::Borrowed(v.as_str());
						if let Some(filters) = filters {
							for filter in filters {
								value = filter.run(value);
							}
						}
						line.push(value.to_string())
					},
					None => {
						exit_with_error(Some(format!("Cell not found \"<cell{i}>\"")), Some(ErrorStages::Process), 1);
					},
				},
				Item::If(condition, then_item, else_item) => {
					let condition_result =
						condition.run(then_item, &else_item.as_ref().map(|b| (**b).clone()), input_line).to_string();
					if &condition_result == "SKIP_THIS_LINE" {
						skip_line = true;
					}
					line.push(condition_result)
				},
				Item::Value(v) => line.push(v.clone()),
			}
		}
		if skip_line {
			skip_line = false;
		} else {
			new_lines.push(line);
		}
	}

	new_lines
}

#[cfg(test)]
mod tests {
	use super::*;
	use crate::csv::CsvParser;
	use std::io::Cursor;

	#[test]
	fn run_cell_test() {
		assert_eq!(
			run(
				&vec![String::from("A"), String::from("B"), String::from("C")],
				&OutputConfig::new(CsvParser::new(Cursor::new("A,B,C\n<cell1>,<cell3>,<cell2>\n"))),
			),
			vec![vec![String::from("A"), String::from("C"), String::from("B")]]
		);
	}

	#[test]
	fn run_value_test() {
		assert_eq!(
			run(
				&vec![String::from("A"), String::from("B"), String::from("C")],
				&OutputConfig {
					heading: String::new(),
					lines: vec![vec![
						Item::Value(String::from("NEW")),
						Item::Cell(2, None),
						Item::Cell(1, None)
					]],
				},
			),
			vec![vec![String::from("NEW"), String::from("C"), String::from("B")]]
		);
	}

	#[test]
	fn run_multiple_lines_test() {
		assert_eq!(
			run(
				&vec![String::from("A"), String::from("B"), String::from("C")],
				&OutputConfig {
					heading: String::new(),
					lines: vec![
						vec![Item::Cell(0, None), Item::Cell(2, None), Item::Cell(1, None)],
						vec![Item::Cell(0, None), Item::Cell(1, None), Item::Cell(2, None)],
					],
				},
			),
			vec![
				vec![String::from("A"), String::from("C"), String::from("B")],
				vec![String::from("A"), String::from("B"), String::from("C")],
			]
		);
	}

	#[test]
	fn run_everything_test() {
		assert_eq!(
			run(
				&vec![String::from("A"), String::from("B"), String::from("C")],
				&OutputConfig {
					heading: String::new(),
					lines: vec![
						vec![Item::Cell(0, None), Item::Cell(2, None), Item::Cell(1, None)],
						vec![
							Item::Cell(2, None),
							Item::Value(String::from("MERGE")),
							Item::Cell(2, None)
						],
						vec![
							Item::Cell(1, None),
							Item::Cell(0, None),
							Item::Value(String::from("NEW"))
						],
					],
				},
			),
			vec![
				vec![String::from("A"), String::from("C"), String::from("B")],
				vec![String::from("C"), String::from("MERGE"), String::from("C")],
				vec![String::from("B"), String::from("A"), String::from("NEW")],
			]
		);
	}

	#[test]
	fn skip_line_test() {
		assert_eq!(
			run(
				&vec![
					String::from("A"),
					String::from("B"),
					String::from("C"),
					String::from("D")
				],
				&OutputConfig::new(CsvParser::new(Cursor::new("Column A,Column B,Column C\n<cell1>,MERGE,<cell2>\n<cell1>,NEW,:IF <cell3> == 'D' ('SKIP_THIS_LINE') ELSE (<cell3>)\n<cell1>,NEW,:IF <cell4> == 'D' ('SKIP_THIS_LINE') ELSE (<cell4>)\n"))),
			),
			vec![
				vec![String::from("A"), String::from("MERGE"), String::from("B")],
				vec![String::from("A"), String::from("NEW"), String::from("C")],
			]
		);
	}
}