Skip to content

Commit

Permalink
fix(inline): Fail update on conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
epage committed Jul 10, 2024
1 parent 90d34b8 commit 1face20
Showing 1 changed file with 69 additions and 49 deletions.
118 changes: 69 additions & 49 deletions crates/snapbox/src/data/runtime.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::BTreeMap;

use super::Data;
use super::Inline;
use super::Position;
Expand Down Expand Up @@ -74,27 +76,37 @@ impl SourceFileRuntime {
fn update(&mut self, actual: &str, inline: &Inline) -> std::io::Result<()> {
let span = Span::from_pos(&inline.position, &self.original_text);
let patch = format_patch(actual);
self.patchwork.patch(span.literal_range, &patch);
self.patchwork.patch(span.literal_range, &patch)?;
std::fs::write(&inline.position.file, &self.patchwork.text)
}
}

#[derive(Debug)]
struct Patchwork {
text: String,
indels: Vec<(std::ops::Range<usize>, usize)>,
indels: BTreeMap<OrdRange, usize>,
}

impl Patchwork {
fn new(text: String) -> Patchwork {
Patchwork {
text,
indels: Vec::new(),
indels: BTreeMap::new(),
}
}
fn patch(&mut self, mut range: std::ops::Range<usize>, patch: &str) {
self.indels.push((range.clone(), patch.len()));
self.indels.sort_by_key(|(delete, _insert)| delete.start);
fn patch(&mut self, mut range: std::ops::Range<usize>, patch: &str) -> std::io::Result<()> {
let key: OrdRange = range.clone().into();
match self.indels.entry(key) {
std::collections::btree_map::Entry::Vacant(entry) => {
entry.insert(patch.len());
}
std::collections::btree_map::Entry::Occupied(_) => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
"cannot update as it was already modified",
));
}
}

let (delete, insert) = self
.indels
Expand All @@ -109,6 +121,22 @@ impl Patchwork {
}

self.text.replace_range(range, patch);
Ok(())
}
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
struct OrdRange {
start: usize,
end: usize,
}

impl From<std::ops::Range<usize>> for OrdRange {
fn from(other: std::ops::Range<usize>) -> Self {
Self {
start: other.start,
end: other.end,
}
}
}

Expand Down Expand Up @@ -377,28 +405,28 @@ world
#[test]
fn test_patchwork() {
let mut patchwork = Patchwork::new("one two three".to_owned());
patchwork.patch(4..7, "zwei");
patchwork.patch(0..3, "один");
patchwork.patch(8..13, "3");
patchwork.patch(4..7, "zwei").unwrap();
patchwork.patch(0..3, "один").unwrap();
patchwork.patch(8..13, "3").unwrap();
assert_data_eq!(
patchwork.to_debug(),
str![[r#"
Patchwork {
text: "один zwei 3",
indels: [
(
0..3,
8,
),
(
4..7,
4,
),
(
8..13,
1,
),
],
indels: {
OrdRange {
start: 0,
end: 3,
}: 8,
OrdRange {
start: 4,
end: 7,
}: 4,
OrdRange {
start: 8,
end: 13,
}: 1,
},
}
"#]],
Expand All @@ -408,23 +436,19 @@ Patchwork {
#[test]
fn test_patchwork_overlap_diverge() {
let mut patchwork = Patchwork::new("one two three".to_owned());
patchwork.patch(4..7, "zwei");
patchwork.patch(4..7, "abcd");
patchwork.patch(4..7, "zwei").unwrap();
patchwork.patch(4..7, "abcd").unwrap_err();
assert_data_eq!(
patchwork.to_debug(),
str![[r#"
Patchwork {
text: "one abcdi three",
indels: [
(
4..7,
4,
),
(
4..7,
4,
),
],
text: "one zwei three",
indels: {
OrdRange {
start: 4,
end: 7,
}: 4,
},
}
"#]],
Expand All @@ -434,23 +458,19 @@ Patchwork {
#[test]
fn test_patchwork_overlap_converge() {
let mut patchwork = Patchwork::new("one two three".to_owned());
patchwork.patch(4..7, "zwei");
patchwork.patch(4..7, "zwei");
patchwork.patch(4..7, "zwei").unwrap();
patchwork.patch(4..7, "zwei").unwrap_err();
assert_data_eq!(
patchwork.to_debug(),
str![[r#"
Patchwork {
text: "one zweii three",
indels: [
(
4..7,
4,
),
(
4..7,
4,
),
],
text: "one zwei three",
indels: {
OrdRange {
start: 4,
end: 7,
}: 4,
},
}
"#]],
Expand Down

0 comments on commit 1face20

Please sign in to comment.