Handle non-utf8 input file with better error
diff --git a/gen/src/error.rs b/gen/src/error.rs
index f140577..b269286 100644
--- a/gen/src/error.rs
+++ b/gen/src/error.rs
@@ -8,8 +8,9 @@
 use std::fmt::{self, Display};
 use std::io::{self, Write};
 use std::ops::Range;
-use std::path::Path;
+use std::path::{Path, PathBuf};
 use std::process;
+use std::str::Utf8Error;
 
 pub(crate) type Result<T, E = Error> = std::result::Result<T, E>;
 
@@ -17,6 +18,7 @@
 pub(crate) enum Error {
     NoBridgeMod,
     Fs(fs::Error),
+    Utf8(PathBuf, Utf8Error),
     Syn(syn::Error),
 }
 
@@ -25,6 +27,7 @@
         match self {
             Error::NoBridgeMod => write!(f, "no #[cxx::bridge] module found"),
             Error::Fs(err) => err.fmt(f),
+            Error::Utf8(path, _) => write!(f, "Failed to read file `{}`", path.display()),
             Error::Syn(err) => err.fmt(f),
         }
     }
@@ -34,6 +37,7 @@
     fn source(&self) -> Option<&(dyn StdError + 'static)> {
         match self {
             Error::Fs(err) => err.source(),
+            Error::Utf8(_, err) => Some(err),
             Error::Syn(err) => err.source(),
             _ => None,
         }
diff --git a/gen/src/fs.rs b/gen/src/fs.rs
index 6ad92f8..d1b0b70 100644
--- a/gen/src/fs.rs
+++ b/gen/src/fs.rs
@@ -66,9 +66,9 @@
     }
 }
 
-pub(crate) fn read_to_string(path: impl AsRef<Path>) -> Result<String> {
+pub(crate) fn read(path: impl AsRef<Path>) -> Result<Vec<u8>> {
     let path = path.as_ref();
-    match std::fs::read_to_string(path) {
+    match std::fs::read(path) {
         Ok(string) => Ok(string),
         Err(e) => err!(e, "Failed to read file `{}`", path),
     }
diff --git a/gen/src/mod.rs b/gen/src/mod.rs
index c52d273..f4d643d 100644
--- a/gen/src/mod.rs
+++ b/gen/src/mod.rs
@@ -69,9 +69,9 @@
 }
 
 pub(super) fn generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode {
-    let source = match fs::read_to_string(path) {
+    let source = match read_to_string(path) {
         Ok(source) => source,
-        Err(err) => format_err(path, "", Error::Fs(err)),
+        Err(err) => format_err(path, "", err),
     };
     match generate_from_string(&source, opt) {
         Ok(out) => out,
@@ -79,6 +79,14 @@
     }
 }
 
+fn read_to_string(path: &Path) -> Result<String> {
+    let bytes = fs::read(path)?;
+    match String::from_utf8(bytes) {
+        Ok(string) => Ok(string),
+        Err(err) => Err(Error::Utf8(path.to_owned(), err.utf8_error())),
+    }
+}
+
 fn generate_from_string(source: &str, opt: &Opt) -> Result<GeneratedCode> {
     let mut source = source;
     if source.starts_with("#!") && !source.starts_with("#![") {