Make ChainRules dependent on the Write

Fri, 20 Oct 2023 14:59:04 -0500

author
Tuomo Valkonen <tuomov@iki.fi>
date
Fri, 20 Oct 2023 14:59:04 -0500
changeset 7
68538da191c7
parent 6
de1cf8032322
child 8
945a396340d2

Make ChainRules dependent on the Write

src/main.rs file | annotate | diff | comparison | revisions
--- a/src/main.rs	Thu Oct 19 23:25:34 2023 -0500
+++ b/src/main.rs	Fri Oct 20 14:59:04 2023 -0500
@@ -5,12 +5,12 @@
 
 use std::io;
 use std::fs::File;
-use std::io::{BufWriter, BufRead, BufReader};
-use std::io::Write;
+use std::io::{BufWriter, BufRead, BufReader, Write, Read};
+use std::fmt::Debug;
 use clap::Parser;
 
 /// Command line parameters
-#[derive(Parser, Debug)]
+#[derive(Parser, Debug, Clone)]
 #[clap(
     about = env!("CARGO_PKG_DESCRIPTION"),
     author = env!("CARGO_PKG_AUTHORS"),
@@ -28,7 +28,7 @@
     config : Config
 }
 
-#[derive(Parser, Debug)]
+#[derive(Parser, Debug, Clone)]
 struct Config {
     #[arg(long, short = 'c')]
     /// Strip comments
@@ -45,33 +45,34 @@
     cli : Config
 }
 
-type AnyChainRule = Box<dyn ChainRule>;
-type AnyNestedRule = Box<dyn NestedRule>;
+type AnyChainRule<W> = Box<dyn ChainRule<W>>;
+type AnyNestedRule<W> = Box<dyn NestedRule<W>>;
 
