Diff a string for presentation to a user in the terminal
Find a file
Solace System Renovate Fox 033015f169
Some checks failed
ci/woodpecker/push/lint/3 Pipeline was successful
ci/woodpecker/push/lint/1 Pipeline was successful
ci/woodpecker/push/lint/2 Pipeline was successful
ci/woodpecker/push/fix unknown status
ci/woodpecker/push/test/1 Pipeline was successful
ci/woodpecker/push/test/2 Pipeline was successful
ci/woodpecker/push/test/3 Pipeline was successful
ci/woodpecker/push/release Pipeline was successful
Merge pull request 'chore(deps): update rust docker digest to 087fe68' (#46) from renovate/rust into main
2025-11-06 00:14:32 +01:00
.cargo chore: add mutants.toml configuration for test mutation exclusions 2025-05-05 15:53:40 +02:00
.woodpecker chore(deps): update rust docker digest to 087fe68 2025-11-05 21:04:49 +00:00
benches refactor(src): move tests to dedicated directory and add benchmarking 2025-05-04 20:50:06 +02:00
src chore: Aktualisiere similar-Bibliothek und entferne pedantic Clippy-Lint 2025-08-27 08:20:25 +02:00
.fastconventional.yaml ci: refactor workflow and configuration files for improved readability 2025-10-05 09:36:46 +02:00
.gitignore refactor(src): move tests to dedicated directory and add benchmarking 2025-05-04 20:50:06 +02:00
Cargo.toml chore(version): v4.1.1 2025-10-08 06:23:21 +00:00
CHANGELOG.md chore(version): v4.1.1 2025-10-08 06:23:21 +00:00
CODE_OF_CONDUCT.md refactor: Markdown formatting 2023-01-20 21:53:32 +01:00
cog.toml chore: remove post-bump git push hooks from cog.toml 2025-10-07 09:46:47 +02:00
CONVENTIONS.md docs: Add project conventions and update diff rendering style 2025-05-05 15:53:40 +02:00
demo_arrows.png docs: Update the images 2021-10-24 01:09:16 +02:00
demo_signs.png docs: Update the images 2021-10-24 01:09:16 +02:00
Justfile ci: add Concourse pipeline configuration command 2025-10-05 09:25:27 +02:00
LICENSE.md feat: Initial Commit 2021-10-22 00:26:16 +02:00
README.md docs(src): Format the examples a little nicer in the README 2022-03-06 22:42:25 +01:00
renovate.json refactor: update Renovate configuration to use library preset 2025-05-15 22:38:10 +02:00

termdiff

Diff a string for presentation to a user in the terminal.

Usage

use termdiff::{SignsTheme, DrawDiff};
let old = "The quick brown fox and\njumps over the sleepy dog";
let new = "The quick red fox and\njumps over the lazy dog";
let theme = SignsTheme::default();
let actual = format!("{}", DrawDiff::new(old, new, &theme));

assert_eq!(
    actual,
    "--- remove | insert +++
-The quick brown fox and
-jumps over the sleepy dog
+The quick red fox and
+jumps over the lazy dog
"
);

Alternatively you can use this interface

use termdiff::{ArrowsTheme, diff};
let old = "The quick brown fox and\njumps over the sleepy dog";
let new = "The quick red fox and\njumps over the lazy dog";
let theme = ArrowsTheme::default();
let mut buffer: Vec<u8> = Vec::new();
diff(&mut buffer, old, new, &theme).unwrap();
let actual: String = String::from_utf8(buffer).expect("Not valid UTF-8");

assert_eq!(
    actual,
    "< left / > right
<The quick brown fox and
<jumps over the sleepy dog
>The quick red fox and
>jumps over the lazy dog
"
);

Read more at Docs.rs

Themes

We have a limited number of built in themes

Arrows

Demo of the arrows
format

Signs

Demo of the signs format

Custom

use termdiff::DrawDiff;
use termdiff::Theme;
use crossterm::style::Stylize;
use std::borrow::Cow;

#[derive(Debug)]
struct MyTheme {}
impl Theme for MyTheme {
    fn highlight_insert<'this>(&self, input: &'this str) -> Cow<'this, str> {
        input.into()
    }

    fn highlight_delete<'this>(&self, input: &'this str) -> Cow<'this, str> {
        input.into()
    }

    fn equal_content<'this>(&self, input: &'this str) -> Cow<'this, str> {
        input.into()
    }

    fn delete_content<'this>(&self, input: &'this str) -> Cow<'this, str> {
        input.into()
    }

    fn equal_prefix<'this>(&self) -> Cow<'this, str> {
        "=".into()
    }

    fn delete_prefix<'this>(&self) -> Cow<'this, str> {
        "!".into()
    }

    fn insert_line<'this>(&self, input: &'this str) -> Cow<'this, str> {
        input.into()
    }

    fn insert_prefix<'this>(&self) -> Cow<'this, str> {
        "|".into()
    }

    fn line_end<'this>(&self) -> Cow<'this, str> {
        "\n".into()
    }

    fn header<'this>(&self) -> Cow<'this, str> {
        format!("{}\n", "Header").into()
    }
}

let my_theme = MyTheme {};

let old = "The quick brown fox and\njumps over the sleepy dog";
let new = "The quick red fox and\njumps over the lazy dog";
let actual = format!("{}", DrawDiff::new(old, new, &my_theme));

assert_eq!(
    actual,
    "Header
!The quick brown fox and
!jumps over the sleepy dog
|The quick red fox and
|jumps over the lazy dog
"
);