epitls-pi 1.5.2

A little helper tool meant to ease the developpment of the C piscine at EPITA/Toulouse.
use std::fs;

use termion::color;

use crate::{
	tasks::FormatTask,
	utils::{log_failure, log_process, log_success},
};

mod formatting;
mod testables;

pub fn main(files: Vec<String>) {
	for file in files {
		log_process(&format!("checking '{file}'"));
		check_formatting(file.to_string());
		let content = fs::read_to_string(&file).unwrap();
		for test in testables::tests() {
			if let Err(reason) = test.test(content.clone()) {
				let name = test.name();
				log_failure(&format!("'{file}': rule '{name}' fails:"));
				println!("{reason}");
			}
		}
	}
}

pub enum Diff {
	ToRemove { index: usize, content: String },
	ToAdd { index: usize, content: String },
	Keep { index: usize, content: String },
}

fn check_formatting(file: String) {
	let content = fs::read_to_string(&file).unwrap();
	let formatted = FormatTask::new(file.clone(), formatting::formatted_config()).run();
	let mut line_number = 0usize;
	let mut invalid = false;
	let differences = diff::lines(&content, &formatted)
		.into_iter()
		.map(|change| {
			match change {
				diff::Result::Left(_) | diff::Result::Both(_, _) => {
					line_number += 1;
				}
				_ => (),
			}
			(line_number, change)
		})
		.map(|(index, d)| match d {
			diff::Result::Left(content) => {
				invalid = true;
				Diff::ToRemove {
					index,
					content: content.into(),
				}
			}
			diff::Result::Both(content, _) => Diff::Keep {
				index,
				content: content.into(),
			},
			diff::Result::Right(content) => {
				invalid = true;
				Diff::ToAdd {
					index,
					content: content.into(),
				}
			}
		})
		.collect::<Vec<_>>();
	if invalid {
		log_failure(&format!("'{file}': invalid formatting:"));
		let red = color::Fg(color::Red);
		let green = color::Fg(color::Green);
		let reset = color::Fg(color::Reset);

		for difference in differences {
			match difference {
				Diff::ToRemove { index, content } => {
					println!("{red}{index:>3} -|{content}{reset}")
				}
				Diff::ToAdd { index, content } => {
					println!("{green}{index:>3} +|{content}{reset}")
				}
				Diff::Keep { index, content } => {
					println!("{index:>3}  |{content}")
				}
			}
		}
	}
}

pub fn format(files: Vec<String>) {
	for file in files {
		let mut formatted = FormatTask::new(file.clone(), formatting::formatted_config()).run();
		if !formatted.ends_with('\n') {
			formatted += "\n";
		}
		fs::write(&file, formatted).unwrap();
		log_success(&format!("formatted '{file}'"));
	}
}