diffy_imara/lib.rs
1//! Tools for finding and manipulating differences between files
2//!
3//! ## Overview
4//!
5//! This library is a fork of [`diffy`](https://docs.rs/diffy) with the backend of
6//! [`imara-diff`](https://docs.rs/imara-diff).
7//! This is done so that all the tools provided by it can use either
8//! [Myers' diff algorithm] or [Histogram diff algorithm].
9//!
10//! This library is intended to be a collection of tools used to find and
11//! manipulate differences between files inspired by [LibXDiff] and [GNU
12//! Diffutils]. Version control systems like [Git] and [Mercurial] generally
13//! communicate differences between two versions of a file using a `diff` or
14//! `patch`.
15//!
16//!
17//! The documentation generally refers to "files" in many places but none of
18//! the apis explicitly operate on on-disk files. Instead this library
19//! requires that the text being operated on resides in-memory and as such if
20//! you want to perform operations on files, it is up to the user to load the
21//! contents of those files into memory before passing their contents to the
22//! apis provided by this library.
23//!
24//! ## UTF-8 and Non-UTF-8
25//!
26//! This library has support for working with both utf8 and non-utf8 texts.
27//! Most of the API's have two different variants, one for working with utf8
28//! `str` texts (e.g. [`create_patch`]) and one for working with bytes `[u8]`
29//! which may or may not be utf8 (e.g. [`create_patch_bytes`]).
30//!
31//! ## Creating a Patch
32//!
33//! A [`Patch`] between two texts can be created by doing the following:
34//!
35//! ```
36//! use diffy_imara::create_patch;
37//!
38//! let original = "The Way of Kings\nWords of Radiance\n";
39//! let modified = "The Way of Kings\nWords of Radiance\nOathbringer\n";
40//!
41//! let patch = create_patch(original, modified);
42//! #
43//! # let expected = "\
44//! # --- original
45//! # +++ modified
46//! # @@ -1,2 +1,3 @@
47//! # The Way of Kings
48//! # Words of Radiance
49//! # +Oathbringer
50//! # ";
51//! #
52//! # assert_eq!(patch.to_string(), expected);
53//! ```
54//!
55//! A [`Patch`] can the be output in the [Unified Format] either by using its
56//! [`Display`] impl or by using a [`PatchFormatter`] to output the diff with
57//! color.
58//!
59//! ```
60//! # use diffy_imara::create_patch;
61//! #
62//! # let original = "The Way of Kings\nWords of Radiance\n";
63//! # let modified = "The Way of Kings\nWords of Radiance\nOathbringer\n";
64//! #
65//! # let patch = create_patch(original, modified);
66//! #
67//! # let expected = "\
68//! # --- original
69//! # +++ modified
70//! # @@ -1,2 +1,3 @@
71//! # The Way of Kings
72//! # Words of Radiance
73//! # +Oathbringer
74//! # ";
75//! #
76//! # assert_eq!(patch.to_string(), expected);
77//! #
78//! // Without color
79//! print!("{}", patch);
80//!
81//! // With color
82//! # use diffy_imara::PatchFormatter;
83//! let f = PatchFormatter::new().with_color();
84//! print!("{}", f.fmt_patch(&patch));
85//! ```
86//!
87//! ```console
88//! --- original
89//! +++ modified
90//! @@ -1,2 +1,3 @@
91//! The Way of Kings
92//! Words of Radiance
93//! +Oathbringer
94//! ```
95//!
96//! ## Applying a Patch
97//!
98//! Once you have a [`Patch`] you can apply it to a base image in order to
99//! recover the new text. Each hunk will be applied to the base image in
100//! sequence. Similarly to GNU `patch`, this implementation can detect when
101//! line numbers specified in the patch are incorrect and will attempt to find
102//! the correct place to apply each hunk by iterating forward and backward
103//! from the given position until all context lines from a hunk match the base
104//! image.
105//!
106//! ```
107//! use diffy_imara::{apply, Patch};
108//!
109//! let s = "\
110//! --- a/skybreaker-ideals
111//! +++ b/skybreaker-ideals
112//! @@ -10,6 +10,8 @@
113//! First:
114//! Life before death,
115//! strength before weakness,
116//! journey before destination.
117//! Second:
118//! - I will put the law before all else.
119//! + I swear to seek justice,
120//! + to let it guide me,
121//! + until I find a more perfect Ideal.
122//! ";
123//!
124//! let patch = Patch::from_str(s).unwrap();
125//!
126//! let base_image = "\
127//! First:
128//! Life before death,
129//! strength before weakness,
130//! journey before destination.
131//! Second:
132//! I will put the law before all else.
133//! ";
134//!
135//! let expected = "\
136//! First:
137//! Life before death,
138//! strength before weakness,
139//! journey before destination.
140//! Second:
141//! I swear to seek justice,
142//! to let it guide me,
143//! until I find a more perfect Ideal.
144//! ";
145//!
146//! assert_eq!(apply(base_image, &patch).unwrap(), expected);
147//! ```
148//!
149//! ## Performing a Three-way Merge
150//!
151//! Two files `A` and `B` can be merged together given a common ancestor or
152//! original file `O` to produce a file `C` similarly to how [diff3]
153//! performs a three-way merge.
154//!
155//! ```console
156//! --- A ---
157//! / \
158//! / \
159//! O C
160//! \ /
161//! \ /
162//! --- B ---
163//! ```
164//!
165//! If files `A` and `B` modified different regions of the original file `O`
166//! (or the same region in the same way) then the files can be merged without
167//! conflict.
168//!
169//! ```
170//! use diffy_imara::merge;
171//!
172//! let original = "the final empire\nThe Well of Ascension\nThe hero of ages\n";
173//! let a = "The Final Empire\nThe Well of Ascension\nThe Hero of Ages\n";
174//! let b = "The Final Empire\nThe Well of Ascension\nThe hero of ages\n";
175//! let expected = "\
176//! The Final Empire
177//! The Well of Ascension
178//! The Hero of Ages
179//! ";
180//!
181//! assert_eq!(merge(original, a, b).unwrap(), expected);
182//! ```
183//!
184//! If both files `A` and `B` modified the same region of the original file
185//! `O` (and those modifications are different), it would result in a conflict
186//! as it is not clear which modifications should be used in the merged
187//! result.
188//!
189//! ```
190//! use diffy_imara::merge;
191//!
192//! let original = "The Final Empire\nThe Well of Ascension\nThe hero of ages\n";
193//! let a = "The Final Empire\nThe Well of Ascension\nThe Hero of Ages\nSecret History\n";
194//! let b = "The Final Empire\nThe Well of Ascension\nThe hero of ages\nThe Alloy of Law\n";
195//! let expected = "\
196//! The Final Empire
197//! The Well of Ascension
198//! <<<<<<< ours
199//! The Hero of Ages
200//! Secret History
201//! ||||||| original
202//! The hero of ages
203//! =======
204//! The hero of ages
205//! The Alloy of Law
206//! >>>>>>> theirs
207//! ";
208//!
209//! assert_eq!(merge(original, a, b).unwrap_err(), expected);
210//! ```
211//!
212//! ## Choosing the diff algorithm
213//!
214//! In addition to free-standing functions [`create_patch`] and [`merge()`], this library provides methods
215//! [`DiffOptions::create_patch`] and [`MergeOptions::merge`], with both structs having an `algorithm` field.
216//! ```
217//! # use diffy_imara::{Algorithm, DiffOptions};
218//! #
219//! # let original = "The Way of Kings\nWords of Radiance\n";
220//! # let modified = "The Way of Kings\nWords of Radiance\nOathbringer\n";
221//! let patch = DiffOptions::new()
222//! .set_algorithm(Algorithm::Myers)
223//! .create_patch(original, modified);
224//! ```
225//!
226//! [LibXDiff]: http://www.xmailserver.org/xdiff-lib.html
227//! [Myers' diff algorithm]: http://www.xmailserver.org/diff2.pdf
228//! [Histogram diff algorithm]: https://github.com/eclipse-jgit/jgit/blob/a1c3a818b739ee2bedfda1c69cdac23989f1e97f/org.eclipse.jgit/src/org/eclipse/jgit/diff/HistogramDiff.java
229//! [GNU Diffutils]: https://www.gnu.org/software/diffutils/
230//! [Git]: https://git-scm.com/
231//! [Mercurial]: https://www.mercurial-scm.org/
232//! [Unified Format]: https://en.wikipedia.org/wiki/Diff#Unified_format
233//! [diff3]: https://en.wikipedia.org/wiki/Diff3
234//!
235//! [`Display`]: https://doc.rust-lang.org/stable/std/fmt/trait.Display.html
236//! [`Patch`]: struct.Patch.html
237//! [`PatchFormatter`]: struct.PatchFormatter.html
238//! [`create_patch`]: fn.create_patch.html
239//! [`create_patch_bytes`]: fn.create_patch_bytes.html
240
241mod apply;
242mod diff;
243mod merge;
244mod patch;
245mod range;
246mod sink;
247mod utils;
248
249pub use apply::{apply, apply_bytes, ApplyError};
250pub use diff::{create_patch, create_patch_bytes, DiffOptions};
251pub use imara_diff::Algorithm;
252pub use merge::{merge, merge_bytes, ConflictStyle, MergeOptions};
253pub use patch::{Hunk, HunkRange, Line, ParsePatchError, Patch, PatchFormatter};