Add fixer
This commit is contained in:
parent
c91a704406
commit
31fa74a342
@ -1,3 +1,4 @@
|
||||
#[derive(Clone)]
|
||||
pub(crate) enum FileFilter {
|
||||
None,
|
||||
Extension(ExtensionFilter),
|
||||
@ -16,6 +17,7 @@ impl FileFilter {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct ExtensionFilter {
|
||||
ext: String,
|
||||
}
|
||||
|
||||
82
src/fixer.rs
Normal file
82
src/fixer.rs
Normal file
@ -0,0 +1,82 @@
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{Read, Seek, SeekFrom, Write},
|
||||
};
|
||||
|
||||
use crate::stats::LineSep;
|
||||
|
||||
pub(crate) struct Fixer {
|
||||
file_name: String,
|
||||
to: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Fixer {
|
||||
pub(crate) fn new(file_name: String, to: LineSep) -> Fixer {
|
||||
let to = match to {
|
||||
LineSep::Lf => vec![b'\n'],
|
||||
LineSep::CrLf => vec![b'\r', b'\n'],
|
||||
LineSep::Cr => vec![b'\r'],
|
||||
};
|
||||
|
||||
Fixer { file_name, to }
|
||||
}
|
||||
|
||||
pub(crate) fn fix(&mut self) {
|
||||
let file = File::options().read(true).write(true).open(&self.file_name);
|
||||
if file.is_err() {
|
||||
println!("Could not open file: {}", self.file_name);
|
||||
return;
|
||||
}
|
||||
|
||||
let mut file = file.unwrap();
|
||||
let mut buf = Vec::new();
|
||||
let mut write_buf = Vec::new();
|
||||
let bytes_read = file.read_to_end(&mut buf);
|
||||
|
||||
if bytes_read.is_err() {
|
||||
println!("Could not read file: {}", self.file_name);
|
||||
return;
|
||||
}
|
||||
|
||||
let bytes_read = bytes_read.unwrap();
|
||||
let mut next_read_head = 0;
|
||||
|
||||
loop {
|
||||
if next_read_head == bytes_read {
|
||||
break;
|
||||
}
|
||||
|
||||
next_read_head = match buf[next_read_head] {
|
||||
b'\r' => {
|
||||
// LL(1) to see if the next byte is '\n'
|
||||
if next_read_head < bytes_read && buf[next_read_head] == b'\n' {
|
||||
write_buf.extend(&self.to);
|
||||
next_read_head + 2
|
||||
} else {
|
||||
next_read_head + 1
|
||||
}
|
||||
}
|
||||
b'\n' => {
|
||||
write_buf.extend(&self.to);
|
||||
next_read_head + 1
|
||||
}
|
||||
any_other_byte => {
|
||||
write_buf.push(any_other_byte);
|
||||
next_read_head + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let seeked = file.seek(SeekFrom::Start(0));
|
||||
if seeked.is_err() {
|
||||
println!("Could not seek to start of file: {}", self.file_name);
|
||||
return;
|
||||
}
|
||||
|
||||
let written = file.write_all(&write_buf);
|
||||
if written.is_err() {
|
||||
println!("Could not write to file: {}", self.file_name);
|
||||
println!("Error: {:?}", written.err());
|
||||
}
|
||||
}
|
||||
}
|
||||
45
src/main.rs
45
src/main.rs
@ -1,7 +1,9 @@
|
||||
use argh::FromArgs;
|
||||
use filters::FileFilter;
|
||||
use stats::{FileNames, FileStats, FileStatsAggregate};
|
||||
use fixer::Fixer;
|
||||
use stats::{FileNames, FileStats, FileStatsAggregate, LineSep};
|
||||
mod filters;
|
||||
mod fixer;
|
||||
mod stats;
|
||||
|
||||
fn main() {
|
||||
@ -12,10 +14,32 @@ fn main() {
|
||||
None => FileFilter::None,
|
||||
};
|
||||
|
||||
let stats = FileNames::generate(args.dir, filter, args.recursive)
|
||||
let stats = FileNames::generate(args.dir.clone(), filter.clone(), args.recursive)
|
||||
.filter_map(FileStats::generate)
|
||||
.fold(FileStatsAggregate::new(), FileStatsAggregate::fold);
|
||||
stats.print_table();
|
||||
|
||||
if args.fix {
|
||||
let target = match args.target {
|
||||
Some(target) => {
|
||||
println!("Fixing line endings to provided line ending - {:}", target);
|
||||
target
|
||||
}
|
||||
None => match stats.max() {
|
||||
Some(max) => {
|
||||
println!("Fixing line endings to most common line ending - {:}", max);
|
||||
max
|
||||
}
|
||||
None => {
|
||||
panic!("No line endings found to fix");
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
FileNames::generate(args.dir, filter, args.recursive)
|
||||
.map(|file_name| Fixer::new(file_name, target.clone()))
|
||||
.for_each(|mut fixer| fixer.fix());
|
||||
}
|
||||
}
|
||||
|
||||
/// Line endings fixer tool
|
||||
@ -32,4 +56,21 @@ struct Args {
|
||||
/// recursively traverse directories
|
||||
#[argh(switch, short = 'r')]
|
||||
recursive: bool,
|
||||
|
||||
/// fix line endings
|
||||
#[argh(switch, short = 'f')]
|
||||
fix: bool,
|
||||
|
||||
/// target line ending, applicable only with -f
|
||||
#[argh(option, short = 't', from_str_fn(parse_line_sep))]
|
||||
target: Option<LineSep>,
|
||||
}
|
||||
|
||||
fn parse_line_sep(s: &str) -> Result<LineSep, String> {
|
||||
match s {
|
||||
"lf" => Ok(LineSep::Lf),
|
||||
"crlf" => Ok(LineSep::CrLf),
|
||||
"cr" => Ok(LineSep::Cr),
|
||||
_ => Err("Invalid line ending".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,6 +24,7 @@ impl FileStatsAggregate {
|
||||
lines: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn fold(mut accumulator: FileStatsAggregate, stat: FileStats) -> FileStatsAggregate {
|
||||
accumulator.crlf += stat.crlf;
|
||||
accumulator.cr += stat.cr;
|
||||
@ -45,6 +46,10 @@ impl FileStatsAggregate {
|
||||
accumulator
|
||||
}
|
||||
|
||||
pub(crate) fn max(&self) -> Option<LineSep> {
|
||||
self.max.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn print_table(&self) {
|
||||
println!(
|
||||
"{:<4} | {:<4} | {:<4} | {:<4} | {:<4} | {}",
|
||||
@ -164,7 +169,7 @@ impl FileStats {
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum LineSep {
|
||||
pub(crate) enum LineSep {
|
||||
Lf,
|
||||
CrLf,
|
||||
Cr,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user