aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTeddy Wing2020-07-28 20:49:50 +0200
committerTeddy Wing2020-07-28 20:49:50 +0200
commit1e2fb780514ff28f136024d362237e5c2038f2cc (patch)
tree8d8ce2650a8eac8466ff8e32830791cec95f2891 /src
parent11118c94cc8fa315cbad1e39f3f285eb7ccbcca8 (diff)
downloadgit-suggestion-1e2fb780514ff28f136024d362237e5c2038f2cc.tar.bz2
Move `Suggestion` to a new module
Planning to separate the `Suggestion` and `Client` code.
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs447
-rw-r--r--src/suggestion.rs456
2 files changed, 459 insertions, 444 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 31df669..4a15852 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,18 +1,15 @@
#![warn(rust_2018_idioms)]
+pub mod suggestion;
+
mod url;
+pub use crate::suggestion::Suggestion;
pub use crate::url::SuggestionUrl;
-use std::io::{BufRead, BufReader, BufWriter, Write};
-use std::path::Path;
-
-use git2::{Patch, Repository};
use github_rs::client::{Executor, Github};
-use regex::Regex;
-use serde::Deserialize;
use serde_json::Value;
use thiserror::Error;
@@ -63,182 +60,6 @@ impl<'a> Client<'a> {
}
}
-#[derive(Debug, PartialEq)]
-enum LineEnding {
- Lf,
- CrLf,
-}
-
-#[derive(Debug, Deserialize)]
-pub struct Suggestion {
- #[serde(rename = "diff_hunk")]
- diff: String,
-
- #[serde(rename = "body")]
- comment: String,
-
- #[serde(rename = "original_commit_id")]
- commit: String,
-
- path: String,
-
- original_start_line: Option<usize>,
-
- #[serde(rename = "original_line")]
- original_end_line: usize,
-}
-
-impl Suggestion {
- // TODO: Rename to `diff`
- pub fn patch(&self) -> String {
- let mut diff: Vec<_> = self.diff.lines()
- .filter(|l| !l.starts_with("-"))
- .map(|l| {
- if l.starts_with("+") {
- return l.replacen("+", " ", 1);
- }
-
- l.to_owned()
- })
- .collect();
-
- let last = diff.len() - 1;
- diff[last] = diff.last().unwrap()
- .replacen(" ", "-", 1);
-
- diff.push(self.suggestion_patch());
-
- diff.join("\n")
- }
-
- pub fn diff(&self) -> String {
- let repo = Repository::open(".").unwrap();
-
- self.diff_with_repo(&repo)
- }
-
- fn diff_with_repo(&self, repo: &Repository) -> String {
- let commit = repo.find_commit(self.commit.parse().unwrap()).unwrap();
-
- let path = Path::new(&self.path);
-
- let object = commit
- .tree().unwrap()
- .get_path(path).unwrap()
- .to_object(&repo).unwrap();
-
- let blob = object.as_blob().unwrap();
-
- let blob_reader = BufReader::new(blob.content());
- let mut new = BufWriter::new(Vec::new());
- self.apply_to(blob_reader, &mut new).unwrap();
- let new_buffer = new.into_inner().unwrap();
-
- let mut diff = Patch::from_blob_and_buffer(
- blob,
- Some(&path),
- &new_buffer,
- Some(&path),
- None,
- ).unwrap();
-
- diff.to_buf()
- .unwrap()
- .as_str()
- .unwrap_or("")
- .to_owned()
- }
-
- fn suggestion_patch(&self) -> String {
- let re = Regex::new(r"(?s).*(?-s)```\s*suggestion.*\n").unwrap();
- let s = re.replace(&self.comment, "+");
- s.replace("```", "")
- }
-
- fn suggestion(&self) -> String {
- self.suggestion_with_line_ending(&LineEnding::Lf)
- }
-
- fn suggestion_with_line_ending(&self, line_ending: &LineEnding) -> String {
- let re = Regex::new(r"(?s).*(?-s)```\s*suggestion.*\n").unwrap();
- let s = re.replace(&self.comment, "");
- let s = s.replace("```", "");
-
- // Suggestion blocks use CRLF by default.
- if *line_ending == LineEnding::Lf {
- return s.replace('\r', "");
- }
-
- s
- }
-
- pub fn apply(&self) -> Result<(), Error> {
- let repo = Repository::open(".").unwrap();
-
- let diff_text = self.diff_with_repo(&repo);
- let diff = git2::Diff::from_buffer(diff_text.as_bytes()).unwrap();
-
- repo.apply(
- &diff,
- git2::ApplyLocation::WorkDir,
- None,
- ).unwrap();
-
- Ok(())
- }
-
- fn apply_to<R: BufRead, W: Write>(
- &self,
- reader: R,
- writer: &mut W,
- ) -> Result<(), Error> {
- let mut line_ending = LineEnding::Lf;
-
- for (i, line) in reader.lines().enumerate() {
- let line_number = i + 1;
-
- match line {
- Ok(l) => {
- // Determine which line endings the file uses by looking at
- // the first line.
- if line_number == 1 && is_line_crlf(&l) {
- line_ending = LineEnding::CrLf;
- }
-
- if line_number == self.original_end_line {
- write!(
- writer,
- "{}",
- self.suggestion_with_line_ending(&line_ending),
- ).unwrap();
- } else if self.original_start_line.is_none()
- || line_number < self.original_start_line.unwrap()
- || line_number > self.original_end_line {
- writeln!(writer, "{}", l).unwrap();
- }
- },
- Err(e) => panic!(e),
- }
- }
-
- Ok(())
- }
-}
-
-/// Determine the line ending for `line`.
-///
-/// If the second-to-last character on the first line is "\r", assume CRLF.
-/// Otherwise, default to LF.
-fn is_line_crlf(line: &str) -> bool {
- if let Some(c) = line.chars().rev().nth(2) {
- if c == '\r' {
- return true;
- }
- }
-
- false
-}
-
#[cfg(test)]
mod tests {
use super::*;
@@ -256,266 +77,4 @@ mod tests {
println!("{:?}", suggestion);
}
-
- #[test]
- fn suggestion_patch_generates_patch() {
- // Diff from gabgodBB (https://github.com/gabgodBB) and suggestion from
- // probablycorey (https://github.com/probablycorey) in this pull
- // request: https://github.com/cli/cli/pull/1123
-
- let suggestion = Suggestion {
- diff: r#"@@ -1, 9 +1, 11 @@
- package command
-
- import (
-+ "bufio" // used to input comment
- "errors"
- "fmt"
- "io"
-+ "os" // used to input comment"#.to_owned(),
- comment: r#"It's ok to leave these uncommented
-
-```suggestion
- "os"
-```"#.to_owned(),
- commit: "".to_owned(),
- path: "".to_owned(),
- original_start_line: Some(8),
- original_end_line: 8,
- };
-
- assert_eq!(
- suggestion.patch(),
- r#"@@ -1, 9 +1, 11 @@
- package command
-
- import (
- "bufio" // used to input comment
- "errors"
- "fmt"
- "io"
-- "os" // used to input comment
-+ "os"
-"#,
- );
- }
-
- #[test]
- fn unified_diff() {
- use unidiff::PatchSet;
-
- let diff = r#"--- a/command/pr.go
-+++ b/command/pr.go
-@@ -1,9 +1,11 @@
- package command
-
- import (
-+ "bufio" // used to input comment
- "errors"
- "fmt"
- "io"
-+ "os" // used to input comment
-"#;
-
- let mut patch = PatchSet::new();
- patch.parse(diff).unwrap();
-
- println!("{:?}", patch);
- println!("{}", patch);
-
- let lines = patch.files_mut()[0].hunks_mut()[0].lines_mut();
-
- // for line in &lines {
- // if line.is_removed() {
- // } else if line.is_added() {
- // line.line_type = unidiff::LINE_TYPE_CONTEXT.to_owned();
- // }
- // }
-
- lines
- .iter_mut()
- .filter(|l| !l.is_removed())
- // .map(|l| {
- .for_each(|l| {
- if l.is_added() {
- l.line_type = unidiff::LINE_TYPE_CONTEXT.to_owned();
- }
- });
-
- lines[lines.len() - 2].line_type = unidiff::LINE_TYPE_REMOVED.to_owned();
-
- patch.files_mut()[0].hunks_mut()[0].append(unidiff::Line::new(
- r#" "os""#,
- unidiff::LINE_TYPE_ADDED,
- ));
-
- println!("{}", patch);
- }
-
- #[test]
- fn read_git_blob() {
- use std::path::Path;
-
- use git2::Repository;
-
- let repo = Repository::open("./private/suggestion-test").unwrap();
- let commit = repo.find_commit("b58be52880a0a0c0d397052351be31f19acdeca4".parse().unwrap()).unwrap();
-
- let object = commit
- .tree().unwrap()
- .get_path(Path::new("src/server.rs")).unwrap()
- .to_object(&repo).unwrap();
-
- let blob = object
- .as_blob().unwrap()
- .content();
-
- println!("{:?}", commit);
- println!("{}", std::str::from_utf8(blob).unwrap());
- }
-
- #[test]
- fn suggestion_diff_with_repo_generates_diff() {
- use tempfile::tempdir;
-
-
- let git_root = tempdir().unwrap();
- let repo = Repository::init(git_root.path()).unwrap();
-
- let file = r#"
- ‘Beware the Jabberwock, my son!
- The jaws that bite, the claws that catch!
- Beware the Jubjub bird, and shun
- The frumious Bandersnatch!’
-
- He took his vorpal blade in hand:
- Long time the manxome foe he sought--
- So rested he by the Tumtum tree,
- And stood awhile in thought.
-"#;
-
- let path = "poems/Jabberwocky.txt";
-
- let mut index = repo.index().unwrap();
- index.add_frombuffer(
- &git2::IndexEntry {
- ctime: git2::IndexTime::new(0, 0),
- mtime: git2::IndexTime::new(0, 0),
- dev: 0,
- ino: 0,
- mode: 0o100644,
- uid: 0,
- gid: 0,
- file_size: file.len() as u32,
- id: git2::Oid::zero(),
- flags: 0,
- flags_extended: 0,
- path: path.as_bytes().to_vec(),
- },
- file.as_bytes(),
- ).unwrap();
- let tree_oid = index.write_tree().unwrap();
- let tree = repo.find_tree(tree_oid).unwrap();
-
- let author = git2::Signature::now(
- "Oshino Shinobu",
- "oshino.shinobu@example.com",
- ).unwrap();
-
- let commit = repo.commit(
- Some("HEAD"),
- &author,
- &author,
- "Sample commit",
- &tree,
- &[],
- ).unwrap();
-
- let suggestion = Suggestion {
- diff: "".to_owned(),
- comment: r#"``` suggestion
- He took his vorpal sword in hand:
- Long time the manxome foe he sought—
-```"#.to_owned(),
- commit: commit.to_string(),
- path: path.to_owned(),
- original_start_line: Some(7),
- original_end_line: 8,
- };
-
- let expected = r#"diff --git a/poems/Jabberwocky.txt b/poems/Jabberwocky.txt
-index 89840a2..06acdfc 100644
---- a/poems/Jabberwocky.txt
-+++ b/poems/Jabberwocky.txt
-@@ -4,7 +4,7 @@
- Beware the Jubjub bird, and shun
- The frumious Bandersnatch!’
-
-- He took his vorpal blade in hand:
-- Long time the manxome foe he sought--
-+ He took his vorpal sword in hand:
-+ Long time the manxome foe he sought—
- So rested he by the Tumtum tree,
- And stood awhile in thought.
-"#;
-
- assert_eq!(
- suggestion.diff_with_repo(&repo),
- expected,
- );
- }
-
- #[test]
- fn suggestion_apply_to_writes_patch_to_writer() {
- use std::io::Cursor;
-
-
- let mut original_buffer = Vec::new();
- let original = r#"
- ‘Beware the Jabberwock, my son!
- The jaws that bite, the claws that catch!
- Beware the Jubjub bird, and shun
- The frumious Bandersnatch!’
-
- He took his vorpal blade in hand:
- Long time the manxome foe he sought--
- So rested he by the Tumtum tree,
- And stood awhile in thought.
-"#;
-
- write!(original_buffer, "{}", original).unwrap();
-
- let suggestion = Suggestion {
- diff: "".to_owned(),
- comment: r#"``` suggestion
- He took his vorpal sword in hand:
- Long time the manxome foe he sought—
-```"#.to_owned(),
- commit: "".to_owned(),
- path: "".to_owned(),
- original_start_line: Some(7),
- original_end_line: 8,
- };
-
- let expected = r#"
- ‘Beware the Jabberwock, my son!
- The jaws that bite, the claws that catch!
- Beware the Jubjub bird, and shun
- The frumious Bandersnatch!’
-
- He took his vorpal sword in hand:
- Long time the manxome foe he sought—
- So rested he by the Tumtum tree,
- And stood awhile in thought.
-"#;
-
- let original_reader = Cursor::new(original_buffer);
- let mut actual = Cursor::new(Vec::new());
- suggestion.apply_to(original_reader, &mut actual).unwrap();
-
- assert_eq!(
- std::str::from_utf8(&actual.into_inner()).unwrap(),
- expected,
- );
- }
}
diff --git a/src/suggestion.rs b/src/suggestion.rs
new file mode 100644
index 0000000..68a2430
--- /dev/null
+++ b/src/suggestion.rs
@@ -0,0 +1,456 @@
+use std::io::{BufRead, BufReader, BufWriter, Write};
+use std::path::Path;
+
+use git2::{Patch, Repository};
+use regex::Regex;
+use serde::Deserialize;
+use thiserror::Error;
+
+
+#[derive(Debug, Error)]
+pub enum Error {
+}
+
+#[derive(Debug, PartialEq)]
+enum LineEnding {
+ Lf,
+ CrLf,
+}
+
+#[derive(Debug, Deserialize)]
+pub struct Suggestion {
+ #[serde(rename = "diff_hunk")]
+ diff: String,
+
+ #[serde(rename = "body")]
+ comment: String,
+
+ #[serde(rename = "original_commit_id")]
+ commit: String,
+
+ path: String,
+
+ original_start_line: Option<usize>,
+
+ #[serde(rename = "original_line")]
+ original_end_line: usize,
+}
+
+impl Suggestion {
+ // TODO: Rename to `diff`
+ pub fn patch(&self) -> String {
+ let mut diff: Vec<_> = self.diff.lines()
+ .filter(|l| !l.starts_with("-"))
+ .map(|l| {
+ if l.starts_with("+") {
+ return l.replacen("+", " ", 1);
+ }
+
+ l.to_owned()
+ })
+ .collect();
+
+ let last = diff.len() - 1;
+ diff[last] = diff.last().unwrap()
+ .replacen(" ", "-", 1);
+
+ diff.push(self.suggestion_patch());
+
+ diff.join("\n")
+ }
+
+ pub fn diff(&self) -> String {
+ let repo = Repository::open(".").unwrap();
+
+ self.diff_with_repo(&repo)
+ }
+
+ fn diff_with_repo(&self, repo: &Repository) -> String {
+ let commit = repo.find_commit(self.commit.parse().unwrap()).unwrap();
+
+ let path = Path::new(&self.path);
+
+ let object = commit
+ .tree().unwrap()
+ .get_path(path).unwrap()
+ .to_object(&repo).unwrap();
+
+ let blob = object.as_blob().unwrap();
+
+ let blob_reader = BufReader::new(blob.content());
+ let mut new = BufWriter::new(Vec::new());
+ self.apply_to(blob_reader, &mut new).unwrap();
+ let new_buffer = new.into_inner().unwrap();
+
+ let mut diff = Patch::from_blob_and_buffer(
+ blob,
+ Some(&path),
+ &new_buffer,
+ Some(&path),
+ None,
+ ).unwrap();
+
+ diff.to_buf()
+ .unwrap()
+ .as_str()
+ .unwrap_or("")
+ .to_owned()
+ }
+
+ fn suggestion_patch(&self) -> String {
+ let re = Regex::new(r"(?s).*(?-s)```\s*suggestion.*\n").unwrap();
+ let s = re.replace(&self.comment, "+");
+ s.replace("```", "")
+ }
+
+ fn suggestion(&self) -> String {
+ self.suggestion_with_line_ending(&LineEnding::Lf)
+ }
+
+ fn suggestion_with_line_ending(&self, line_ending: &LineEnding) -> String {
+ let re = Regex::new(r"(?s).*(?-s)```\s*suggestion.*\n").unwrap();
+ let s = re.replace(&self.comment, "");
+ let s = s.replace("```", "");
+
+ // Suggestion blocks use CRLF by default.
+ if *line_ending == LineEnding::Lf {
+ return s.replace('\r', "");
+ }
+
+ s
+ }
+
+ pub fn apply(&self) -> Result<(), Error> {
+ let repo = Repository::open(".").unwrap();
+
+ let diff_text = self.diff_with_repo(&repo);
+ let diff = git2::Diff::from_buffer(diff_text.as_bytes()).unwrap();
+
+ repo.apply(
+ &diff,
+ git2::ApplyLocation::WorkDir,
+ None,
+ ).unwrap();
+
+ Ok(())
+ }
+
+ fn apply_to<R: BufRead, W: Write>(
+ &self,
+ reader: R,
+ writer: &mut W,
+ ) -> Result<(), Error> {
+ let mut line_ending = LineEnding::Lf;
+
+ for (i, line) in reader.lines().enumerate() {
+ let line_number = i + 1;
+
+ match line {
+ Ok(l) => {
+ // Determine which line endings the file uses by looking at
+ // the first line.
+ if line_number == 1 && is_line_crlf(&l) {
+ line_ending = LineEnding::CrLf;
+ }
+
+ if line_number == self.original_end_line {
+ write!(
+ writer,
+ "{}",
+ self.suggestion_with_line_ending(&line_ending),
+ ).unwrap();
+ } else if self.original_start_line.is_none()
+ || line_number < self.original_start_line.unwrap()
+ || line_number > self.original_end_line {
+ writeln!(writer, "{}", l).unwrap();
+ }
+ },
+ Err(e) => panic!(e),
+ }
+ }
+
+ Ok(())
+ }
+}
+
+/// Determine the line ending for `line`.
+///
+/// If the second-to-last character on the first line is "\r", assume CRLF.
+/// Otherwise, default to LF.
+fn is_line_crlf(line: &str) -> bool {
+ if let Some(c) = line.chars().rev().nth(2) {
+ if c == '\r' {
+ return true;
+ }
+ }
+
+ false
+}
+
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn suggestion_patch_generates_patch() {
+ // Diff from gabgodBB (https://github.com/gabgodBB) and suggestion from
+ // probablycorey (https://github.com/probablycorey) in this pull
+ // request: https://github.com/cli/cli/pull/1123
+
+ let suggestion = Suggestion {
+ diff: r#"@@ -1, 9 +1, 11 @@
+ package command
+
+ import (
++ "bufio" // used to input comment
+ "errors"
+ "fmt"
+ "io"
++ "os" // used to input comment"#.to_owned(),
+ comment: r#"It's ok to leave these uncommented
+
+```suggestion
+ "os"
+```"#.to_owned(),
+ commit: "".to_owned(),
+ path: "".to_owned(),
+ original_start_line: Some(8),
+ original_end_line: 8,
+ };
+
+ assert_eq!(
+ suggestion.patch(),
+ r#"@@ -1, 9 +1, 11 @@
+ package command
+
+ import (
+ "bufio" // used to input comment
+ "errors"
+ "fmt"
+ "io"
+- "os" // used to input comment
++ "os"
+"#,
+ );
+ }
+
+ #[test]
+ fn unified_diff() {
+ use unidiff::PatchSet;
+
+ let diff = r#"--- a/command/pr.go
++++ b/command/pr.go
+@@ -1,9 +1,11 @@
+ package command
+
+ import (
++ "bufio" // used to input comment
+ "errors"
+ "fmt"
+ "io"
++ "os" // used to input comment
+"#;
+
+ let mut patch = PatchSet::new();
+ patch.parse(diff).unwrap();
+
+ println!("{:?}", patch);
+ println!("{}", patch);
+
+ let lines = patch.files_mut()[0].hunks_mut()[0].lines_mut();
+
+ // for line in &lines {
+ // if line.is_removed() {
+ // } else if line.is_added() {
+ // line.line_type = unidiff::LINE_TYPE_CONTEXT.to_owned();
+ // }
+ // }
+
+ lines
+ .iter_mut()
+ .filter(|l| !l.is_removed())
+ // .map(|l| {
+ .for_each(|l| {
+ if l.is_added() {
+ l.line_type = unidiff::LINE_TYPE_CONTEXT.to_owned();
+ }
+ });
+
+ lines[lines.len() - 2].line_type = unidiff::LINE_TYPE_REMOVED.to_owned();
+
+ patch.files_mut()[0].hunks_mut()[0].append(unidiff::Line::new(
+ r#" "os""#,
+ unidiff::LINE_TYPE_ADDED,
+ ));
+
+ println!("{}", patch);
+ }
+
+ #[test]
+ fn read_git_blob() {
+ use std::path::Path;
+
+ use git2::Repository;
+
+ let repo = Repository::open("./private/suggestion-test").unwrap();
+ let commit = repo.find_commit("b58be52880a0a0c0d397052351be31f19acdeca4".parse().unwrap()).unwrap();
+
+ let object = commit
+ .tree().unwrap()
+ .get_path(Path::new("src/server.rs")).unwrap()
+ .to_object(&repo).unwrap();
+
+ let blob = object
+ .as_blob().unwrap()
+ .content();
+
+ println!("{:?}", commit);
+ println!("{}", std::str::from_utf8(blob).unwrap());
+ }
+
+ #[test]
+ fn suggestion_diff_with_repo_generates_diff() {
+ use tempfile::tempdir;
+
+
+ let git_root = tempdir().unwrap();
+ let repo = Repository::init(git_root.path()).unwrap();
+
+ let file = r#"
+ ‘Beware the Jabberwock, my son!
+ The jaws that bite, the claws that catch!
+ Beware the Jubjub bird, and shun
+ The frumious Bandersnatch!’
+
+ He took his vorpal blade in hand:
+ Long time the manxome foe he sought--
+ So rested he by the Tumtum tree,
+ And stood awhile in thought.
+"#;
+
+ let path = "poems/Jabberwocky.txt";
+
+ let mut index = repo.index().unwrap();
+ index.add_frombuffer(
+ &git2::IndexEntry {
+ ctime: git2::IndexTime::new(0, 0),
+ mtime: git2::IndexTime::new(0, 0),
+ dev: 0,
+ ino: 0,
+ mode: 0o100644,
+ uid: 0,
+ gid: 0,
+ file_size: file.len() as u32,
+ id: git2::Oid::zero(),
+ flags: 0,
+ flags_extended: 0,
+ path: path.as_bytes().to_vec(),
+ },
+ file.as_bytes(),
+ ).unwrap();
+ let tree_oid = index.write_tree().unwrap();
+ let tree = repo.find_tree(tree_oid).unwrap();
+
+ let author = git2::Signature::now(
+ "Oshino Shinobu",
+ "oshino.shinobu@example.com",
+ ).unwrap();
+
+ let commit = repo.commit(
+ Some("HEAD"),
+ &author,
+ &author,
+ "Sample commit",
+ &tree,
+ &[],
+ ).unwrap();
+
+ let suggestion = Suggestion {
+ diff: "".to_owned(),
+ comment: r#"``` suggestion
+ He took his vorpal sword in hand:
+ Long time the manxome foe he sought—
+```"#.to_owned(),
+ commit: commit.to_string(),
+ path: path.to_owned(),
+ original_start_line: Some(7),
+ original_end_line: 8,
+ };
+
+ let expected = r#"diff --git a/poems/Jabberwocky.txt b/poems/Jabberwocky.txt
+index 89840a2..06acdfc 100644
+--- a/poems/Jabberwocky.txt
++++ b/poems/Jabberwocky.txt
+@@ -4,7 +4,7 @@
+ Beware the Jubjub bird, and shun
+ The frumious Bandersnatch!’
+
+- He took his vorpal blade in hand:
+- Long time the manxome foe he sought--
++ He took his vorpal sword in hand:
++ Long time the manxome foe he sought—
+ So rested he by the Tumtum tree,
+ And stood awhile in thought.
+"#;
+
+ assert_eq!(
+ suggestion.diff_with_repo(&repo),
+ expected,
+ );
+ }
+
+ #[test]
+ fn suggestion_apply_to_writes_patch_to_writer() {
+ use std::io::Cursor;
+
+
+ let mut original_buffer = Vec::new();
+ let original = r#"
+ ‘Beware the Jabberwock, my son!
+ The jaws that bite, the claws that catch!
+ Beware the Jubjub bird, and shun
+ The frumious Bandersnatch!’
+
+ He took his vorpal blade in hand:
+ Long time the manxome foe he sought--
+ So rested he by the Tumtum tree,
+ And stood awhile in thought.
+"#;
+
+ write!(original_buffer, "{}", original).unwrap();
+
+ let suggestion = Suggestion {
+ diff: "".to_owned(),
+ comment: r#"``` suggestion
+ He took his vorpal sword in hand:
+ Long time the manxome foe he sought—
+```"#.to_owned(),
+ commit: "".to_owned(),
+ path: "".to_owned(),
+ original_start_line: Some(7),
+ original_end_line: 8,
+ };
+
+ let expected = r#"
+ ‘Beware the Jabberwock, my son!
+ The jaws that bite, the claws that catch!
+ Beware the Jubjub bird, and shun
+ The frumious Bandersnatch!’
+
+ He took his vorpal sword in hand:
+ Long time the manxome foe he sought—
+ So rested he by the Tumtum tree,
+ And stood awhile in thought.
+"#;
+
+ let original_reader = Cursor::new(original_buffer);
+ let mut actual = Cursor::new(Vec::new());
+ suggestion.apply_to(original_reader, &mut actual).unwrap();
+
+ assert_eq!(
+ std::str::from_utf8(&actual.into_inner()).unwrap(),
+ expected,
+ );
+ }
+}