diff --git a/bin/src/cli_args.rs b/bin/src/cli_args.rs index a5b224f..99dea85 100644 --- a/bin/src/cli_args.rs +++ b/bin/src/cli_args.rs @@ -3,9 +3,13 @@ use clap::Parser; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] pub struct CliArgs { + /// Runs only the diffing algorithm and outputs if the two files matches. + #[arg(short, long, num_args = 0)] + pub(crate) diff_only: bool, + /// Path to file in base revision - #[arg(short, long)] - pub(crate) base_path: std::path::PathBuf, + #[arg(short, long, requires_if("false", "diff_only"))] + pub(crate) base_path: Option, /// Path to file in left revision #[arg(short, long)] @@ -16,8 +20,8 @@ pub struct CliArgs { pub(crate) right_path: std::path::PathBuf, /// Path where the merged file should be written - #[arg(short, long)] - pub(crate) merge_path: std::path::PathBuf, + #[arg(short, long, requires_if("false", "diff_only"))] + pub(crate) merge_path: Option, /// The language that the files being merged are written in. /// If not provided the language will try to be inferred by the extension of the base file. diff --git a/bin/src/cli_exit_codes.rs b/bin/src/cli_exit_codes.rs index 82fa9fe..fd83e12 100644 --- a/bin/src/cli_exit_codes.rs +++ b/bin/src/cli_exit_codes.rs @@ -1,6 +1,9 @@ pub const SUCCESS_WITHOUT_CONFLICTS: i32 = 0; pub const SUCCESS_WITH_CONFLICTS: i32 = 1; +pub const SUCCESS_FILES_FULLY_MATCH: i32 = 0; +pub const SUCCESS_FILES_DO_NOT_FULLY_MATCH: i32 = 1; + pub const READING_FILE_ERROR: i32 = 129; pub const INVALID_LANGUAGE_ERROR: i32 = 130; pub const WRITING_FILE_ERROR: i32 = 131; diff --git a/bin/src/control.rs b/bin/src/control.rs index 600fee5..23c72c6 100644 --- a/bin/src/control.rs +++ b/bin/src/control.rs @@ -3,7 +3,7 @@ use std::{ fmt::{self, Display}, }; -use matching::matching_configuration; +use matching::{matching_configuration, MatchingEntry}; use parsing::ParserConfiguration; #[derive(Debug)] @@ -102,3 +102,31 @@ pub fn run_tool_on_merge_scenario( false => Ok(ExecutionResult::WithoutConflicts(result.to_string())), } } + +pub fn run_diff_on_files( + language: model::Language, + left: &str, + right: &str, +) -> Result { + let parser_configuration = ParserConfiguration::from(language); + + log::info!("Started parsing left file"); + let left_tree_root = + parsing::parse_string(left, &parser_configuration).map_err(ExecutionError::ParsingError)?; + log::info!("Finished parsing left file"); + log::info!("Started parsing right file"); + let right_tree_root = parsing::parse_string(right, &parser_configuration) + .map_err(ExecutionError::ParsingError)?; + log::info!("Finished parsing right file"); + + let matching_configuration = matching_configuration::MatchingConfiguration::from(language); + log::info!("Started calculation of matchings between left and right"); + let matchings_left_right = + matching::calculate_matchings(&left_tree_root, &right_tree_root, &matching_configuration); + log::info!("Finished calculation of matchings between left and right"); + + Ok(matchings_left_right + .get_matching_entry(&left_tree_root, &right_tree_root) + .unwrap_or_default() + .to_owned()) +} diff --git a/bin/src/lib.rs b/bin/src/lib.rs index 96a7e8b..8e285ae 100644 --- a/bin/src/lib.rs +++ b/bin/src/lib.rs @@ -2,4 +2,4 @@ mod cli_exit_codes; mod control; pub use cli_exit_codes::*; -pub use control::run_tool_on_merge_scenario; +pub use control::{run_diff_on_files, run_tool_on_merge_scenario}; diff --git a/bin/src/main.rs b/bin/src/main.rs index be48789..aba48e6 100644 --- a/bin/src/main.rs +++ b/bin/src/main.rs @@ -4,15 +4,27 @@ mod control; mod language; use clap::Parser; +use cli_args::CliArgs; fn main() { let args = cli_args::CliArgs::parse(); - env_logger::builder().filter_level(args.log_level.unwrap_or(log::LevelFilter::Info)).init(); + env_logger::builder() + .filter_level(args.log_level.unwrap_or(log::LevelFilter::Info)) + .init(); log::info!("Starting Generic Merge tool execution"); log::debug!("Parsed arguments: {:?}", args); - let base = std::fs::read_to_string(&args.base_path).unwrap_or_else(|error| { + match !args.diff_only { + true => run_merge(args), + false => run_diff(args), + } +} + +fn run_merge(args: CliArgs) { + let base_path = args.base_path.unwrap(); + + let base = std::fs::read_to_string(&base_path).unwrap_or_else(|error| { log::error!("Error while reading base file: {}", error); std::process::exit(cli_exit_codes::READING_FILE_ERROR) }); @@ -27,7 +39,7 @@ fn main() { let language = match args.language { Some(language) => language::get_language_from_name(&language), - None => language::get_language_by_file_path(&args.base_path), + None => language::get_language_by_file_path(&base_path), } .unwrap_or_else(|error| { log::error!("Error while retrieving language configuration: {}", error); @@ -40,7 +52,7 @@ fn main() { std::process::exit(cli_exit_codes::INTERNAL_EXECUTION_ERROR) }); - std::fs::write(args.merge_path, result.to_string()).unwrap_or_else(|error| { + std::fs::write(args.merge_path.unwrap(), result.to_string()).unwrap_or_else(|error| { log::error!("Error while writing output file: {}", error); std::process::exit(cli_exit_codes::WRITING_FILE_ERROR) }); @@ -56,3 +68,40 @@ fn main() { } } } + +fn run_diff(args: CliArgs) { + let left = std::fs::read_to_string(&args.left_path).unwrap_or_else(|error| { + log::error!("Error while reading left file: {}", error); + std::process::exit(cli_exit_codes::READING_FILE_ERROR) + }); + let right = std::fs::read_to_string(&args.right_path).unwrap_or_else(|error| { + log::error!("Error while reading right file: {}", error); + std::process::exit(cli_exit_codes::READING_FILE_ERROR) + }); + + let language = match args.language { + Some(language) => language::get_language_from_name(&language), + None => language::get_language_by_file_path(&args.left_path), + } + .unwrap_or_else(|error| { + log::error!("Error while retrieving language configuration: {}", error); + std::process::exit(cli_exit_codes::INVALID_LANGUAGE_ERROR) + }); + + let result = control::run_diff_on_files(language, &left, &right).unwrap_or_else(|error| { + log::error!("Error while running tool: {}", error); + std::process::exit(cli_exit_codes::INTERNAL_EXECUTION_ERROR) + }); + + log::info!("{:?}", result); + match result.is_perfect_match { + true => { + log::info!("Both files are equivalent"); + std::process::exit(cli_exit_codes::SUCCESS_FILES_FULLY_MATCH) + } + false => { + log::info!("Both files are different"); + std::process::exit(cli_exit_codes::SUCCESS_FILES_DO_NOT_FULLY_MATCH) + } + } +} diff --git a/bin/tests/cli.rs b/bin/tests/cli.rs index f66f39c..edae8f1 100644 --- a/bin/tests/cli.rs +++ b/bin/tests/cli.rs @@ -25,3 +25,25 @@ fn if_there_is_no_conflict_it_returns_valid_exit_code() { .assert() .code(bin::SUCCESS_WITHOUT_CONFLICTS); } + +#[test] +fn if_i_am_running_on_diff_mode_and_files_fully_match_it_returns_zero() { + let mut cmd = Command::cargo_bin("generic-merge").unwrap(); + cmd.arg("--diff-only") + .arg("--left-path=tests/diff_scenarios/java_files_full_match/left.java") + .arg("--right-path=tests/diff_scenarios/java_files_full_match/right.java") + .arg("--language=java") + .assert() + .code(bin::SUCCESS_FILES_FULLY_MATCH); +} + +#[test] +fn if_i_am_running_on_diff_mode_and_files_do_not_fully_match_it_returns_one() { + let mut cmd = Command::cargo_bin("generic-merge").unwrap(); + cmd.arg("--diff-only") + .arg("--left-path=tests/diff_scenarios/java_files_not_fully_matching/left.java") + .arg("--right-path=tests/diff_scenarios/java_files_not_fully_matching/right.java") + .arg("--language=java") + .assert() + .code(bin::SUCCESS_FILES_DO_NOT_FULLY_MATCH); +} diff --git a/bin/tests/diff_scenarios/java_files_full_match/left.java b/bin/tests/diff_scenarios/java_files_full_match/left.java new file mode 100644 index 0000000..d945670 --- /dev/null +++ b/bin/tests/diff_scenarios/java_files_full_match/left.java @@ -0,0 +1,11 @@ +public class Main { + static { + int x = 2; + } + + public static void main() { + int a = 0; + } + + public static void teste() {} +} diff --git a/bin/tests/diff_scenarios/java_files_full_match/right.java b/bin/tests/diff_scenarios/java_files_full_match/right.java new file mode 100644 index 0000000..16f477a --- /dev/null +++ b/bin/tests/diff_scenarios/java_files_full_match/right.java @@ -0,0 +1,11 @@ +public class Main { + static { + int x = 2; + } + + public static void teste() {} + + public static void main() { + int a = 0; + } +} diff --git a/bin/tests/diff_scenarios/java_files_not_fully_matching/left.java b/bin/tests/diff_scenarios/java_files_not_fully_matching/left.java new file mode 100644 index 0000000..88402ec --- /dev/null +++ b/bin/tests/diff_scenarios/java_files_not_fully_matching/left.java @@ -0,0 +1,11 @@ +public class Main { + static { + int x = 2; + } + + public static void main() { + int a = 2; + } + + public static void teste() {} +} diff --git a/bin/tests/diff_scenarios/java_files_not_fully_matching/right.java b/bin/tests/diff_scenarios/java_files_not_fully_matching/right.java new file mode 100644 index 0000000..16f477a --- /dev/null +++ b/bin/tests/diff_scenarios/java_files_not_fully_matching/right.java @@ -0,0 +1,11 @@ +public class Main { + static { + int x = 2; + } + + public static void teste() {} + + public static void main() { + int a = 0; + } +}