#![allow(unreachable_patterns)]
use crate::error::{ErrorKind, Result};
use crate::util;
use std::fs::File;
use std::io::{self, BufRead, BufReader, Read, Seek, Write};
use std::path::Path;
use byteseeker::ByteSeeker;
#[derive(Debug, Clone)]
pub struct RsMerger<'a> {
opts: RsMergerOptions<'a>,
}
#[derive(Clone, Debug, Default)]
struct RsMergerOptions<'a> {
skip_head: Option<Skip<'a>>,
skip_tail: Option<Skip<'a>>,
padding: Option<Pad<'a>>,
newline: Option<Newline>,
}
#[non_exhaustive]
#[derive(Debug, Clone)]
pub enum Skip<'a> {
Bytes(usize),
BytesOnce(usize),
Lines(usize),
LinesOnce(usize),
Repeats(&'a [u8]),
Until(&'a [u8]),
Before(&'a [u8]),
}
#[non_exhaustive]
#[derive(Debug, Clone)]
pub enum Pad<'a> {
Before(&'a [u8]),
Between(&'a [u8]),
After(&'a [u8]),
Custom(Option<&'a [u8]>, Option<&'a [u8]>, Option<&'a [u8]>),
}
#[derive(Debug, Clone, Copy)]
pub enum Newline {
Lf,
Crlf,
}
impl Default for Newline {
fn default() -> Self {
Newline::Lf
}
}
impl<'a> Default for RsMerger<'a> {
fn default() -> Self {
let opts = RsMergerOptions {
skip_head: None,
skip_tail: None,
padding: None,
newline: None,
};
RsMerger { opts }
}
}
impl<'a> RsMerger<'a> {
pub fn new() -> Self {
Default::default()
}
pub fn skip_head(&mut self, skip: Skip<'a>) -> &mut Self {
self.opts.skip_head = Some(skip);
self
}
pub fn skip_tail(&mut self, skip: Skip<'a>) -> &mut Self {
self.opts.skip_tail = Some(skip);
self
}
pub fn pad_with(&mut self, padding: Pad<'a>) -> &mut Self {
self.opts.padding = Some(padding);
self
}
pub fn force_ending_newline(&mut self, newline: Newline) -> &mut Self {
self.opts.newline = Some(newline);
self
}
pub fn merge_sources_into<RS, W>(&self, mut sources: Vec<RS>, writer: &mut W) -> Result<()>
where
RS: Read + Seek,
W: Write,
{
let len = sources.len();
if len == 0 {
return Err(ErrorKind::NothingPassed);
}
self.write_contents(&mut sources[0], writer, PartPos::Start)?;
for i in 1..(len - 1) {
self.write_contents(&mut sources[i], writer, PartPos::Inside)?;
}
if len > 1 {
self.write_contents(&mut sources[len - 1], writer, PartPos::End)?;
}
return Ok(());
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum PartPos {
Start,
Inside,
End,
}
impl<'a> RsMerger<'a> {
fn write_contents<RS, W>(&self, reader: &mut RS, writer: &mut W, pos: PartPos) -> Result<()>
where
RS: Read + Seek,
W: Write,
{
self.write_padding_before(writer, pos)?;
let endn = util::endswith_newline(reader)?;
let stream_len = util::seek_to_end(reader)? as usize;
util::seek_to_start(reader)?;
if !self.should_view_contents() {
io::copy(reader, writer)?;
} else {
if self.opts.skip_head.is_some() || self.opts.skip_tail.is_some() {
let mut seeker = ByteSeeker::new(reader);
seeker.reset();
let start = match &self.opts.skip_head {
None => 0,
Some(skip) => match *skip {
Skip::Bytes(n) => n,
Skip::BytesOnce(n) => match pos {
PartPos::Start => 0,
_ => n,
},
Skip::Lines(n) => match n {
0 => 0,
_ => {
let pos;
if !endn && n == 1 {
match seeker.seek_nth(b"\n", 1) {
Ok(idx) => {
pos = idx + 1;
}
Err(e) => match e.kind() {
byteseeker::ErrorKind::ByteNotFound => pos = stream_len,
_ => return Err(e.into()),
},
}
} else {
let nth = if endn { n } else { n - 1 };
match seeker.seek_nth(b"\n", nth) {
Ok(idx) => {
if endn {
pos = idx + 1;
} else {
match seeker.seek(b"\n") {
Ok(idx) => {
pos = idx + 1;
}
Err(e) => match e.kind() {
byteseeker::ErrorKind::ByteNotFound => {
pos = stream_len
}
_ => return Err(e.into()),
},
}
}
}
Err(e) => match e.kind() {
byteseeker::ErrorKind::ByteNotFound => {
return Err(ErrorKind::InvalidSkip);
}
_ => return Err(e.into()),
},
}
}
pos
}
},
Skip::LinesOnce(n) => match pos {
PartPos::Start => 0,
_ => match n {
0 => 0,
_ => {
let pos;
if !endn && n == 1 {
match seeker.seek_nth(b"\n", 1) {
Ok(idx) => {
pos = idx + 1;
}
Err(e) => match e.kind() {
byteseeker::ErrorKind::ByteNotFound => {
pos = stream_len
}
_ => return Err(e.into()),
},
}
} else {
let nth = if endn { n } else { n - 1 };
match seeker.seek_nth(b"\n", nth) {
Ok(idx) => {
if endn {
pos = idx + 1;
} else {
match seeker.seek(b"\n") {
Ok(idx) => {
pos = idx + 1;
}
Err(e) => match e.kind() {
byteseeker::ErrorKind::ByteNotFound => {
pos = stream_len
}
_ => return Err(e.into()),
},
}
}
}
Err(e) => match e.kind() {
byteseeker::ErrorKind::ByteNotFound => {
return Err(ErrorKind::InvalidSkip);
}
_ => return Err(e.into()),
},
}
}
pos
}
},
},
Skip::Until(bytes) => match seeker.seek(bytes) {
Ok(pos) => pos + bytes.len(),
Err(e) => match e.kind() {
byteseeker::ErrorKind::ByteNotFound => stream_len,
_ => return Err(e.into()),
},
},
Skip::Before(bytes) => match seeker.seek(bytes) {
Ok(pos) => pos,
Err(e) => match e.kind() {
byteseeker::ErrorKind::ByteNotFound => stream_len,
_ => return Err(e.into()),
},
},
Skip::Repeats(bytes) => {
let width = bytes.len();
match width {
0 => 0,
_ => {
let mut buf = Vec::with_capacity(width);
buf.resize(width, 0);
let mut reader = seeker.get_mut();
util::seek_to_start(&mut reader)?;
let mut bytes_match = 0;
loop {
reader.read_exact(&mut buf)?;
if &buf == bytes {
bytes_match += width;
if bytes_match == stream_len {
break;
}
} else {
break;
}
}
bytes_match
}
}
}
_ => unimplemented!(),
},
};
seeker.reset();
let end = match &self.opts.skip_tail {
None => util::seek_to_end(reader)? as usize,
Some(skip) => match *skip {
Skip::Bytes(n) => match n > stream_len {
true => return Err(ErrorKind::InvalidSkip),
false => stream_len - n,
},
Skip::BytesOnce(n) => match pos {
PartPos::End => stream_len,
_ => match n > stream_len {
true => return Err(ErrorKind::InvalidSkip),
false => stream_len - n,
},
},
Skip::Lines(n) => match n {
0 => stream_len,
_ => {
let pos;
if endn {
seeker.seek_back(b"\n")?;
}
match n {
1 => match seeker.seek_back(b"\n") {
Ok(idx) => {
pos = idx + 1;
}
Err(e) => match e.kind() {
byteseeker::ErrorKind::ByteNotFound => pos = 0,
_ => return Err(e.into()),
},
},
_ => match seeker.seek_nth_back(b"\n", n - 1) {
Ok(_) => match seeker.seek_back(b"\n") {
Ok(idx) => pos = idx + 1,
Err(e) => match e.kind() {
byteseeker::ErrorKind::ByteNotFound => pos = 0,
_ => return Err(e.into()),
},
},
Err(e) => match e.kind() {
byteseeker::ErrorKind::ByteNotFound => {
return Err(ErrorKind::InvalidSkip)
}
_ => return Err(e.into()),
},
},
}
pos
}
},
Skip::LinesOnce(n) => match pos {
PartPos::End => stream_len,
_ => match n {
0 => stream_len,
_ => {
let pos;
if endn {
seeker.seek_back(b"\n")?;
}
match n {
1 => match seeker.seek_back(b"\n") {
Ok(idx) => {
pos = idx + 1;
}
Err(e) => match e.kind() {
byteseeker::ErrorKind::ByteNotFound => pos = 0,
_ => return Err(e.into()),
},
},
_ => match seeker.seek_nth_back(b"\n", n - 1) {
Ok(_) => match seeker.seek_back(b"\n") {
Ok(idx) => pos = idx + 1,
Err(e) => match e.kind() {
byteseeker::ErrorKind::ByteNotFound => pos = 0,
_ => return Err(e.into()),
},
},
Err(e) => match e.kind() {
byteseeker::ErrorKind::ByteNotFound => {
return Err(ErrorKind::InvalidSkip)
}
_ => return Err(e.into()),
},
},
}
pos
}
},
},
Skip::Until(bytes) => match seeker.seek_back(bytes) {
Ok(pos) => pos,
Err(e) => match e.kind() {
byteseeker::ErrorKind::ByteNotFound => 0,
_ => return Err(e.into()),
},
},
Skip::Before(bytes) => match seeker.seek(bytes) {
Ok(pos) => pos + bytes.len(),
Err(e) => match e.kind() {
byteseeker::ErrorKind::ByteNotFound => 0,
_ => return Err(e.into()),
},
},
Skip::Repeats(bytes) => {
let width = bytes.len();
match width {
0 => stream_len,
_ => {
let mut buf = Vec::with_capacity(width);
buf.resize(width, 0);
let mut reader = seeker.get_mut();
util::seek_to_end(&mut reader)?;
let mut bytes_match = 0;
loop {
if bytes_match + width > stream_len {
break;
}
util::seek_end(-((bytes_match + width) as i64), reader)?;
reader.read_exact(&mut buf)?;
if &buf == bytes {
bytes_match += width;
if bytes_match == stream_len {
break;
}
} else {
break;
}
}
stream_len - bytes_match
}
}
}
},
};
match (start, end) {
_ if end < start => return Err(ErrorKind::InvalidSkip),
_ if start == end => (),
_ => {
let bytes_count = end - start;
match bytes_count {
0 => (),
_ => {
let mut buf_reader = BufReader::new(reader);
let mut read = 0;
util::seek_start(start as u64, &mut buf_reader)?;
loop {
let buf = buf_reader.fill_buf()?;
let length = buf.len();
if length == 0 {
break;
}
if read + length > bytes_count {
let mut buffer = buf.to_owned();
buffer.truncate(bytes_count - read);
writer.write_all(&buffer)?;
buf_reader.consume(length);
} else {
read += length;
writer.write_all(buf)?;
buf_reader.consume(length);
}
}
}
}
}
}
} else {
io::copy(reader, writer)?;
}
}
if self.opts.newline.is_some() && !endn {
match self.opts.newline.unwrap() {
Newline::Lf => {
writer.write_all(b"\n")?;
}
Newline::Crlf => {
writer.write_all(b"\r\n")?;
}
}
}
self.write_padding_after(writer, pos)?;
Ok(())
}
fn write_padding_before<W: Write>(&self, writer: &mut W, pos: PartPos) -> Result<()> {
if let Some(pad) = &self.opts.padding {
match (pad, pos) {
(Pad::Before(padding), PartPos::Start) => {
writer.write_all(padding)?;
}
(Pad::Custom(Some(padding), _, _), PartPos::Start) => {
writer.write_all(padding)?;
}
_ => (),
}
}
Ok(())
}
fn write_padding_after<W: Write>(&self, writer: &mut W, pos: PartPos) -> Result<()> {
if let Some(pad) = &self.opts.padding {
match (pad, pos) {
(Pad::After(padding), PartPos::End) => {
writer.write_all(padding)?;
}
(Pad::Between(padding), PartPos::Start) => {
writer.write_all(padding)?;
}
(Pad::Between(padding), PartPos::Inside) => {
writer.write_all(padding)?;
}
(Pad::Custom(_, Some(padding), _), PartPos::Start) => {
writer.write_all(padding)?;
}
(Pad::Custom(_, Some(padding), _), PartPos::Inside) => {
writer.write_all(padding)?;
}
(Pad::Custom(_, _, Some(padding)), PartPos::End) => {
writer.write_all(padding)?;
}
_ => (),
}
}
Ok(())
}
fn should_view_contents(&self) -> bool {
self.opts.newline.is_some()
|| self.opts.skip_head.is_some()
|| self.opts.skip_tail.is_some()
}
}
#[derive(Clone, Debug)]
pub struct FileMerger<'a>(RsMerger<'a>);
impl<'a> Default for FileMerger<'a> {
fn default() -> Self {
let opts = RsMergerOptions {
skip_head: None,
skip_tail: None,
padding: None,
newline: None,
};
FileMerger(RsMerger { opts })
}
}
impl<'a> FileMerger<'a> {
pub fn new() -> Self {
Default::default()
}
pub fn skip_head(&mut self, skip: Skip<'a>) -> &mut Self {
self.0.opts.skip_head = Some(skip);
self
}
pub fn skip_tail(&mut self, skip: Skip<'a>) -> &mut Self {
self.0.opts.skip_tail = Some(skip);
self
}
pub fn pad_with(&mut self, padding: Pad<'a>) -> &mut Self {
self.0.opts.padding = Some(padding);
self
}
pub fn force_ending_newline(&mut self, newline: Newline) -> &mut Self {
self.0.opts.newline = Some(newline);
self
}
pub fn with_paths<P, W>(&self, paths: Vec<P>, writer: &mut W) -> Result<()>
where
P: AsRef<Path>,
W: Write,
{
let sources: Result<Vec<_>> = paths
.into_iter().enumerate()
.map(|(i, p)| File::open(p).map_err(|_| ErrorKind::InvalidPath(i)))
.collect();
self.with_files(sources?, writer)
}
pub fn with_paths_lossy<P, W>(&self, paths: Vec<P>, writer: &mut W) -> Result<()>
where
P: AsRef<Path>,
W: Write,
{
let sources: Vec<_> = paths.into_iter().filter(|p| p.as_ref().is_file()).collect();
self.with_paths(sources, writer)
}
pub fn with_files<W>(&self, files: Vec<File>, writer: &mut W) -> Result<()>
where
W: Write,
{
self.0.merge_sources_into(files, writer)
}
}