-trait ChainRule {
-    fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule;
-    fn flush(self : Box<Self>, ctx : &Context);
+trait ChainRule<W> {
+    fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule<W>;
+    fn flush(self : Box<Self>, ctx : &Context) -> W;
 }
 
-trait NestedRule : ChainRule {
+trait NestedRule<W : Write> : ChainRule<W> {
     fn produce(&mut self, c : char, ctx : &Context);
-    fn next(self : Box<Self>) -> AnyChainRule;
+    fn next(self : Box<Self>) -> AnyChainRule<W>;
     fn produce_string(&mut self, s : String, ctx : &Context) {
         s.chars().for_each(|c| self.produce(c, ctx));
     }
     fn start_ignored_comment(&mut self, c : char);
 }
 
-impl<W : Write + 'static> ChainRule for Out<W> {
-    fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule {
+impl<W : Write + 'static> ChainRule<W> for Out<W> {
+    fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule<W> {
         basic_consume(self, c, ctx, true)
     }
-    fn flush(mut self : Box<Self>, _ctx : &Context) {
+    fn flush(mut self : Box<Self>, _ctx : &Context) -> W {
         self.output.flush().unwrap();
+        self.output
     }
 }
 
-impl<W : Write + 'static> NestedRule for Out<W> {
+impl<W : Write + 'static> NestedRule<W> for Out<W> {
     fn produce(&mut self, c : char, ctx : &Context) {
         if c == '\n' {
             self.line_end(ctx.cli.strip_whitespace, ctx.input_only_ws)
@@ -86,7 +87,7 @@
         }
     }
 
-    fn next(self : Box<Self>) -> AnyChainRule {
+    fn next(self : Box<Self>) -> AnyChainRule<W> {
         self
     }
 
@@ -103,8 +104,12 @@
     }
 }
 
-fn basic_consume(mut s : AnyNestedRule, c : char, ctx : &Context, print_end : bool)
--> AnyChainRule {
+fn basic_consume<W : Write + 'static>(
+    mut s : AnyNestedRule<W>,
+    c : char,
+    ctx : &Context,
+    print_end : bool
+) -> AnyChainRule<W> {
     match c {
         '{' => {
             s.produce(c, ctx);
@@ -135,13 +140,13 @@
     }
 }
 
-struct CommandName {
-    parent : AnyNestedRule,
+struct CommandName<W : Write> {
+    parent : AnyNestedRule<W>,
     command : String
 }
 
-impl ChainRule for CommandName {
-    fn consume(mut self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule {
+impl<W : Write + 'static> ChainRule<W> for CommandName<W> {
+    fn consume(mut self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule<W> {
         match c {
             '}' | '{' | '\\' if self.command.len() <= 1 => {
                 self.command.push(c);
@@ -158,14 +163,14 @@
         }
     }
 
-    fn flush(self : Box<Self>, ctx : &Context) {
+    fn flush(self : Box<Self>, ctx : &Context) -> W {
         self.handle(ctx)
             .flush(ctx)
     }
 }
 
-impl CommandName {
-    fn handle(mut self, ctx : &Context) -> AnyChainRule {
+impl<W : Write + 'static> CommandName<W> {
+    fn handle(mut self, ctx : &Context) -> AnyChainRule<W> {
         match self.command.as_str() {
             "\\added" => {
                 Scan::new(Added(self.parent))
@@ -184,10 +189,10 @@
     }
 }
 
-struct Comment(AnyNestedRule);
+struct Comment<W : Write>(AnyNestedRule<W>);
 
-impl ChainRule for Comment {
-    fn consume(mut self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule {
+impl<W : Write + 'static> ChainRule<W> for Comment<W> {
+    fn consume(mut self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule<W> {
         if c == '\n' {
             self.0.consume(c, ctx)
         } else {
@@ -195,42 +200,42 @@
             self
         }
     }
-    fn flush(self : Box<Self>, ctx : &Context) {
+    fn flush(self : Box<Self>, ctx : &Context) -> W {
         self.0.flush(ctx)
     }
 }
 
-struct IgnoreComment(AnyChainRule);
+struct IgnoreComment<W : Write>(AnyChainRule<W>);
 
-impl ChainRule for IgnoreComment {
-    fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule {
+impl<W : Write +'static> ChainRule<W> for IgnoreComment<W> {
+    fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule<W> {
         if c == '\n' {
             self.0.consume(c, ctx)
         } else {
             self
         }
     }
-    fn flush(self : Box<Self>, ctx : &Context) {
+    fn flush(self : Box<Self>, ctx : &Context) -> W {
         self.0.flush(ctx)
     }
 }
 
-struct Group(AnyNestedRule);
+struct Group<W : Write>(AnyNestedRule<W>);
 
-impl ChainRule for Group {
-    fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule {
+impl<W : Write + 'static> ChainRule<W> for Group<W> {
+    fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule<W> {
         basic_consume(self, c, ctx, true)
     }
-    fn flush(self : Box<Self>, ctx : &Context) {
+    fn flush(self : Box<Self>, ctx : &Context) -> W {
         self.0.flush(ctx)
     }
 }
 
-impl NestedRule for Group {
+impl<W : Write + 'static> NestedRule<W> for Group<W> {
     fn produce(&mut self, c : char, ctx : &Context) {
         self.0.produce(c, ctx)
     }
-    fn next(self : Box<Self>) -> AnyChainRule {
+    fn next(self : Box<Self>) -> AnyChainRule<W> {
         self.0
     }
     fn start_ignored_comment(&mut self, c : char) {
@@ -238,43 +243,43 @@
     }
 }
 
-struct Added(AnyNestedRule);
+struct Added<W : Write>(AnyNestedRule<W>);
 
-impl ChainRule for Added {
-    fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule {
+impl<W : Write + 'static> ChainRule<W> for Added<W> {
+    fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule<W> {
         basic_consume(self, c, ctx, false)
     }
-    fn flush(self : Box<Self>, ctx : &Context) {
+    fn flush(self : Box<Self>, ctx : &Context) -> W {
         self.0.flush(ctx)
     }
 }
 
-impl NestedRule for Added {
+impl<W : Write + 'static> NestedRule<W> for Added<W> {
     fn produce(&mut self, c : char, ctx : &Context) {
         self.0.produce(c, ctx)
     }
-    fn next(self : Box<Self>) -> AnyChainRule {
+    fn next(self : Box<Self>) -> AnyChainRule<W> {
         self.0
     }
     fn start_ignored_comment(&mut self, c : char) {
         self.0.start_ignored_comment(c)
     }
 }
-struct Deleted(AnyNestedRule);
+struct Deleted<W : Write>(AnyNestedRule<W>);
 
-impl ChainRule for Deleted {
-    fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule {
+impl<W : Write + 'static> ChainRule<W> for Deleted<W> {
+    fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule<W> {
         basic_consume(self, c, ctx, false)
     }
-    fn flush(self : Box<Self>, ctx : &Context) {
+    fn flush(self : Box<Self>, ctx : &Context) -> W {
         self.0.flush(ctx)
     }
 }
 
-impl NestedRule for Deleted {
+impl<W : Write + 'static> NestedRule<W> for Deleted<W> {
     fn produce(&mut self, _c : char, _ctx : &Context) {
     }
-    fn next(self : Box<Self>) -> AnyChainRule {
+    fn next(self : Box<Self>) -> AnyChainRule<W> {
         self.0
     }
     fn start_ignored_comment(&mut self, c : char) {
@@ -282,22 +287,22 @@
     }
 }
 
-struct Replaced(AnyNestedRule);
+struct Replaced<W : Write>(AnyNestedRule<W>);
 
-impl ChainRule for Replaced {
-    fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule {
+impl<W : Write + 'static> ChainRule<W> for Replaced<W> {
+    fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule<W> {
         basic_consume(self, c, ctx, false)
     }
-    fn flush(self : Box<Self>, ctx : &Context) {
+    fn flush(self : Box<Self>, ctx : &Context) -> W {
         self.0.flush(ctx)
     }
 }
 
-impl NestedRule for Replaced {
+impl<W : Write + 'static> NestedRule<W> for Replaced<W> {
     fn produce(&mut self, c : char, ctx : &Context) {
         self.0.produce(c, ctx)
     }
-    fn next(self : Box<Self>) -> AnyChainRule {
+    fn next(self : Box<Self>) -> AnyChainRule<W> {
         Scan::new(Deleted(self.0))
     }
     fn start_ignored_comment(&mut self, c : char) {
@@ -305,10 +310,10 @@
     }
 }
 
-struct Scan(AnyNestedRule);
+struct Scan<W : Write>(AnyNestedRule<W>);
 
-impl ChainRule for Scan {
-    fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule {
+impl<W : Write + 'static> ChainRule<W> for Scan<W> {
+    fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule<W> {
         if c.is_whitespace() || c == '\n' {
             self
         } else if c == '{' {
@@ -320,13 +325,13 @@
                     line {lineno}", lineno = ctx.lineno)
         }
     }
-    fn flush(self : Box<Self>, ctx : &Context) {
+    fn flush(self : Box<Self>, ctx : &Context) -> W {
         self.0.flush(ctx)
     }
 }
 
-impl Scan {
-    fn new<R : NestedRule + 'static>(r : R) -> Box<dyn ChainRule> {
+impl<W : Write + 'static> Scan<W> {
+    fn new<R : NestedRule<W> + 'static>(r : R) -> Box<dyn ChainRule<W>> {
         Box::new(Scan(Box::new(r)))
     }
 }
@@ -370,16 +375,34 @@
 
 fn main() {
     let cli = CommandLineArgs::parse();
-    let input = cli.input.map_or_else(
-        || Box::new(BufReader::new(io::stdin())) as Box<dyn BufRead>,
-        |f| Box::new(BufReader::new(File::open(f).unwrap())) as Box<dyn BufRead>
-    );
-    let output = cli.output.map_or_else(
-        || Box::new(BufWriter::new(io::stdout())) as Box<dyn Write>,
-        |f| Box::new(BufWriter::new(File::create(f).unwrap())) as Box<dyn Write>
-    );
+
+    match (cli.input, cli.output) {
+        (None, None) => {
+            process_buffered(cli.config, io::stdin(), io::stdout());
+        },
+        (None, Some(o)) => {
+            process_buffered(cli.config, io::stdin(), File::create(o).unwrap());
+        }
+        (Some(i), None) => {
+            process_buffered(cli.config, File::open(i).unwrap(), io::stdout());
+        }
+        (Some(i), Some(o)) => {
+            process_buffered(cli.config, File::open(i).unwrap(), File::create(o).unwrap());
+        }
+    }
+}
+
+fn process_buffered<I : Read, O : Debug + Write + 'static>(
+    config : Config,
+    input : I,
+    output : O
+) -> O {
+    process(config, BufReader::new(input), BufWriter::new(output)).into_inner().unwrap()
+}
+
+fn process<I : BufRead, O : Write + 'static>(config : Config, input : I, output : O) -> O {
     
-    let mut rule : Box<dyn ChainRule> = Box::new(Out {
+    let mut rule : Box<dyn ChainRule<O>> = Box::new(Out {
         only_whitespace : true,
         stored_whitespace : String::new(),
         output,
@@ -388,7 +411,7 @@
         ignored_comment_only_line : false
     });
 
-    let mut ctx = Context{ lineno : 0, cli : cli.config, input_only_ws : true};
+    let mut ctx = Context{ lineno : 0, cli : config, input_only_ws : true};
 
     for l in input.lines().map(|l| l.unwrap()) {
         ctx.lineno += 1;
@@ -400,5 +423,5 @@
         rule = rule.consume('\n', &ctx);
     }
 
-    rule.flush(&ctx);
+    rule.flush(&ctx)
 }

mercurial