moros 0.12.0

MOROS: Obscure Rust Operating System
Documentation
use crate::api::console::Style;
use crate::api::fs;
use crate::api::process::ExitCode;

use core::cmp;
use alloc::string::String;
use alloc::vec::Vec;
use alloc::vec;

#[derive(Debug, PartialEq)]
enum Diff {
    Equal(String),
    Insert(String),
    Delete(String),
}

fn diff(old: &str, new: &str) -> Vec<Diff> {
    let old: Vec<&str> = old.lines().collect();
    let new: Vec<&str> = new.lines().collect();
    backtrack(&old, &new, &lcs(&old, &new))
}

fn lcs(old: &[&str], new: &[&str]) -> Vec<Vec<usize>> {
    let n = old.len();
    let m = new.len();
    let mut lcs = vec![vec![0; m + 1]; n + 1];

    for i in 0..n {
        for j in 0..m {
            if old[i] == new[j] {
                lcs[i + 1][j + 1] = lcs[i][j] + 1;
            } else {
                lcs[i + 1][j + 1] = cmp::max(lcs[i][j + 1], lcs[i + 1][j]);
            }
        }
    }

    lcs
}

fn backtrack(old: &[&str], new: &[&str], lcs: &[Vec<usize>]) -> Vec<Diff> {
    let mut diff = Vec::new();
    let mut i = old.len();
    let mut j = new.len();

    while i > 0 && j > 0 {
        if old[i - 1] == new[j - 1] {
            diff.push(Diff::Equal(old[i - 1].into()));
            i -= 1;
            j -= 1;
        } else if lcs[i][j - 1] > lcs[i - 1][j] {
            diff.push(Diff::Insert(new[j - 1].into()));
            j -= 1;
        } else {
            diff.push(Diff::Delete(old[i - 1].into()));
            i -= 1;
        }
    }

    while i > 0 {
        diff.push(Diff::Delete(old[i - 1].into()));
        i -= 1;
    }

    while j > 0 {
        diff.push(Diff::Insert(new[j - 1].into()));
        j -= 1;
    }

    diff.reverse();
    diff
}

pub fn main(args: &[&str]) -> Result<(), ExitCode> {
    for arg in args {
        if *arg == "-h" || *arg == "--help" {
            help();
            return Ok(());
        }
    }
    if args.len() != 3 {
        help();
        return Err(ExitCode::UsageError);
    }
    let csi_insert = Style::color("green");
    let csi_delete = Style::color("red");
    let csi_reset = Style::reset();
    if let Ok(old) = fs::read_to_string(args[1]) {
        if let Ok(new) = fs::read_to_string(args[2]) {
            for op in diff(&old, &new) {
                match op {
                    Diff::Equal(line) => {
                        println!(" {}", line);
                    }
                    Diff::Delete(line) => {
                        println!("{}-{}{}", csi_delete, line, csi_reset);
                    }
                    Diff::Insert(line) => {
                        println!("{}+{}{}", csi_insert, line, csi_reset);
                    }
                }
            }
        }
    }
    Ok(())
}

fn help() {
    let csi_option = Style::color("aqua");
    let csi_title = Style::color("yellow");
    let csi_reset = Style::reset();
    println!(
        "{}Usage:{} diff {}<path1> <path2>{}",
        csi_title, csi_reset, csi_option, csi_reset
    );
}

#[test_case]
fn test_diff() {
    assert_eq!(
        diff("a", "a"),
        vec![
            Diff::Equal("a".into())
        ]
    );
    assert_eq!(
        diff("a", "b"),
        vec![
            Diff::Insert("b".into()),
            Diff::Delete("a".into())
        ]
    );
    assert_eq!(
        diff("a\nb\nc\n", "a\nc\n"),
        vec![
            Diff::Equal("a".into()),
            Diff::Delete("b".into()),
            Diff::Equal("c".into())
        ]
    );
}