refactor: remove legacy review JSON migration
This commit is contained in:
+1
-192
@@ -99,90 +99,6 @@ pub struct Review {
|
||||
saved: RefCell<Option<(PathBuf, Arc<Vec<u8>>)>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct LegacyReview {
|
||||
version: u32,
|
||||
root: PathBuf,
|
||||
summary: String,
|
||||
verdict: Option<Verdict>,
|
||||
comments: Vec<LegacyComment>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct LegacyComment {
|
||||
id: u64,
|
||||
anchor: LegacyAnchor,
|
||||
body: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
struct LegacyAnchor {
|
||||
revision: String,
|
||||
path: PathBuf,
|
||||
side: Side,
|
||||
start_line: usize,
|
||||
end_line: usize,
|
||||
content: String,
|
||||
}
|
||||
|
||||
impl LegacyReview {
|
||||
fn migrate(self) -> Result<Review, String> {
|
||||
if self.version != 1 {
|
||||
return Err(format!("Unsupported review version {}", self.version));
|
||||
}
|
||||
let comments = self
|
||||
.comments
|
||||
.into_iter()
|
||||
.map(|comment| {
|
||||
let old = comment.anchor;
|
||||
if old.start_line == 0 || old.end_line < old.start_line {
|
||||
return Err("Legacy review ranges must be inclusive and nonzero".into());
|
||||
}
|
||||
let mut lines = old.content.split('\n');
|
||||
let last = lines
|
||||
.nth(old.end_line - 1)
|
||||
.ok_or("Legacy review range is outside the anchor text")?;
|
||||
let end = if lines.next().is_some() {
|
||||
crate::plugin::TextPosition {
|
||||
row: old.end_line,
|
||||
column: 0,
|
||||
}
|
||||
} else {
|
||||
crate::plugin::TextPosition {
|
||||
row: old.end_line - 1,
|
||||
column: last.graphemes(true).count(),
|
||||
}
|
||||
};
|
||||
Ok(Comment {
|
||||
id: comment.id,
|
||||
anchor: Anchor {
|
||||
revision: old.revision,
|
||||
path: old.path,
|
||||
side: old.side,
|
||||
start: crate::plugin::TextPosition {
|
||||
row: old.start_line - 1,
|
||||
column: 0,
|
||||
},
|
||||
end,
|
||||
content: old.content,
|
||||
},
|
||||
body: comment.body,
|
||||
})
|
||||
})
|
||||
.collect::<Result<_, String>>()?;
|
||||
Ok(Review {
|
||||
version: 2,
|
||||
root: self.root,
|
||||
summary: self.summary,
|
||||
verdict: self.verdict,
|
||||
comments,
|
||||
saved: RefCell::new(None),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl Default for Review {
|
||||
fn default() -> Self {
|
||||
Self::new(PathBuf::new())
|
||||
@@ -264,20 +180,8 @@ impl Review {
|
||||
|
||||
pub fn load(path: &Path, root: &Path) -> Result<Self, String> {
|
||||
let bytes = read_file(path)?.ok_or("Review file does not exist")?;
|
||||
#[derive(Deserialize)]
|
||||
struct Version {
|
||||
version: u32,
|
||||
}
|
||||
let Version { version } = serde_json::from_slice(&bytes)
|
||||
let mut review: Self = serde_json::from_slice(&bytes)
|
||||
.map_err(|error| format!("Invalid review JSON: {error}"))?;
|
||||
let mut review: Self = match version {
|
||||
1 => serde_json::from_slice::<LegacyReview>(&bytes)
|
||||
.map_err(|error| format!("Invalid review JSON: {error}"))?
|
||||
.migrate()?,
|
||||
2 => serde_json::from_slice(&bytes)
|
||||
.map_err(|error| format!("Invalid review JSON: {error}"))?,
|
||||
_ => return Err(format!("Unsupported review version {version}")),
|
||||
};
|
||||
review.validate()?;
|
||||
let expected = fs::canonicalize(root).map_err(|error| error.to_string())?;
|
||||
let actual = fs::canonicalize(&review.root).map_err(|error| error.to_string())?;
|
||||
@@ -517,101 +421,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_lines_migrate_losslessly_and_save_exact_v2_ranges() {
|
||||
use crate::plugin::TextPosition;
|
||||
let directory = directory();
|
||||
let root = directory.0.canonicalize().unwrap();
|
||||
let path = root.join("review.json");
|
||||
let cases = [
|
||||
("first\nlast\n", 1, 2, (0, 0), (2, 0), 0..=1),
|
||||
("first\nlast", 2, 2, (1, 0), (1, 4), 1..=1),
|
||||
("é👩🔬e\u{301}", 1, 1, (0, 0), (0, 3), 0..=0),
|
||||
("first\n\n", 2, 2, (1, 0), (2, 0), 1..=1),
|
||||
("first\n", 2, 2, (1, 0), (1, 0), 1..=1),
|
||||
("", 1, 1, (0, 0), (0, 0), 0..=0),
|
||||
];
|
||||
let comments: Vec<_> = cases
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, case)| {
|
||||
serde_json::json!({
|
||||
"id": index + 11,
|
||||
"anchor": {
|
||||
"revision": "base:immutable",
|
||||
"path": "old/name.rs",
|
||||
"side": "before",
|
||||
"start_line": case.1,
|
||||
"end_line": case.2,
|
||||
"content": case.0,
|
||||
},
|
||||
"body": format!("Comment {index}"),
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
let original = serde_json::json!({
|
||||
"version": 1,
|
||||
"root": root,
|
||||
"summary": "Keep this summary",
|
||||
"verdict": "looks_good",
|
||||
"comments": comments,
|
||||
});
|
||||
fs::write(&path, serde_json::to_vec(&original).unwrap()).unwrap();
|
||||
let migrated = Review::load(&path, &root).unwrap();
|
||||
assert_eq!(migrated.version, 2);
|
||||
assert_eq!(migrated.root, root);
|
||||
assert_eq!(migrated.summary, "Keep this summary");
|
||||
assert_eq!(migrated.verdict, Some(Verdict::LooksGood));
|
||||
for (index, (comment, case)) in migrated.comments.iter().zip(&cases).enumerate() {
|
||||
assert_eq!(comment.id, index as u64 + 11);
|
||||
assert_eq!(comment.body, format!("Comment {index}"));
|
||||
assert_eq!(comment.anchor.revision, "base:immutable");
|
||||
assert_eq!(comment.anchor.path, Path::new("old/name.rs"));
|
||||
assert_eq!(comment.anchor.side, Side::Before);
|
||||
assert_eq!(comment.anchor.content, case.0);
|
||||
assert_eq!(
|
||||
comment.anchor.start,
|
||||
TextPosition {
|
||||
row: case.3.0,
|
||||
column: case.3.1
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
comment.anchor.end,
|
||||
TextPosition {
|
||||
row: case.4.0,
|
||||
column: case.4.1
|
||||
}
|
||||
);
|
||||
assert_eq!(comment.anchor.rows(), case.5);
|
||||
}
|
||||
migrated.save(&path).unwrap();
|
||||
let saved: serde_json::Value = serde_json::from_slice(&fs::read(&path).unwrap()).unwrap();
|
||||
assert_eq!(saved["version"], 2);
|
||||
assert!(saved["comments"][0]["anchor"].get("start_line").is_none());
|
||||
assert!(saved["comments"][0]["anchor"].get("end_line").is_none());
|
||||
assert_eq!(
|
||||
Review::load(&path, &root).unwrap().comments,
|
||||
migrated.comments
|
||||
);
|
||||
|
||||
for (start, end) in [(0, 1), (2, 1), (1, 4)] {
|
||||
let mut invalid = original.clone();
|
||||
invalid["comments"][0]["anchor"]["start_line"] = start.into();
|
||||
invalid["comments"][0]["anchor"]["end_line"] = end.into();
|
||||
fs::write(&path, serde_json::to_vec(&invalid).unwrap()).unwrap();
|
||||
assert!(Review::load(&path, &root).is_err());
|
||||
}
|
||||
let mut mixed = original.clone();
|
||||
mixed["comments"][0]["anchor"]["start"] = serde_json::json!({"row": 0, "column": 0});
|
||||
fs::write(&path, serde_json::to_vec(&mixed).unwrap()).unwrap();
|
||||
assert!(Review::load(&path, &root).is_err());
|
||||
let mut wrong_version = original;
|
||||
wrong_version["version"] = 2.into();
|
||||
fs::write(&path, serde_json::to_vec(&wrong_version).unwrap()).unwrap();
|
||||
assert!(Review::load(&path, &root).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exact_grapheme_ranges_roundtrip_and_reject_invalid_coordinates() {
|
||||
use crate::plugin::TextPosition;
|
||||
|
||||
Reference in New Issue
Block a user