finished the thing
This commit is contained in:
parent
bfdcc1ebef
commit
b671e391db
45
.vscode/launch.json
vendored
Normal file
45
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug executable 'diff-tool'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"--bin=diff-tool",
|
||||||
|
"--package=diff-tool"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "diff-tool",
|
||||||
|
"kind": "bin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": ["--", "res/file1.txt", "res/file2.txt"],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug unit tests in executable 'diff-tool'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--bin=diff-tool",
|
||||||
|
"--package=diff-tool"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "diff-tool",
|
||||||
|
"kind": "bin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
18
Cargo.lock
generated
18
Cargo.lock
generated
|
@ -54,12 +54,24 @@ dependencies = [
|
||||||
"os_str_bytes",
|
"os_str_bytes",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colored"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"lazy_static",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "diff-tool"
|
name = "diff-tool"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
|
"colored",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -77,6 +89,12 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.137"
|
version = "0.2.137"
|
||||||
|
|
|
@ -8,3 +8,4 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "4.0.26"
|
clap = "4.0.26"
|
||||||
clap_derive = "4.0.21"
|
clap_derive = "4.0.21"
|
||||||
|
colored = "2.0.0"
|
||||||
|
|
1
res/file1.txt
Normal file
1
res/file1.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
This is not a text file
|
1
res/file2.txt
Normal file
1
res/file2.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
This is a text file tho
|
135
src/diff.rs
135
src/diff.rs
|
@ -1,6 +1,6 @@
|
||||||
use std::{fs::File, io::{BufReader, Read}};
|
use std::{fs::File, io::{BufReader, Read}, fmt::Display};
|
||||||
|
|
||||||
use crate::grid::Grid;
|
use crate::grid::Grid;
|
||||||
|
use colored::*;
|
||||||
|
|
||||||
macro_rules! safe_unwrap {
|
macro_rules! safe_unwrap {
|
||||||
($expression: expr) => {
|
($expression: expr) => {
|
||||||
|
@ -11,13 +11,32 @@ macro_rules! safe_unwrap {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
#[derive(Clone)]
|
||||||
enum Arrow {
|
enum Arrow {
|
||||||
#[default] LEFT,
|
NONE,
|
||||||
|
LEFT,
|
||||||
UP,
|
UP,
|
||||||
DIAGONAL
|
DIAGONAL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for Arrow {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Arrow::NONE => write!(f, " "),
|
||||||
|
Arrow::LEFT => write!(f, "<-"),
|
||||||
|
Arrow::UP => write!(f, "^^"),
|
||||||
|
Arrow::DIAGONAL => write!(f, "<^")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Action {
|
||||||
|
INSERT(u32, char),
|
||||||
|
DELETE(u32),
|
||||||
|
REPLACE(usize, char)
|
||||||
|
}
|
||||||
|
|
||||||
fn open_and_read(file: String) -> Result<String, std::io::Error> {
|
fn open_and_read(file: String) -> Result<String, std::io::Error> {
|
||||||
let fp = File::open(file)?;
|
let fp = File::open(file)?;
|
||||||
let mut buf_reader = BufReader::new(fp);
|
let mut buf_reader = BufReader::new(fp);
|
||||||
|
@ -27,9 +46,21 @@ fn open_and_read(file: String) -> Result<String, std::io::Error> {
|
||||||
Ok(content)
|
Ok(content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Cell(usize, Arrow);
|
||||||
|
|
||||||
|
impl Display for Cell {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "({},{})", self.0, self.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Diff {
|
pub struct Diff {
|
||||||
first: String,
|
first: String,
|
||||||
second: String,
|
second: String,
|
||||||
|
|
||||||
|
actions: Vec<Action>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Diff {
|
impl Diff {
|
||||||
|
@ -39,15 +70,105 @@ impl Diff {
|
||||||
|
|
||||||
let mut diff = Diff {
|
let mut diff = Diff {
|
||||||
first: first_string,
|
first: first_string,
|
||||||
second: second_string
|
second: second_string,
|
||||||
|
|
||||||
|
actions: vec![]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let grid = diff.create_lcs();
|
||||||
|
|
||||||
|
let mut pos = (diff.first.len() as u32, diff.second.len() as u32);
|
||||||
|
while pos != (0, 0) {
|
||||||
|
pos = match grid[pos].1 {
|
||||||
|
Arrow::DIAGONAL => (pos.0 - 1, pos.1 - 1),
|
||||||
|
Arrow::UP => {
|
||||||
|
diff.actions.push(Action::INSERT(pos.0, diff.second.chars().nth(pos.1 as usize - 1).unwrap()));
|
||||||
|
(pos.0, pos.1 - 1)
|
||||||
|
},
|
||||||
|
Arrow::LEFT => {
|
||||||
|
diff.actions.push(Action::DELETE(pos.0));
|
||||||
|
(pos.0 - 1, pos.1)
|
||||||
|
},
|
||||||
|
Arrow::NONE => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(diff)
|
Ok(diff)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_lcs(&self) -> Grid<Arrow> {
|
pub fn get_input(&self) -> (&String, &String) {
|
||||||
Grid::new(self.first.len() as u32, self.second.len() as u32)
|
(&self.first, &self.second)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn step_by_step(&self) -> Vec<String> {
|
||||||
|
let mut progression = vec![self.first.to_owned()];
|
||||||
|
|
||||||
|
for action in &self.actions {
|
||||||
|
let mut text = progression.last().unwrap().to_owned();
|
||||||
|
|
||||||
|
match action {
|
||||||
|
Action::INSERT(pos, chr) => { text.insert(*pos as usize, *chr); },
|
||||||
|
Action::DELETE(pos) => { text.remove(*pos as usize - 1); },
|
||||||
|
Action::REPLACE(_, _) => unimplemented!()
|
||||||
|
};
|
||||||
|
|
||||||
|
progression.push(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
progression
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_lcs(&self) -> Grid<Cell> {
|
||||||
|
let mut grid = Grid::new(
|
||||||
|
self.first.len() as u32 + 1,
|
||||||
|
self.second.len() as u32 + 1,
|
||||||
|
Cell(0, Arrow::NONE)
|
||||||
|
);
|
||||||
|
|
||||||
|
self.first.chars().enumerate().for_each(|(i, _)| grid[(i as u32 + 1, 0u32)] = Cell(0, Arrow::LEFT));
|
||||||
|
self.second.chars().enumerate().for_each(|(i, _)| grid[(0u32, i as u32 + 1)] = Cell(0, Arrow::UP));
|
||||||
|
|
||||||
|
grid[(0, 0)] = Cell(0, Arrow::NONE);
|
||||||
|
|
||||||
|
for (i, x) in self.first.chars().enumerate().map(|(i, c)| (i as u32 + 1, c)) {
|
||||||
|
for (j, y) in self.second.chars().enumerate().map(|(i, c)| (i as u32 + 1, c)) {
|
||||||
|
|
||||||
|
if x == y {
|
||||||
|
grid[(i, j)] = Cell(grid[(i - 1, j - 1)].0 + 1, Arrow::DIAGONAL);
|
||||||
|
} else {
|
||||||
|
let left = grid[(i - 1, j)].0;
|
||||||
|
let up = grid[(i, j - 1)].0;
|
||||||
|
|
||||||
|
grid[(i, j)] = match up >= left {
|
||||||
|
true => Cell(up, Arrow::UP),
|
||||||
|
false => Cell(left, Arrow::LEFT)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
grid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Diff {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let mut chars = self.first.chars().map(|c| c.to_string().normal()).collect::<Vec<ColoredString>>();
|
||||||
|
|
||||||
|
for action in &self.actions {
|
||||||
|
match action {
|
||||||
|
Action::INSERT(pos, chr) => { chars.insert(*pos as usize, chr.to_string().green()) },
|
||||||
|
Action::DELETE(pos) => { chars[*pos as usize - 1] = chars[*pos as usize - 1].to_owned().red().strikethrough() },
|
||||||
|
Action::REPLACE(_, _) => unimplemented!()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for chr in chars {
|
||||||
|
write!(f, "{chr}")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
37
src/grid.rs
37
src/grid.rs
|
@ -1,33 +1,46 @@
|
||||||
use std::ops;
|
use std::{ops, fmt::Display};
|
||||||
|
|
||||||
pub struct Grid<T: Default + Clone> {
|
pub struct Grid<T: Clone> {
|
||||||
width: usize,
|
width: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
|
|
||||||
buf: Vec<T>
|
buf: Vec<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Default + Clone> Grid<T> {
|
impl<T: Clone> Grid<T> {
|
||||||
pub fn new(width: u32, height: u32) -> Grid<T> {
|
pub fn new(width: u32, height: u32, default: T) -> Grid<T> {
|
||||||
Grid {
|
Grid {
|
||||||
width: width as usize,
|
width: width as usize,
|
||||||
height: height as usize,
|
height: height as usize,
|
||||||
|
|
||||||
buf: vec![T::default(); width as usize * height as usize]
|
buf: vec![default; width as usize * height as usize]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Default + Clone> ops::Index<(u32, u32)> for Grid<T> {
|
impl<T: Clone + Display> Display for Grid<T> {
|
||||||
type Output = T;
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
for y in 0..self.height {
|
||||||
fn index(&self, index: (u32, u32)) -> &Self::Output {
|
for x in 0..self.width {
|
||||||
&self.buf[index.0 as usize * self.width + index.1 as usize]
|
write!(f, "{}", self[(x as u32, y as u32)])?;
|
||||||
|
}
|
||||||
|
writeln!(f, "")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Default + Clone> ops::IndexMut<(u32, u32)> for Grid<T> {
|
impl<T: Clone> ops::Index<(u32, u32)> for Grid<T> {
|
||||||
|
type Output = T;
|
||||||
|
|
||||||
|
fn index(&self, index: (u32, u32)) -> &Self::Output {
|
||||||
|
&self.buf[index.1 as usize * self.width + index.0 as usize]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone> ops::IndexMut<(u32, u32)> for Grid<T> {
|
||||||
fn index_mut(&mut self, index: (u32, u32)) -> &mut Self::Output {
|
fn index_mut(&mut self, index: (u32, u32)) -> &mut Self::Output {
|
||||||
&mut self.buf[index.0 as usize * self.width + index.1 as usize]
|
&mut self.buf[index.1 as usize * self.width + index.0 as usize]
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,6 +5,7 @@ use clap::Parser;
|
||||||
use clap_derive::Parser;
|
use clap_derive::Parser;
|
||||||
use diff::Diff;
|
use diff::Diff;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
struct Args {
|
struct Args {
|
||||||
first: String,
|
first: String,
|
||||||
|
@ -21,4 +22,9 @@ fn main() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let inputs = diff.get_input();
|
||||||
|
println!("First: {}\nSecond: {}\n", inputs.0, inputs.1);
|
||||||
|
|
||||||
|
println!("{diff}");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue