Repository: LukasKalbertodt/bunt (language: rust)

Issue:
Fails to compile if `?` is used in macro with error that isn't `std::io::Error`
Hi, thanks for creating `bunt` (and great name, by the way! ;)).

The following fails:

```rust
fn main() -> color_eyre::Result<()> {

    // works:
    let ok: Result<()> = Ok(());
    std::println!("{:?}", ok?);

    //failes:
    let ok: Result<()> = Ok(());
    bunt::println!("{:?}", ok?);
    
    Ok(())
}
```

The error message is:

```
error[E0277]: `?` couldn't convert the error to `std::io::Error`
  --> abalet_com/src/bin/web_dev_server.rs:94:30
   |
94 |     bunt::println!("{:?}", ok?);
   |                              ^ the trait `std::convert::From<color_eyre::Report>` is not implemented for `std::io::Error`
   |
   = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
   = help: the following implementations were found:
             <std::io::Error as std::convert::From<getrandom::error::Error>>
             <std::io::Error as std::convert::From<openssl::error::ErrorStack>>
             <std::io::Error as std::convert::From<rand_core::error::Error>>
             <std::io::Error as std::convert::From<serde_json::error::Error>>
           and 4 others
   = note: required by `std::convert::From::from`
```


Discussion / hints:
Thanks for reporting this issue. I didn't think of that. I agree that this should work. Currently it doesn't work because `bunt` emits a IIFE (a closure) in order to use `?` in the emitted code. But it shouldn't be a problem to fix that. Will look into it.

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f3e104a..fbe7dba 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,7 +4,8 @@ All notable changes to this project will be documented in this file.
 
 
 ## [Unreleased]
-
+### Fixed
+- Make `?` work inside arguments (e.g. `println!("{}", foo?)`)
 
 ## [0.2.1] - 2020-09-20
 ### Added
diff --git a/macros/src/gen.rs b/macros/src/gen.rs
index f69eaf1..d035e5a 100644
--- a/macros/src/gen.rs
+++ b/macros/src/gen.rs
@@ -170,7 +170,11 @@ impl WriteInput {
 
                     // Combine everything in `write!` invocation.
                     writes.extend(quote! {
-                        std::write!(#buf, #fmt_str #(, #used_args = #used_args)* )?;
+                        if let std::result::Result::Err(e)
+                            = std::write!(#buf, #fmt_str #(, #used_args = #used_args)* )
+                        {
+                            break std::result::Result::Err(e);
+                        }
                     });
                 }
 
@@ -183,7 +187,11 @@ impl WriteInput {
                     let style_def = new_style.to_tokens();
                     style_stack.push(new_style);
                     writes.extend(quote! {
-                        ::bunt::termcolor::WriteColor::set_color(#buf, &#style_def)?;
+                        if let std::result::Result::Err(e)
+                            = ::bunt::termcolor::WriteColor::set_color(#buf, &#style_def)
+                        {
+                            break std::result::Result::Err(e);
+                        }
                     });
                 }
 
@@ -195,7 +203,11 @@ impl WriteInput {
                     let style = style_stack.last().copied().unwrap_or(Style::default());
                     let style_def = style.to_tokens();
                     writes.extend(quote! {
-                        ::bunt::termcolor::WriteColor::set_color(#buf, &#style_def)?;
+                        if let std::result::Result::Err(e)
+                            = ::bunt::termcolor::WriteColor::set_color(#buf, &#style_def)
+                        {
+                            break std::result::Result::Err(e);
+                        }
                     });
                 }
             }
@@ -214,15 +226,15 @@ impl WriteInput {
         // Combine everything.
         let target = &self.target;
         Ok(quote! {
-            (|| -> Result<(), ::std::io::Error> {
+            loop {
                 use std::io::Write as _;
 
                 #arg_bindings
                 let #buf = &mut #target;
                 #writes
 
-                Ok(())
-            })()
+                break Ok(());
+            }
         })
     }
 }


Test patch:
diff --git a/tests/main.rs b/tests/main.rs
index e75bc25..bb6ebf0 100644
--- a/tests/main.rs
+++ b/tests/main.rs
@@ -251,3 +251,57 @@ fn mixed_tag_args() {
         "a {$yellow}...{[intense]}...{$bold}b{[green]}c{/$}{/$}d", 27, true
     );
 }
+
+#[test]
+fn questionmark_in_argument() {
+    fn foo(s: &str) -> Result<bool, std::num::ParseIntError> {
+        let mut b = buf();
+        let _  = bunt::writeln!(b, "Hello {[green]}", s.parse::<u64>()?);
+        Ok(true)
+    }
+
+    assert!(foo("hi").is_err());
+    assert_eq!(foo("3"), Ok(true));
+}
+
+#[test]
+fn io_error() {
+    let mut empty = [];
+    let res = bunt::write!(&mut empty[..], "hello");
+    assert!(res.is_err());
+}
+
+#[test]
+fn set_color_error() {
+    use std::io;
+
+    struct Dummy;
+
+    impl io::Write for Dummy {
+        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+            Ok(buf.len())
+        }
+        fn flush(&mut self) -> io::Result<()> {
+            Ok(())
+        }
+    }
+
+    impl bunt::termcolor::WriteColor for Dummy {
+        fn supports_color(&self) -> bool {
+            true
+        }
+        fn set_color(&mut self, _: &bunt::termcolor::ColorSpec) -> io::Result<()> {
+            Err(io::Error::new(io::ErrorKind::NotFound, "oops"))
+        }
+        fn reset(&mut self) -> io::Result<()> {
+            Ok(())
+        }
+    }
+
+    let mut b = Dummy;
+    assert!(bunt::write!(b, "hello").is_ok());
+
+    let res = bunt::write!(b, "{$green}colooor{/$}");
+    assert!(res.is_err());
+    assert_eq!(res.unwrap_err().kind(), io::ErrorKind::NotFound);
+}