Skip to content

Commit

Permalink
Prepare for testing
Browse files Browse the repository at this point in the history
  • Loading branch information
hedgar2017 committed Dec 12, 2018
1 parent a210e15 commit d96b00d
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 29 deletions.
32 changes: 17 additions & 15 deletions peg/patch.peg
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,35 @@ path_timestamp_delimiter = { "\t" }

// Values
path = { (!"\t" ~ ANY)+ }
timestamp = { line }
number = { NUMBER+ }
file1_l = { number }
file1_s = { number }
file2_l = { number }
file2_s = { number }
file1_l = { NUMBER+ }
file1_s = { NUMBER+ }
file2_l = { NUMBER+ }
file2_s = { NUMBER+ }

// Lines
line = { (!NEWLINE ~ ANY)* ~ NEWLINE }
line_context = { context_line_start ~ line }
line_deleted = { deleted_line_start ~ line }
line_inserted = { inserted_line_start ~ line }
line = { (!NEWLINE ~ ANY)* }
timestamp = { line }
line_context = { line }
line_deleted = { line }
line_inserted = { line }

// Headers
file1_header = { file1_header_start ~ SPACE_SEPARATOR ~ path ~ path_timestamp_delimiter ~ timestamp }
file2_header = { file2_header_start ~ SPACE_SEPARATOR ~ path ~ path_timestamp_delimiter ~ timestamp }
file1_header = { file1_header_start ~ SPACE_SEPARATOR ~ path ~ path_timestamp_delimiter ~ timestamp ~ NEWLINE }
file2_header = { file2_header_start ~ SPACE_SEPARATOR ~ path ~ path_timestamp_delimiter ~ timestamp ~ NEWLINE }
context_header = {
context_header_delimiter ~ SPACE_SEPARATOR ~
"-" ~ file1_l ~ "," ~ file1_s ~ SPACE_SEPARATOR ~
"+" ~ file2_l ~ "," ~ file2_s ~ SPACE_SEPARATOR ~
context_header_delimiter ~ SPACE_SEPARATOR? ~ line
context_header_delimiter ~ SPACE_SEPARATOR? ~ line ~ NEWLINE
}

