use std::io::{self, Write};
use std::fs;
pub enum Input<'a> {
StdIn,
File(&'a str),
Buffer(&'a str),
}
pub enum Output<'b> {
StdOut,
File(&'b str),
Buffer(&'b mut String),
}
pub enum EditType {
Append,
Insert,
Replace,
}
struct Editor<'c> {
newline_count_trigger: u8,
new_text: &'c str,
edit_type: EditType,
current_count: u8,
buffer: String
}
impl<'c> Default for Editor<'c> {
fn default() -> Self {
Editor {
newline_count_trigger: 2,
new_text: "-------\n",
edit_type: EditType::Append,
current_count: 0,
buffer: String::new(),
}
}
}
impl<'c> Editor<'c> {
fn add_line(&mut self, line: &str) {
self.buffer += line;
if line == "\n" {
self.current_count += 1;
if self.current_count == self.newline_count_trigger {
self.current_count = 0;
match &self.edit_type {
EditType::Append => self.buffer += self.new_text,
EditType::Insert => {
self.buffer.insert_str(0, self.new_text);
}
EditType::Replace => {
self.buffer.replace_range(.., self.new_text);
}
}
}
} else {
self.current_count = 0;
}
}
fn try_output(&mut self) -> Option<String> {
if self.current_count == 0 {
Some(self.buffer.drain(..).collect())
} else {
None
}
}
fn get_remaining_output(&mut self) -> Option<String> {
if !self.buffer.is_empty() {
Some(self.buffer.drain(..).collect())
} else {
None
}
}
}
pub struct LinurgyBuilder<'a, 'b, 'c> {
input: Input<'a>,
output: Output<'b>,
editor: Editor<'c>,
file: Option<fs::File>,
}
impl Default for LinurgyBuilder<'_, '_, '_> {
fn default() -> Self {
LinurgyBuilder {
input: Input::StdIn,
output: Output::StdOut,
editor: Editor::default(),
file: None,
}
}
}
impl<'a, 'b, 'c> LinurgyBuilder<'a, 'b, 'c> {
pub fn new() -> Self {
Self::default()
}
pub fn set_input(&mut self, input: Input<'a>) -> &mut Self {
self.input = input;
self
}
pub fn set_output(&mut self, output: Output<'b>) -> &mut Self {
self.output = output;
self
}
pub fn set_newline_trigger(&mut self, count: u8) -> &mut Self {
self.editor.newline_count_trigger = count;
self
}
pub fn set_new_text(&mut self, new_text: &'c str) -> &mut Self {
self.editor.new_text = new_text;
self
}
pub fn set_edit_type(&mut self, edit_type: EditType) -> &mut Self {
self.editor.edit_type = edit_type;
self
}
pub fn run(&mut self) -> &mut Self {
if let Output::File(path) = &self.output {
self.file = Some(fs::File::create(path).expect("Create file"))
}
match self.input {
Input::StdIn => {
let stdin = io::stdin();
let reader = stdin.lock();
self.process(reader);
}
Input::File(name) => {
let file = fs::File::open(name).expect("Unable to open file");
let reader = io::BufReader::new(file);
self.process(reader);
}
Input::Buffer(buffer) => {
let reader = io::Cursor::new(buffer);
self.process(reader);
}
}
self
}
fn process(&mut self, reader: impl io::BufRead) {
for line in reader.lines() {
let line = line.unwrap() + "\n";
self.editor.add_line(&line);
let edited_text = self.editor.try_output();
self.write(edited_text);
}
let edited_text = self.editor.get_remaining_output();
self.write(edited_text);
}
fn write(&mut self, edited_text: Option<String>) {
if let Some(text) = edited_text {
match self.output {
Output::StdOut => print!("{}", &text),
Output::File(_) => {
if let Some(file) = &mut self.file {
file.write_all(&text.as_bytes())
.expect("Write to file")
}
}
Output::Buffer(ref mut buffer) => buffer.push_str(&text),
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_editor() {
let editor = Editor::default();
assert_eq!(2, editor.newline_count_trigger);
assert_eq!(0, editor.current_count);
assert_eq!("", editor.buffer);
assert_eq!("-------\n", editor.new_text);
if let EditType::Append = editor.edit_type {
assert!(true);
} else {
assert!(false, "Correct type not implemented");
}
}
#[test]
fn default_linurgy_builder() {
let lb = LinurgyBuilder::new();
let editor = Editor::default();
if let Input::StdIn = lb.input {
assert!(true);
} else {
assert!(false, "Correct type not implemented");
}
if let Output::StdOut = lb.output {
assert!(true);
} else {
assert!(false, "Correct type not implemented");
}
assert_eq!(editor.new_text, lb.editor.new_text);
}
#[test]
fn linurgy_set_input() {
let buffer = String::from("Test builder");
let mut lb = LinurgyBuilder::new();
lb.set_input(Input::Buffer(&buffer));
match lb.input {
Input::Buffer(text) => assert_eq!(&buffer, text),
_ => assert!(false, "Correct type not implemented"),
}
lb.set_input(Input::File("filename"));
match lb.input {
Input::File(text) => assert_eq!("filename", text),
_ => assert!(false, "Correct type not implemented"),
}
lb.set_input(Input::StdIn);
match lb.input {
Input::StdIn => assert!(true),
_ => assert!(false, "Correct type not implemented"),
}
}
#[test]
fn linurgy_set_output() {
let mut buffer = String::from("Test builder");
let mut buffer2 = String::from("Test builder");
let mut lb = LinurgyBuilder::new();
lb.set_output(Output::Buffer(&mut buffer));
match lb.output {
Output::Buffer(ref text) => assert_eq!(&&mut buffer2, text),
_ => assert!(false, "Correct type not implemented"),
}
lb.set_output(Output::File("filename"));
match lb.output {
Output::File(text) => assert_eq!("filename", text),
_ => assert!(false, "Correct type not implemented"),
}
lb.set_output(Output::StdOut);
match lb.output {
Output::StdOut => assert!(true),
_ => assert!(false, "Correct type not implemented"),
}
}
#[test]
fn linurgy_set_newline_trigger() {
let mut lb = LinurgyBuilder::new();
lb.set_newline_trigger(5);
assert_eq!(5, lb.editor.newline_count_trigger);
}
#[test]
fn linurgy_set_new_text() {
let mut lb = LinurgyBuilder::new();
lb.set_new_text("cheese");
assert_eq!("cheese", lb.editor.new_text);
}
#[test]
fn linurgy_set_edit_type() {
let mut lb = LinurgyBuilder::new();
lb.set_edit_type(EditType::Insert);
if let EditType::Insert = lb.editor.edit_type {
assert!(true);
} else {
assert!(false, "Correct type not implemented");
}
}
#[test]
fn editor_add_line() {
let mut ed = Editor::default();
let line = String::from("test text\n");
ed.add_line(&line);
assert_eq!("test text\n", ed.buffer);
assert_eq!(0, ed.current_count);
let line = String::from(" more\n");
ed.add_line(&line);
assert_eq!("test text\n more\n", ed.buffer);
assert_eq!(0, ed.current_count);
let line = String::from("\n");
ed.add_line(&line);
assert_eq!("test text\n more\n\n", ed.buffer);
assert_eq!(1, ed.current_count);
let line = String::from("\n");
ed.add_line(&line);
assert_eq!("test text\n more\n\n\n-------\n", ed.buffer);
assert_eq!(0, ed.current_count);
}
#[test]
fn editor_add_line_with_diff_edit_type() {
let mut ed = Editor::default();
ed.edit_type = EditType::Insert;
let line = String::from("\n");
ed.add_line(&line);
ed.add_line(&line);
assert_eq!("-------\n\n\n", ed.buffer);
let mut ed = Editor::default();
ed.edit_type = EditType::Replace;
let line = String::from("\n");
ed.add_line(&line);
ed.add_line(&line);
assert_eq!("-------\n", ed.buffer);
}
#[test]
fn editor_try_output() {
let mut ed = Editor::default();
assert_eq!(Some(String::from("")), ed.try_output());
let line = String::from("\n");
ed.add_line(&line);
assert_eq!(None, ed.try_output());
ed.add_line(&line);
assert_eq!(Some(String::from("\n\n-------\n")), ed.try_output());
assert_eq!(Some(String::from("")), ed.try_output());
let line = String::from("test\n");
ed.add_line(&line);
assert_eq!(Some(String::from("test\n")), ed.try_output());
}
#[test]
fn linurgy_write() {
let mut output = String::new();
let mut lb = LinurgyBuilder::new();
lb.set_output(Output::Buffer(&mut output));
let test_line = None;
lb.write(test_line);
assert_eq!("", output);
let mut output = String::new();
let mut lb = LinurgyBuilder::new();
lb.set_output(Output::Buffer(&mut output));
let test_line = Some(String::from("testline\n"));
lb.write(test_line);
assert_eq!("testline\n", output);
let mut output = String::new();
let mut lb = LinurgyBuilder::new();
lb.set_output(Output::Buffer(&mut output));
let test_line = Some(String::from("testline\n"));
lb.write(test_line);
let test_line = Some(String::from("testline\n"));
lb.write(test_line);
assert_eq!("testline\ntestline\n", output);
}
#[test]
fn linurgy_process() {
let mut output = String::new();
let mut lb = LinurgyBuilder::new();
lb.set_output(Output::Buffer(&mut output));
let input = String::from("test\nlines\n");
let reader = io::Cursor::new(&input);
lb.process(reader);
assert_eq!("test\nlines\n", &output);
let mut output = String::new();
let mut lb = LinurgyBuilder::new();
lb.set_output(Output::Buffer(&mut output));
let input = String::from("\n\n");
let reader = io::Cursor::new(&input);
lb.process(reader);
assert_eq!("\n\n-------\n", &output);
let mut output = String::new();
let mut lb = LinurgyBuilder::new();
lb.set_output(Output::Buffer(&mut output));
let input = String::from("\n\n test post text\n\n");
let reader = io::Cursor::new(&input);
lb.process(reader);
assert_eq!("\n\n-------\n test post text\n\n", &output);
}
#[test]
fn linurgy_run() {
let input = String::from("test\nlines\n");
let mut output = String::new();
let mut lb = LinurgyBuilder::new();
lb.set_input(Input::Buffer(&input));
lb.set_output(Output::Buffer(&mut output));
lb.run();
assert_eq!("test\nlines\n", &output);
}
}