Skip to main content

oxicode_hashline/
types.rs

1//! Pure data types shared across the hashline parser, applier, and patcher.
2//! No filesystem, agent runtime, or schema library references — keep it pure.
3
4/// A line-number anchor (1-indexed).
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
6pub struct Anchor {
7    /// The 1-indexed source line this anchor points at.
8    pub line: u32,
9}
10
11/// Where an `insert` edit should land relative to existing content.
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub enum Cursor {
14    /// Insert at the beginning of the file (before line 1).
15    Bof,
16    /// Insert at the end of the file (after the last line).
17    Eof,
18    /// Insert immediately before the given anchor line.
19    BeforeAnchor(Anchor),
20    /// Insert immediately after the given anchor line.
21    AfterAnchor(Anchor),
22}
23
24/// Insert mode for replacement lowering (SWAP lowers to Insert::Replacement).
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum InsertMode {
27    /// The insert replaces existing content at the cursor (SWAP lowering).
28    Replacement,
29}
30
31/// Block operations mode (future: block-ops feature).
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum BlockMode {
34    /// Insert content after the resolved block.
35    InsertAfter,
36}
37
38/// A single low-level edit produced by the parser and consumed by the applier.
39#[derive(Debug, Clone)]
40pub enum Edit {
41    /// Insert new text at a cursor position.
42    Insert {
43        /// Where the inserted text lands relative to existing content.
44        cursor: Cursor,
45        /// The literal text to insert.
46        text: String,
47        /// 1-indexed line number of this edit in the patch text (for diagnostics).
48        line_num: u32,
49        /// Position of this edit in the patch's edit list (for stable ordering).
50        index: usize,
51        /// Lowering mode for this insert, if any (e.g. SWAP-to-replacement).
52        mode: Option<InsertMode>,
53    },
54    /// Delete the line at an anchor.
55    Delete {
56        /// The 1-indexed line this delete targets.
57        anchor: Anchor,
58        /// 1-indexed line number of this edit in the patch text (for diagnostics).
59        line_num: u32,
60        /// Position of this edit in the patch's edit list (for stable ordering).
61        index: usize,
62        /// Optional content asserted present at the deleted line (reserved for future guards).
63        old_assertion: Option<String>,
64    },
65}
66
67impl Edit {
68    /// The 1-indexed line number in the patch text where this edit appears
69    /// (for diagnostics).
70    pub fn line_num(&self) -> u32 {
71        match self {
72            Edit::Insert { line_num, .. } | Edit::Delete { line_num, .. } => *line_num,
73        }
74    }
75
76    /// The anchor line this edit targets (for session-chain recovery guards).
77    pub fn anchor_line(&self) -> u32 {
78        match self {
79            Edit::Insert { cursor, .. } => match cursor {
80                Cursor::BeforeAnchor(a) | Cursor::AfterAnchor(a) => a.line,
81                Cursor::Bof => 0,
82                Cursor::Eof => u32::MAX,
83            },
84            Edit::Delete { anchor, .. } => anchor.line,
85        }
86    }
87
88    /// Index in the patch's edit list (for stable ordering).
89    pub fn index(&self) -> usize {
90        match self {
91            Edit::Insert { index, .. } | Edit::Delete { index, .. } => *index,
92        }
93    }
94}
95
96/// Result of applying a parsed set of edits to a text body.
97#[derive(Debug, Clone, Default)]
98pub struct ApplyResult {
99    /// The resulting text after all edits are applied.
100    pub text: String,
101    /// 1-indexed line number of the first changed line, if any.
102    pub first_changed_line: Option<u32>,
103    /// Warning messages generated during apply (boundary repair, landing
104    /// correction, etc.).
105    pub warnings: Vec<String>,
106}
107
108/// A parsed `start.=end` line range (1-indexed, inclusive).
109#[derive(Debug, Clone, Copy, PartialEq, Eq)]
110pub struct ParsedRange {
111    /// First line of the range (inclusive).
112    pub start: u32,
113    /// Last line of the range (inclusive).
114    pub end: u32,
115}
116
117/// Optional hints for [`crate::parser::split_patch_input`].
118#[derive(Debug, Clone, Default)]
119pub struct SplitOptions {
120    /// Root directory for resolving relative paths.
121    pub root: Option<std::path::PathBuf>,
122}
123
124/// Result of [`crate::diff_preview::build_compact_diff_preview`].
125#[derive(Debug, Clone, Default)]
126pub struct CompactDiffPreview {
127    /// Textual preview lines.
128    pub lines: Vec<String>,
129}
130
131/// Optional knobs for compact diff preview.
132#[derive(Debug, Clone)]
133pub struct CompactDiffOptions {
134    /// Maximum unchanged lines kept as context between hunks.
135    pub max_unchanged_context: usize,
136}
137
138impl Default for CompactDiffOptions {
139    fn default() -> Self {
140        Self {
141            max_unchanged_context: 3,
142        }
143    }
144}
145
146/// Resolved 1-indexed inclusive line span of a `replace_block N:` target.
147/// (block-ops feature — future)
148#[derive(Debug, Clone, Copy)]
149pub struct BlockSpan {
150    /// First line of the resolved span (inclusive).
151    pub start: u32,
152    /// Last line of the resolved span (inclusive).
153    pub end: u32,
154}
155
156/// One block anchor resolution (block-ops feature — future).
157#[derive(Debug, Clone)]
158pub struct BlockResolution {
159    /// 1-indexed line that named the block being resolved.
160    pub anchor_line: u32,
161    /// First line of the resolved block (inclusive).
162    pub start: u32,
163    /// Last line of the resolved block (inclusive).
164    pub end: u32,
165    /// The operation to perform on the resolved block.
166    pub op: BlockOp,
167}
168
169/// Kind of block operation to perform (block-ops feature — future).
170#[derive(Debug, Clone, Copy)]
171pub enum BlockOp {
172    /// Replace the resolved block's content.
173    Replace,
174    /// Delete the resolved block.
175    Delete,
176    /// Insert new content after the resolved block.
177    InsertAfter,
178}
179
180/// Request handed to a block resolver (block-ops feature — future).
181#[derive(Debug, Clone)]
182pub struct BlockResolverRequest {
183    /// The source text the resolver searches within.
184    pub text: String,
185    /// 1-indexed line naming the block to resolve.
186    pub anchor_line: u32,
187}
188
189/// Resolves a block anchor to a line span (block-ops feature — future).
190pub type BlockResolver =
191    std::sync::Arc<dyn Fn(&BlockResolverRequest) -> Option<BlockSpan> + Send + Sync>;