suture_driver/lib.rs
1//! SutureDriver trait and registry for format-specific drivers.
2//!
3//! Drivers translate between file formats and semantic patches,
4//! enabling Suture to understand *what changed* rather than just
5//! *which bytes changed*.
6
7pub mod error;
8pub mod plugin;
9pub mod registry;
10pub mod types;
11
12pub use error::DriverError;
13pub use plugin::{BuiltinDriverPlugin, DriverPlugin, PluginError, PluginRegistry};
14pub use registry::DriverRegistry;
15pub use types::{DiffHunk, DiffHunkType, DiffSummary, VisualDiff};
16
17/// Format-specific driver for translating between file formats and Suture patches.
18///
19/// Implementations must be `Send + Sync` for concurrent use across threads.
20/// A driver understands the *semantics* of a file format — it knows that
21/// changing a key in a JSON object is a different operation than appending
22/// to an array.
23pub trait SutureDriver: Send + Sync {
24 /// Human-readable driver name (e.g., "JSON", "OpenTimelineIO", "CSV").
25 fn name(&self) -> &str;
26
27 /// File extensions this driver handles (e.g., `[".json", ".jsonl"]`).
28 fn supported_extensions(&self) -> &[&str];
29
30 /// Parse a file and produce a semantic diff between it and an optional base.
31 ///
32 /// If `base_content` is `None`, this is a new file — produce creation patches.
33 /// If `base_content` is `Some`, produce patches representing the differences.
34 ///
35 /// Each returned `SemanticChange` describes a meaningful semantic operation
36 /// (e.g., "key `users.2.email` changed from `old@example.com` to `new@example.com`").
37 fn diff(
38 &self,
39 base_content: Option<&str>,
40 new_content: &str,
41 ) -> Result<Vec<SemanticChange>, DriverError>;
42
43 /// Produce a human-readable diff string between two versions of a file.
44 ///
45 /// This is used by `suture diff` when a driver is available for the file type.
46 /// The output should be more meaningful than raw line diffs — showing
47 /// semantic operations like key changes, array insertions, etc.
48 fn format_diff(
49 &self,
50 base_content: Option<&str>,
51 new_content: &str,
52 ) -> Result<String, DriverError>;
53
54 /// Perform a semantic three-way merge.
55 ///
56 /// Given base, ours, and theirs content, produce a merged result.
57 /// Returns `None` if the merge cannot be resolved automatically (conflict).
58 /// Returns `Some(merged_content)` if the merge is clean.
59 fn merge(
60 &self,
61 _base: &str,
62 _ours: &str,
63 _theirs: &str,
64 ) -> Result<Option<String>, DriverError> {
65 Ok(None)
66 }
67}
68
69/// A single semantic change detected by a driver.
70#[derive(Debug, Clone, PartialEq)]
71pub enum SemanticChange {
72 /// A value was added at a path (e.g., new key in JSON object).
73 Added { path: String, value: String },
74 /// A value was removed at a path.
75 Removed { path: String, old_value: String },
76 /// A value was modified at a path.
77 Modified {
78 path: String,
79 old_value: String,
80 new_value: String,
81 },
82 /// A value was moved/renamed from one path to another.
83 Moved {
84 old_path: String,
85 new_path: String,
86 value: String,
87 },
88}