serde_hkx_features/
diff.rs1use crate::error::{Error, Result};
4use crate::fs::ReadExt as _;
5use serde_hkx::bytes::hexdump;
6use std::path::Path;
7use tokio::fs;
8
9pub async fn write_diff<I1, I2, O>(
23 old: I1,
24 new: I2,
25 output: Option<O>,
26 use_color: bool,
27) -> Result<()>
28where
29 I1: AsRef<Path>,
30 I2: AsRef<Path>,
31 O: AsRef<Path>,
32{
33 let old_str = read_any_to_string(old).await?;
34 let new_str = read_any_to_string(new).await?;
35
36 let diff_str = diff(old_str, new_str, use_color);
37 match output {
38 Some(output) => fs::write(output, &diff_str).await?,
39 None => print!("{diff_str}"),
40 };
41 Ok(())
42}
43
44async fn read_any_to_string<I>(path: I) -> Result<String>
51where
52 I: AsRef<Path>,
53{
54 let path = path.as_ref();
55 let ext = path.extension();
56
57 if let Some(ext) = ext {
58 if ext.eq_ignore_ascii_case("hkx") {
59 Ok(hexdump::to_string(path.read_bytes().await?))
60 } else {
61 path.read_any_string().await
62 }
63 } else {
64 Err(Error::MissingExtension {
65 path: path.to_path_buf(),
66 })
67 }
68}
69
70pub fn diff(old: impl AsRef<str>, new: impl AsRef<str>, color: bool) -> String {
74 let mut output_diff = String::new();
75
76 let diff = similar::TextDiff::from_lines(old.as_ref(), new.as_ref());
77 for change in diff.iter_all_changes() {
78 let (sign, end) = if color {
79 const DELETE: &str = "\u{1b}[31m-"; const INSERT: &str = "\u{1b}[32m+"; const RESET_COLOR: &str = "\u{1b}[39m";
82
83 output_diff.push_str(RESET_COLOR);
84
85 let sign = match change.tag() {
86 similar::ChangeTag::Delete => DELETE,
87 similar::ChangeTag::Insert => INSERT,
88 similar::ChangeTag::Equal => " ",
89 };
90 (sign, RESET_COLOR)
91 } else {
92 let sign = match change.tag() {
93 similar::ChangeTag::Delete => "-",
94 similar::ChangeTag::Insert => "+",
95 similar::ChangeTag::Equal => " ",
96 };
97 (sign, "")
98 };
99 output_diff += &format!("{sign}{change}{end}");
100 }
101 output_diff
102}