// Sections
context = {
context_header ~
(line_context | line_deleted | line_inserted)+
context_header ~ (
(context_line_start ~ line_context ~ NEWLINE) |
(deleted_line_start ~ line_deleted ~ NEWLINE) |
(inserted_line_start ~ line_inserted ~ NEWLINE)
)+
}
patch = {
SOI ~
Expand Down
8 changes: 4 additions & 4 deletions src/bin/patch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
//! The GNU patch Rust binary entry point.
//!

extern crate patch_rs;
extern crate clap;
extern crate patch_rs;

use std::{
fs,
Expand Down Expand Up @@ -44,13 +44,13 @@ fn main() -> PatchResult<()> {
let file = args.value_of("file").unwrap();
let patch = args.value_of("patch").unwrap();

println!("patching {}", file);

let text = read_to_vec(file)?;
let patch = fs::read_to_string(patch)?;

let parser = PatchParser::new(&text, &patch);
let _ = parser.process(&|s| println!("{}", s))?;
for s in parser.process()? {
println!("{}", s);
}

Ok(())
}
21 changes: 17 additions & 4 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//! The error chain.
//!

use std::io;
use std::{io, num};

use parser::Rule;

Expand All @@ -13,13 +13,26 @@ error_chain! {

foreign_links {
Reading(io::Error);
Parsing(pest::error::Error<Rule>);
ParsingPatch(pest::error::Error<Rule>);
ParsingContext(num::ParseIntError);
}

errors {
NotFound(t: &'static str) {
NotFound(desc: &'static str) {
description("Element is not found")
display("Missing an element: {}", t)
display("Missing an element: {}", desc)
}
MalformedPatch(desc: &'static str) {
description("Malformed patch")
display("Elements are found in invalid order: {}", desc)
}
AbruptInput(line: usize) {
description("Abrupt input file")
display("Line #{} not found", line)
}
PatchInputMismatch(line: usize) {
description("Patch does not match the input file")
display("Invalid line #{}", line)
}
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ extern crate pest;
extern crate pest_derive;

pub use {
error::{PatchError, PatchResult},
parser::PatchParser,
error::{PatchResult, PatchError},
};
119 changes: 114 additions & 5 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
//! The parser implementation.
//!

use std::fmt;

use pest::{iterators::Pair, Parser};

use super::error::{PatchResult, PatchErrorKind};
use super::error::{PatchErrorKind, PatchResult};

#[derive(Parser)]
#[grammar = "../peg/patch.peg"]
Expand All @@ -13,16 +15,123 @@ pub struct PatchParser<'a> {
patch: &'a str,
}

#[derive(Default)]
struct ContextHeader {
pub file1_l: usize,
pub file1_s: usize,
pub file2_l: usize,
pub file2_s: usize,
}

impl fmt::Display for ContextHeader {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"-{},{} +{},{}",
self.file1_l, self.file1_s, self.file2_l, self.file2_s,
)?;
Ok(())
}
}

impl<'a> PatchParser<'a> {
pub fn new(text: &'a [String], patch: &'a str) -> Self {
Self { text, patch }
}

pub fn process(&self, callback: &Fn(String) -> ()) -> PatchResult<()> {
pub fn process(&self) -> PatchResult<Vec<String>> {
let patch = Self::parse(Rule::patch, self.patch)?
.next()
.ok_or(PatchErrorKind::NotFound("patch"))?
.into_inner();
Ok(())
.ok_or(PatchErrorKind::NotFound("patch"))?;

let mut file2_text = Vec::new();
let mut file1_ptr: usize = 0;

for patch_element in patch.into_inner() {
match patch_element.as_rule() {
Rule::context => {
let mut context = patch_element.into_inner();
let context_header = context
.next()
.ok_or(PatchErrorKind::NotFound("context_header"))?;
let context_header = if let Rule::context_header = context_header.as_rule() {
Self::get_context_header(context_header)?
} else {
return Err(PatchErrorKind::MalformedPatch(
"Context header is not at the start of a context",
)
.into());
};
for i in file1_ptr..context_header.file1_l {
file2_text.push(
self.text
.get(i)
.ok_or(PatchErrorKind::AbruptInput(i))?
.to_owned(),
);
}
file1_ptr = context_header.file1_l;
for line in context {
match line.as_rule() {
Rule::line_context => {
if self
.text
.get(file1_ptr)
.ok_or(PatchErrorKind::AbruptInput(file1_ptr))?
!= line.as_span().as_str()
{
return Err(PatchErrorKind::PatchInputMismatch(file1_ptr).into());
}
file2_text.push(line.as_span().as_str().to_owned());
file1_ptr += 1;
}
Rule::line_deleted => {
if self
.text
.get(file1_ptr)
.ok_or(PatchErrorKind::AbruptInput(file1_ptr))?
!= line.as_span().as_str()
{
return Err(PatchErrorKind::PatchInputMismatch(file1_ptr).into());
}
file1_ptr += 1;
}
Rule::line_inserted => {
file2_text.push(line.as_span().as_str().to_owned());
}
_ => {}
}
}
}
_ => {}
}
}

for i in file1_ptr..self.text.len() {
file2_text.push(
self.text
.get(i)
.ok_or(PatchErrorKind::AbruptInput(i))?
.to_owned(),
);
}

Ok(file2_text)
}

fn get_context_header(header: Pair<'_, Rule>) -> PatchResult<ContextHeader> {
let mut output = ContextHeader::default();
for header_element in header.into_inner() {
match header_element.as_rule() {
Rule::file1_l => output.file1_l = header_element.as_span().as_str().parse()?,
Rule::file1_s => output.file1_s = header_element.as_span().as_str().parse()?,
Rule::file2_l => output.file2_l = header_element.as_span().as_str().parse()?,
Rule::file2_s => output.file2_s = header_element.as_span().as_str().parse()?,
_ => {}
}
}
output.file1_l -= 1;
output.file2_l -= 1;
Ok(output)
}
}

0 comments on commit d96b00d

Please sign in to comment.