Skip to main content

clankerdiff_core/models/
patch_line.rs

1use super::DiffSide;
2use clankerdiff_theme::DiffTone;
3use serde::{Deserialize, Serialize};
4use std::sync::Arc;
5
6/// A line's semantic patch kind.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8pub enum PatchLineKind {
9    HunkHeader,
10    Context,
11    Added,
12    Removed,
13    Meta,
14}
15
16impl PatchLineKind {
17    #[must_use]
18    pub const fn as_str(self) -> &'static str {
19        match self {
20            Self::HunkHeader => "HunkHeader",
21            Self::Context => "Context",
22            Self::Added => "Added",
23            Self::Removed => "Removed",
24            Self::Meta => "Meta",
25        }
26    }
27
28    #[must_use]
29    pub const fn sides(self) -> &'static [DiffSide] {
30        match self {
31            Self::Removed => &[DiffSide::Old],
32            Self::Added => &[DiffSide::New],
33            _ => &[DiffSide::Old, DiffSide::New],
34        }
35    }
36
37    /// Semantic tint used when presenting a line of this kind.
38    #[must_use]
39    pub const fn tone(self) -> DiffTone {
40        match self {
41            Self::Added => DiffTone::Added,
42            Self::Removed => DiffTone::Removed,
43            Self::Meta | Self::HunkHeader => DiffTone::Meta,
44            Self::Context => DiffTone::Context,
45        }
46    }
47}
48
49/// A line in a hunk, including canonical old/new numbering.
50#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
51pub struct PatchLine {
52    pub kind: PatchLineKind,
53    pub text: Arc<str>,
54    pub old_line_no: Option<usize>,
55    pub new_line_no: Option<usize>,
56    pub no_newline: bool,
57}
58
59impl PatchLine {
60    /// Creates an added line.
61    #[must_use]
62    pub fn added(text: impl Into<Arc<str>>, new_line_no: usize) -> Self {
63        Self {
64            kind: PatchLineKind::Added,
65            text: text.into(),
66            old_line_no: None,
67            new_line_no: Some(new_line_no),
68            no_newline: false,
69        }
70    }
71
72    /// Creates a removed line.
73    #[must_use]
74    pub fn removed(text: impl Into<Arc<str>>, old_line_no: usize) -> Self {
75        Self {
76            kind: PatchLineKind::Removed,
77            text: text.into(),
78            old_line_no: Some(old_line_no),
79            new_line_no: None,
80            no_newline: false,
81        }
82    }
83
84    /// Creates a context line.
85    #[must_use]
86    pub fn context(text: impl Into<Arc<str>>, old_line_no: usize, new_line_no: usize) -> Self {
87        Self {
88            kind: PatchLineKind::Context,
89            text: text.into(),
90            old_line_no: Some(old_line_no),
91            new_line_no: Some(new_line_no),
92            no_newline: false,
93        }
94    }
95
96    /// Marks this line as the final line without a trailing newline.
97    #[must_use]
98    pub fn without_newline(mut self) -> Self {
99        self.no_newline = true;
100        self
101    }
102
103    /// Returns the number for a side.
104    #[must_use]
105    pub const fn line_number(&self, side: DiffSide) -> Option<usize> {
106        match side {
107            DiffSide::Old => self.old_line_no,
108            DiffSide::New => self.new_line_no,
109        }
110    }
111}