Skip to main content

differential_engine/
model.rs

1//! Data model for the canonical diff view.
2//!
3//! Paths are raw bytes throughout the engine; they become UTF-8 strings only at
4//! JSON serialisation time, where a non-UTF-8 path is a hard error naming the
5//! file (deferred support, never silent).
6
7/// One line's content, without its newline. `Vec<Vec<u8>>` splits preserve the
8/// trailing empty element that encodes "ends with a newline".
9pub type Line = Vec<u8>;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum Disposition {
13    Added,
14    Deleted,
15    Modified,
16}
17
18impl Disposition {
19    pub fn letter(self) -> u8 {
20        match self {
21            Disposition::Added => b'A',
22            Disposition::Deleted => b'D',
23            Disposition::Modified => b'M',
24        }
25    }
26}
27
28/// One changed file in the canonical (`--no-renames`) view. Exists independently
29/// of hunks: empty-file adds/deletes, mode-only changes and binary files carry
30/// zero hunks but are still real changes.
31#[derive(Debug, Clone)]
32pub struct FileChange {
33    pub path: Vec<u8>,
34    pub disposition: Disposition,
35    /// "100644" etc. New-side mode; `None` for deletions.
36    pub new_mode: Option<String>,
37    /// Old-side mode when it differs, and for deletions.
38    pub old_mode: Option<String>,
39    pub binary: bool,
40    /// `Some((old, new))` commit ids for gitlink (mode 160000) changes.
41    pub submodule: Option<(Option<String>, Option<String>)>,
42    /// Full blob oids from the `--raw --full-index` record. `new_oid` is used to
43    /// stage BINARY files only — for text files the tree is always built from
44    /// applied hunks, or the tree assertion would be tautological.
45    pub old_oid: Option<String>,
46    pub new_oid: Option<String>,
47    /// Indices into the canonical hunk vector, in file order.
48    pub hunks: Vec<usize>,
49    // Annotations merged in from the rename-detected view:
50    pub rename_similarity: Option<u8>,
51    /// A side of a rename: where the content came from.
52    pub rename_from: Option<Vec<u8>>,
53    /// D side of a rename: where the content went.
54    pub rename_to: Option<Vec<u8>>,
55    /// Generated-file hint (never affects enumeration).
56    pub generated: Option<GeneratedBy>,
57}
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60pub enum GeneratedBy {
61    Builtin,
62    Attr,
63    Config,
64}
65
66/// One canonical hunk from `git diff -U0 --no-renames`.
67#[derive(Debug, Clone)]
68pub struct Hunk {
69    /// Index into the canonical file vector.
70    pub file: usize,
71    pub old_start: u32,
72    pub old_count: u32,
73    pub new_start: u32,
74    pub new_count: u32,
75    /// Removed lines, without prefix or newline.
76    pub removed: Vec<Line>,
77    /// Added lines, without prefix or newline.
78    pub added: Vec<Line>,
79    /// `\ No newline at end of file` on the old side.
80    pub nonl_old: bool,
81    /// `\ No newline at end of file` on the new side.
82    pub nonl_new: bool,
83}
84
85/// The canonical enumeration: every file, every hunk, no exclusions.
86#[derive(Debug, Clone, Default)]
87pub struct DiffView {
88    pub files: Vec<FileChange>,
89    /// Canonical order: file order in the diff, hunk order within each file.
90    /// A hunk's id `hN` is its index here.
91    pub hunks: Vec<Hunk>,
92}
93
94impl DiffView {
95    pub fn file_of(&self, hunk: &Hunk) -> &FileChange {
96        &self.files[hunk.file]
97    }
98}