Bladeren bron

refactor(tauri) update error handling crate to anyhow+thiserror, close #613 (#621)

* Replace error-chain with thiserror in util crate

* Replace errorchain with anyhow/thiserror in api

* Replace with anyhow/thiserror in updater

* Replace with anyhow/thiserror in tauri

* Fix error handling on windows
Wu Yu Wei 5 jaren geleden
bovenliggende
commit
c23675b5e5

+ 2 - 1
tauri-api/Cargo.toml

@@ -22,7 +22,8 @@ tempfile = "3"
 either = "1.5.3"
 tar = "0.4"
 flate2 = "1"
-error-chain = "0.12"
+anyhow = "1.0.31"
+thiserror = "1.0.19"
 rand = "0.7"
 nfd = "0.0.4"
 attohttpc = {version = "0.13.0", features=["json", "form" ]}

+ 22 - 28
tauri-api/src/command.rs

@@ -10,35 +10,28 @@ use tauri_utils::platform;
 
 #[cfg(not(windows))]
 pub fn get_output(cmd: String, args: Vec<String>, stdout: Stdio) -> crate::Result<String> {
-  Command::new(cmd)
-    .args(args)
-    .stdout(stdout)
-    .output()
-    .map_err(|err| crate::Error::with_chain(err, "Command: get output failed"))
-    .and_then(|output| {
-      if output.status.success() {
-        Ok(String::from_utf8_lossy(&output.stdout).to_string())
-      } else {
-        Err(crate::ErrorKind::Command(String::from_utf8_lossy(&output.stderr).to_string()).into())
-      }
-    })
+  let output = Command::new(cmd).args(args).stdout(stdout).output()?;
+
+  if output.status.success() {
+    Ok(String::from_utf8_lossy(&output.stdout).to_string())
+  } else {
+    Err(crate::Error::Command(String::from_utf8_lossy(&output.stderr).to_string()).into())
+  }
 }
 
 #[cfg(windows)]
 pub fn get_output(cmd: String, args: Vec<String>, stdout: Stdio) -> crate::Result<String> {
-  Command::new(cmd)
+  let output = Command::new(cmd)
     .args(args)
     .stdout(stdout)
     .creation_flags(CREATE_NO_WINDOW)
-    .output()
-    .map_err(|err| crate::Error::with_chain(err, "Command: get output failed"))
-    .and_then(|output| {
-      if output.status.success() {
-        Ok(String::from_utf8_lossy(&output.stdout).to_string())
-      } else {
-        Err(crate::ErrorKind::Command(String::from_utf8_lossy(&output.stderr).to_string()).into())
-      }
-    })
+    .output()?;
+
+  if output.status.success() {
+    Ok(String::from_utf8_lossy(&output.stdout).to_string())
+  } else {
+    Err(crate::Error::Command(String::from_utf8_lossy(&output.stderr).to_string()).into())
+  }
 }
 
 pub fn format_command(path: String, command: String) -> String {
@@ -52,7 +45,7 @@ pub fn format_command(path: String, command: String) -> String {
 pub fn relative_command(command: String) -> crate::Result<String> {
   match std::env::current_exe()?.parent() {
     Some(exe_dir) => Ok(format_command(exe_dir.display().to_string(), command)),
-    None => Err(crate::ErrorKind::Command("Could not evaluate executable dir".to_string()).into()),
+    None => Err(crate::Error::Command("Could not evaluate executable dir".to_string()).into()),
   }
 }
 
@@ -60,7 +53,7 @@ pub fn relative_command(command: String) -> crate::Result<String> {
 pub fn command_path(command: String) -> crate::Result<String> {
   match std::env::current_exe()?.parent() {
     Some(exe_dir) => Ok(format!("{}/{}", exe_dir.display().to_string(), command)),
-    None => Err(crate::ErrorKind::Command("Could not evaluate executable dir".to_string()).into()),
+    None => Err(crate::Error::Command("Could not evaluate executable dir".to_string()).into()),
   }
 }
 
@@ -68,7 +61,7 @@ pub fn command_path(command: String) -> crate::Result<String> {
 pub fn command_path(command: String) -> crate::Result<String> {
   match std::env::current_exe()?.parent() {
     Some(exe_dir) => Ok(format!("{}/{}.exe", exe_dir.display().to_string(), command)),
-    None => Err(crate::ErrorKind::Command("Could not evaluate executable dir".to_string()).into()),
+    None => Err(crate::Error::Command("Could not evaluate executable dir".to_string()).into()),
   }
 }
 
@@ -106,7 +99,8 @@ pub fn binary_command(binary_name: String) -> crate::Result<String> {
 #[cfg(test)]
 mod test {
   use super::*;
-  use crate::{Error, ErrorKind};
+  use crate::Error;
+  use std::io;
   use totems::{assert_err, assert_ok};
 
   #[test]
@@ -141,7 +135,7 @@ mod test {
     assert_err!(&res);
 
     // destruct the Error to check the ErrorKind and test that it is a Command type.
-    if let Err(Error(ErrorKind::Command(e), _)) = &res {
+    if let Some(Error::Command(e)) = res.unwrap_err().downcast_ref::<Error>() {
       // assert that the message in the error matches this string.
       assert_eq!(*e, "cat: test/: Is a directory\n".to_string());
     }
@@ -200,7 +194,7 @@ mod test {
     assert_err!(&res);
 
     // after asserting that the result is an error, check that the error kind is ErrorKind::Io
-    if let Err(Error(ErrorKind::Io(s), _)) = &res {
+    if let Some(s) = res.unwrap_err().downcast_ref::<io::Error>() {
       // assert that the ErrorKind inside of the ErrorKind Io is ErrorKind::NotFound
       assert_eq!(s.kind(), std::io::ErrorKind::NotFound);
     }

+ 27 - 17
tauri-api/src/dialog.rs

@@ -1,33 +1,43 @@
-use nfd::{DialogType, open_dialog};
 pub use nfd::Response;
+use nfd::{open_dialog, DialogType};
 
-fn open_dialog_internal(dialog_type: DialogType, filter: Option<String>, default_path: Option<String>) -> crate::Result<Response> {
-  open_dialog(filter.as_deref(), default_path.as_deref(), dialog_type)
-    .map_err(|err| crate::Error::with_chain(err, "open dialog failed"))
-    .and_then(|response| {
-      match response {
-        Response::Cancel => Err(crate::Error::from("user cancelled")),
-        _ => Ok(response)
-      }
-    })
+fn open_dialog_internal(
+  dialog_type: DialogType,
+  filter: Option<String>,
+  default_path: Option<String>,
+) -> crate::Result<Response> {
+  let response = open_dialog(filter.as_deref(), default_path.as_deref(), dialog_type)?;
+  match response {
+    Response::Cancel => Err(crate::Error::Dialog("user cancelled".into()).into()),
+    _ => Ok(response),
+  }
 }
 
 /// Open single select file dialog
-pub fn select(filter_list: Option<String>, default_path: Option<String>) -> crate::Result<Response> {
-    open_dialog_internal(DialogType::SingleFile, filter_list, default_path)
+pub fn select(
+  filter_list: Option<String>,
+  default_path: Option<String>,
+) -> crate::Result<Response> {
+  open_dialog_internal(DialogType::SingleFile, filter_list, default_path)
 }
 
 /// Open mulitple select file dialog
-pub fn select_multiple(filter_list: Option<String>, default_path: Option<String>) -> crate::Result<Response> {
-    open_dialog_internal(DialogType::MultipleFiles, filter_list, default_path)
+pub fn select_multiple(
+  filter_list: Option<String>,
+  default_path: Option<String>,
+) -> crate::Result<Response> {
+  open_dialog_internal(DialogType::MultipleFiles, filter_list, default_path)
 }
 
 /// Open save dialog
-pub fn save_file(filter_list: Option<String>, default_path: Option<String>) -> crate::Result<Response> {
-    open_dialog_internal(DialogType::SaveFile, filter_list, default_path)
+pub fn save_file(
+  filter_list: Option<String>,
+  default_path: Option<String>,
+) -> crate::Result<Response> {
+  open_dialog_internal(DialogType::SaveFile, filter_list, default_path)
 }
 
 /// Open pick folder dialog
 pub fn pick_folder(default_path: Option<String>) -> crate::Result<Response> {
-    open_dialog_internal(DialogType::PickFolder, None, default_path)
+  open_dialog_internal(DialogType::PickFolder, None, default_path)
 }

+ 13 - 16
tauri-api/src/dir.rs

@@ -18,7 +18,7 @@ pub struct DiskEntry {
 fn is_dir(file_name: String) -> crate::Result<bool> {
   match metadata(file_name) {
     Ok(md) => Result::Ok(md.is_dir()),
-    Err(err) => Result::Err(err.to_string().into()),
+    Err(err) => Result::Err(err.into()),
   }
 }
 
@@ -43,21 +43,18 @@ pub fn walk_dir(path_copy: String) -> crate::Result<Vec<DiskEntry>> {
 }
 
 pub fn list_dir_contents(dir_path: String) -> crate::Result<Vec<DiskEntry>> {
-  fs::read_dir(dir_path)
-    .map_err(|err| crate::Error::with_chain(err, "read dir failed"))
-    .and_then(|paths| {
-      let mut dirs: Vec<DiskEntry> = vec![];
-      for path in paths {
-        let dir_path = path.expect("dirpath error").path();
-        let _dir_name = dir_path.display();
-        dirs.push(DiskEntry {
-          path: format!("{}", _dir_name),
-          is_dir: true,
-          name: get_dir_name_from_path(_dir_name.to_string()),
-        });
-      }
-      Ok(dirs)
-    })
+  let paths = fs::read_dir(dir_path)?;
+  let mut dirs: Vec<DiskEntry> = vec![];
+  for path in paths {
+    let dir_path = path.expect("dirpath error").path();
+    let _dir_name = dir_path.display();
+    dirs.push(DiskEntry {
+      path: format!("{}", _dir_name),
+      is_dir: true,
+      name: get_dir_name_from_path(_dir_name.to_string()),
+    });
+  }
+  Ok(dirs)
 }
 
 pub fn with_temp_dir<F: FnOnce(&tempfile::TempDir) -> ()>(callback: F) -> crate::Result<()> {

+ 10 - 10
tauri-api/src/file.rs

@@ -3,27 +3,27 @@ mod file_move;
 
 use std::fs;
 
-use crate::{Error, ErrorKind};
+use crate::Error;
 
 pub use extract::*;
 pub use file_move::*;
 
 pub fn read_string(file: String) -> crate::Result<String> {
   fs::read_to_string(file)
-    .map_err(|err| Error::from(ErrorKind::File(format!("Read_string failed: {}", err))))
+    .map_err(|err| Error::File(format!("Read_string failed: {}", err)).into())
     .map(|c| c)
 }
 
 pub fn read_binary(file: String) -> crate::Result<Vec<u8>> {
   fs::read(file)
-    .map_err(|err| Error::from(ErrorKind::File(format!("Read_binary failed: {}", err))))
+    .map_err(|err| Error::File(format!("Read_binary failed: {}", err)).into())
     .map(|b| b)
 }
 
 #[cfg(test)]
 mod test {
   use super::*;
-  use crate::{Error, ErrorKind};
+  use crate::Error;
   use totems::{assert_err, assert_ok};
 
   #[test]
@@ -47,15 +47,15 @@ mod test {
 
     assert_err!(res);
 
-    if let Err(Error(ErrorKind::File(e), _)) = res {
+    if let Some(Error::File(e)) = res.unwrap_err().downcast_ref::<Error>() {
       #[cfg(windows)]
       assert_eq!(
-        e,
+        *e,
         "Read_string failed: Access is denied. (os error 5)".to_string()
       );
       #[cfg(not(windows))]
       assert_eq!(
-        e,
+        *e,
         "Read_string failed: Is a directory (os error 21)".to_string()
       );
     }
@@ -93,15 +93,15 @@ mod test {
 
     assert_err!(res);
 
-    if let Err(Error(ErrorKind::File(e), _)) = res {
+    if let Some(Error::File(e)) = res.unwrap_err().downcast_ref::<Error>() {
       #[cfg(windows)]
       assert_eq!(
-        e,
+        *e,
         "Read_binary failed: Access is denied. (os error 5)".to_string()
       );
       #[cfg(not(windows))]
       assert_eq!(
-        e,
+        *e,
         "Read_binary failed: Is a directory (os error 21)".to_string()
       );
     }

+ 8 - 7
tauri-api/src/file/extract.rs

@@ -92,9 +92,10 @@ impl<'a> Extract<'a> {
                 }
               }
             }
-            let file_name = self.source.file_name().ok_or_else(|| {
-              crate::ErrorKind::Extract("Extractor source has no file-name".into())
-            })?;
+            let file_name = self
+              .source
+              .file_name()
+              .ok_or_else(|| crate::Error::Extract("Extractor source has no file-name".into()))?;
             let mut out_path = into_dir.join(file_name);
             out_path.set_extension("");
             let mut out_file = fs::File::create(&out_path)?;
@@ -148,9 +149,9 @@ impl<'a> Extract<'a> {
                 }
               }
             }
-            let file_name = file_to_extract.file_name().ok_or_else(|| {
-              crate::ErrorKind::Extract("Extractor source has no file-name".into())
-            })?;
+            let file_name = file_to_extract
+              .file_name()
+              .ok_or_else(|| crate::Error::Extract("Extractor source has no file-name".into()))?;
             let out_path = into_dir.join(file_name);
             let mut out_file = fs::File::create(&out_path)?;
             io::copy(&mut reader, &mut out_file)?;
@@ -162,7 +163,7 @@ impl<'a> Extract<'a> {
               .filter_map(|e| e.ok())
               .find(|e| e.path().ok().filter(|p| p == file_to_extract).is_some())
               .ok_or_else(|| {
-                crate::ErrorKind::Extract(format!(
+                crate::Error::Extract(format!(
                   "Could not find the required path in the archive: {:?}",
                   file_to_extract
                 ))

+ 1 - 1
tauri-api/src/file/file_move.rs

@@ -48,7 +48,7 @@ impl<'a> Move<'a> {
           fs::rename(dest, temp)?;
           if let Err(e) = fs::rename(self.source, dest) {
             fs::rename(temp, dest)?;
-            return Err(crate::Error::from(e));
+            return Err(e.into());
           }
         } else {
           fs::rename(self.source, dest)?;

+ 24 - 17
tauri-api/src/http.rs

@@ -1,11 +1,11 @@
-use std::time::Duration;
-use std::fs::File;
-use std::collections::HashMap;
-use attohttpc::{RequestBuilder, Method};
+use attohttpc::{Method, RequestBuilder};
 use http::header::HeaderName;
-use serde_json::Value;
 use serde::Deserialize;
-use serde_repr::{Serialize_repr, Deserialize_repr};
+use serde_json::Value;
+use serde_repr::{Deserialize_repr, Serialize_repr};
+use std::collections::HashMap;
+use std::fs::File;
+use std::time::Duration;
 
 #[derive(Serialize_repr, Deserialize_repr, Clone, Debug)]
 #[repr(u16)]
@@ -19,7 +19,7 @@ pub enum BodyType {
   /// - if the body is a byte array, send is as bytes (application/octet-stream)
   /// - if the body is an object or array, send it as JSON (application/json with UTF-8 charset)
   /// - if the body is a string, send it as text (text/plain with UTF-8 charset)
-  Auto
+  Auto,
 }
 
 #[derive(Serialize_repr, Deserialize_repr, Clone, Debug)]
@@ -31,7 +31,7 @@ pub enum ResponseType {
   /// Read the response as text
   Text,
   /// Read the response as binary
-  Binary
+  Binary,
 }
 
 #[derive(Deserialize)]
@@ -81,7 +81,10 @@ pub fn make_request(options: HttpRequestOptions) -> crate::Result<String> {
 
   if let Some(headers) = options.headers {
     for (header, header_value) in headers.iter() {
-      builder = builder.header(HeaderName::from_bytes(header.as_bytes())?, format!("{}", header_value));
+      builder = builder.header(
+        HeaderName::from_bytes(header.as_bytes())?,
+        format!("{}", header_value),
+      );
     }
   }
 
@@ -103,7 +106,9 @@ pub fn make_request(options: HttpRequestOptions) -> crate::Result<String> {
   if let Some(allow_compression) = options.allow_compression {
     builder = builder.allow_compression(allow_compression);
   }
-  builder = builder.danger_accept_invalid_certs(true).danger_accept_invalid_hostnames(true);
+  builder = builder
+    .danger_accept_invalid_certs(true)
+    .danger_accept_invalid_hostnames(true);
 
   let response = if let Some(body) = options.body {
     match options.body_type.unwrap_or(BodyType::Auto) {
@@ -112,9 +117,9 @@ pub fn make_request(options: HttpRequestOptions) -> crate::Result<String> {
         if let Some(path) = body.as_str() {
           builder.file(File::open(path)?).send()
         } else {
-          return Err(crate::Error::from("Body must be the path to the file"));
+          return Err(crate::Error::Path("Body must be the path to the file".into()).into());
         }
-      },
+      }
       BodyType::Auto => {
         if body.is_object() {
           builder.json(&body)?.send()
@@ -124,24 +129,26 @@ pub fn make_request(options: HttpRequestOptions) -> crate::Result<String> {
           let u: Result<Vec<u8>, _> = serde_json::from_value(body.clone());
           match u {
             Ok(vec) => builder.bytes(&vec).send(),
-            Err(_) => builder.json(&body)?.send()
+            Err(_) => builder.json(&body)?.send(),
           }
         } else {
           builder.send()
         }
       }
     }
-  } else { builder.send() };
+  } else {
+    builder.send()
+  };
 
   let response = response?;
   if response.is_success() {
     let response_data = match options.response_type.unwrap_or(ResponseType::Json) {
       ResponseType::Json => response.json()?,
       ResponseType::Text => response.text()?,
-      ResponseType::Binary => serde_json::to_string(&response.bytes()?)?
+      ResponseType::Binary => serde_json::to_string(&response.bytes()?)?,
     };
     Ok(response_data)
   } else {
-    Err(crate::Error::from(response.status().as_str()))
+    Err(crate::Error::Network(response.status()).into())
   }
-}
+}

+ 20 - 34
tauri-api/src/lib.rs

@@ -4,46 +4,32 @@
 )]
 
 pub mod command;
+pub mod dialog;
 pub mod dir;
 pub mod file;
+pub mod http;
+pub mod path;
 pub mod rpc;
-pub mod version;
 pub mod tcp;
-pub mod dialog;
-pub mod path;
-pub mod http;
+pub mod version;
 
 pub use tauri_utils::*;
 
-use error_chain::error_chain;
+pub use anyhow::Result;
+use thiserror::Error;
 
-error_chain! {
-    foreign_links {
-        Io(::std::io::Error);
-        ZipError(::zip::result::ZipError);
-        SemVer(::semver::SemVerError);
-        Platform(::tauri_utils::Error);
-        Json(::serde_json::Error);
-        Http(::attohttpc::Error);
-        HttpMethod(::http::method::InvalidMethod);
-        HttpHeaderName(::http::header::InvalidHeaderName);
-    }
-    errors {
-        Extract(t: String) {
-            description("Extract Error")
-            display("Extract Error: '{}'", t)
-        }
-        Command(t: String) {
-            description("Command Execution Error")
-            display("Command Error: '{}'", t)
-        }
-        File(t: String) {
-            description("File function Error")
-            display("File Error: {}", t)
-        }
-        Path(t: String) {
-            description("Path function Error")
-            display("Path Error: {}", t)
-        }
-    }
+#[derive(Error, Debug)]
+pub enum Error {
+  #[error("Extract Error:{0}")]
+  Extract(String),
+  #[error("Command Error:{0}")]
+  Command(String),
+  #[error("File Error:{0}")]
+  File(String),
+  #[error("Path Error:{0}")]
+  Path(String),
+  #[error("Dialog Error:{0}")]
+  Dialog(String),
+  #[error("Network Error:{0}")]
+  Network(attohttpc::StatusCode),
 }

+ 13 - 12
tauri-api/src/path.rs

@@ -1,5 +1,6 @@
 use std::path::PathBuf;
-use serde_repr::{Serialize_repr, Deserialize_repr};
+
+use serde_repr::{Deserialize_repr, Serialize_repr};
 
 #[derive(Serialize_repr, Deserialize_repr, Clone, Debug)]
 #[repr(u16)]
@@ -50,7 +51,7 @@ pub fn resolve_path(path: String, dir: Option<BaseDirectory>) -> crate::Result<S
       base_dir_path_value.push(path);
       Ok(base_dir_path_value.to_string_lossy().to_string())
     } else {
-      Err(crate::Error::from(crate::ErrorKind::Path("unable to determine base dir path".to_string())))
+      Err(crate::Error::Path("unable to determine base dir path".to_string()).into())
     }
   } else {
     Ok(path)
@@ -144,20 +145,20 @@ pub fn resource_dir() -> Option<PathBuf> {
 fn app_name() -> crate::Result<String> {
   let exe = std::env::current_exe()?;
   let app_name = exe
-    .file_name().expect("failed to get exe filename")
+    .file_name()
+    .expect("failed to get exe filename")
     .to_string_lossy();
 
   Ok(app_name.to_string())
 }
 
 pub fn app_dir() -> Option<PathBuf> {
-  dirs::config_dir()
-    .and_then(|mut dir| {
-      if let Ok(app_name) = app_name() {
-        dir.push(app_name);
-        Some(dir)
-      } else {
-        None
-      }
-    })
+  dirs::config_dir().and_then(|mut dir| {
+    if let Ok(app_name) = app_name() {
+      dir.push(app_name);
+      Some(dir)
+    } else {
+      None
+    }
+  })
 }

+ 1 - 1
tauri-api/src/version.rs

@@ -8,7 +8,7 @@ pub fn compare(first: &str, second: &str) -> crate::Result<i32> {
   match v1.cmp(&v2) {
     Ordering::Greater => Ok(-1),
     Ordering::Less => Ok(1),
-    Ordering::Equal => Ok(0)
+    Ordering::Equal => Ok(0),
   }
 }
 

+ 2 - 1
tauri-updater/Cargo.toml

@@ -16,6 +16,7 @@ serde_json = "1.0"
 serde = "1.0"
 zip = "0.5.3"
 tempdir = "0.3"
-error-chain = "0.12.1"
 tauri-api = { version = "0.5", path = "../tauri-api" }
 tauri-utils = { version = "0.5", path = "../tauri-utils" }
+anyhow = "1.0.31"
+thiserror = "1.0.19"

+ 1 - 5
tauri-updater/src/http.rs

@@ -23,11 +23,7 @@ pub fn download<T: Write>(url: String, dest: T, _display_progress: bool) -> crat
   let resp = get(url)?;
 
   if !resp.status().is_success() {
-    bail!(
-      crate::ErrorKind::Download,
-      "Download request failed with status: {:?}",
-      resp.status()
-    )
+    return Err(crate::Error::Download(resp.status()).into());
   }
 
   let file = BufWriter::new(dest);

+ 12 - 31
tauri-updater/src/lib.rs

@@ -3,36 +3,17 @@ pub mod macros;
 pub mod http;
 pub mod updater;
 
-use error_chain::error_chain;
+pub use anyhow::Result;
+use thiserror::Error;
 
-error_chain! {
-    foreign_links{
-        Io(::std::io::Error);
-        Json(::serde_json::Error);
-        Zip(::zip::result::ZipError);
-        API(::tauri_api::Error);
-        HTTP(::attohttpc::Error);
-    }
-    errors{
-        Download(t: String) {
-            description("Download Error")
-            display("Download Error: '{}'", t)
-        }
-        Updater(t: String) {
-            description("Updater Error")
-            display("Updater Error: '{}'", t)
-        }
-        Release(t: String) {
-            description("Release Error")
-            display("Release Error: '{}'", t)
-        }
-        Network(t: String) {
-            description("Network Error")
-            display("Network Error: '{}'", t)
-        }
-        Config(t: String) {
-            description("Config Error")
-            display("Config Error: '{}'", t)
-        }
-    }
+#[derive(Error, Debug)]
+pub enum Error {
+  #[error("Download request failed with status:{0}")]
+  Download(attohttpc::StatusCode),
+  #[error("Failed to determine parent dir")]
+  Updater,
+  #[error("Network Error:{0}")]
+  Network(String),
+  #[error("Config Error:{0} required")]
+  Config(String),
 }

+ 0 - 19
tauri-updater/src/macros.rs

@@ -1,22 +1,3 @@
-macro_rules! format_err {
-  ($e_type:expr, $literal:expr) => {
-      $e_type(format!($literal))
-  };
-  ($e_type:expr, $literal:expr, $($arg:expr),*) => {
-      $e_type(format!($literal, $($arg),*))
-  };
-}
-
-/// Helper for formatting `errors::Error`s and returning early
-macro_rules! bail {
-  ($e_type:expr, $literal:expr) => {
-      return Err(format_err!($e_type, $literal).into())
-  };
-  ($e_type:expr, $literal:expr, $($arg:expr),*) => {
-      return Err(format_err!($e_type, $literal, $($arg),*).into())
-  };
-}
-
 /// Helper to `print!` and immediately `flush` `stdout`
 macro_rules! print_flush {
   ($literal:expr) => {

+ 6 - 6
tauri-updater/src/updater.rs

@@ -160,27 +160,27 @@ impl UpdateBuilder {
       release: if let Some(ref release) = self.release {
         release.to_owned()
       } else {
-        bail!(crate::ErrorKind::Config, "`release` required")
+        return Err(crate::Error::Config("`release`".into()).into());
       },
       bin_name: if let Some(ref name) = self.bin_name {
         name.to_owned()
       } else {
-        bail!(crate::ErrorKind::Config, "`bin_name` required")
+        return Err(crate::Error::Config("`bin_name`".into()).into());
       },
       bin_install_path: if let Some(ref path) = self.bin_install_path {
         path.to_owned()
       } else {
-        bail!(crate::ErrorKind::Config, "`bin_install_path` required")
+        return Err(crate::Error::Config("`bin_install_path`".into()).into());
       },
       bin_path_in_archive: if let Some(ref path) = self.bin_path_in_archive {
         path.to_owned()
       } else {
-        bail!(crate::ErrorKind::Config, "`bin_path_in_archive` required")
+        return Err(crate::Error::Config("`bin_path_in_archive`".into()).into());
       },
       current_version: if let Some(ref ver) = self.current_version {
         ver.to_owned()
       } else {
-        bail!(crate::ErrorKind::Config, "`current_version` required")
+        return Err(crate::Error::Config("`current_version`".into()).into());
       },
       show_download_progress: self.show_download_progress,
       show_output: self.show_output,
@@ -236,7 +236,7 @@ impl Update {
     let tmp_dir_parent = self
       .bin_install_path
       .parent()
-      .ok_or_else(|| crate::ErrorKind::Updater("Failed to determine parent dir".into()))?;
+      .ok_or_else(|| crate::Error::Updater)?;
     let tmp_dir =
       tempdir::TempDir::new_in(&tmp_dir_parent, &format!("{}_download", self.bin_name))?;
     let tmp_archive_path = tmp_dir.path().join(&self.release.asset_name);

+ 16 - 12
tauri-updater/src/updater/github.rs

@@ -12,12 +12,14 @@ pub fn get_latest_release(repo_owner: &str, repo_name: &str) -> crate::Result<Re
   );
   let resp = http::get(api_url.clone())?;
   if !resp.status().is_success() {
-    bail!(
-      crate::ErrorKind::Network,
-      "api request failed with status: {:?} - for: {:?}",
-      resp.status(),
-      api_url
-    )
+    return Err(
+      crate::Error::Network(format!(
+        "api request failed with status: {:?} - for: {:?}",
+        resp.status(),
+        api_url
+      ))
+      .into(),
+    );
   }
   let json = resp.json::<serde_json::Value>()?;
   Ok(Release::parse(&json)?)
@@ -31,12 +33,14 @@ pub fn get_release_version(repo_owner: &str, repo_name: &str, ver: &str) -> crat
   );
   let resp = http::get(api_url.clone())?;
   if !resp.status().is_success() {
-    bail!(
-      crate::ErrorKind::Network,
-      "api request failed with status: {:?} - for: {:?}",
-      resp.status(),
-      api_url
-    )
+    return Err(
+      crate::Error::Network(format!(
+        "api request failed with status: {:?} - for: {:?}",
+        resp.status(),
+        api_url
+      ))
+      .into(),
+    );
   }
   let json = resp.json::<serde_json::Value>()?;
   Ok(Release::parse(&json)?)

+ 17 - 19
tauri-updater/src/updater/github/release.rs

@@ -14,15 +14,12 @@ impl ReleaseAsset {
   /// Errors:
   ///     * Missing required name & browser_download_url keys
   fn from_asset(asset: &serde_json::Value) -> crate::Result<ReleaseAsset> {
-    let download_url = asset["browser_download_url"].as_str().ok_or_else(|| {
-      format_err!(
-        crate::ErrorKind::Network,
-        "Asset missing `browser_download_url`"
-      )
-    })?;
+    let download_url = asset["browser_download_url"]
+      .as_str()
+      .ok_or_else(|| crate::Error::Network("Asset missing `browser_download_url`".into()))?;
     let name = asset["name"]
       .as_str()
-      .ok_or_else(|| format_err!(crate::ErrorKind::Network, "Asset missing `name`"))?;
+      .ok_or_else(|| crate::Error::Network("Asset missing `name`".into()))?;
     Ok(ReleaseAsset {
       download_url: download_url.to_owned(),
       name: name.to_owned(),
@@ -42,15 +39,15 @@ impl Release {
   pub fn parse(release: &serde_json::Value) -> crate::Result<Release> {
     let tag = release["tag_name"]
       .as_str()
-      .ok_or_else(|| format_err!(crate::ErrorKind::Network, "Release missing `tag_name`"))?;
+      .ok_or_else(|| crate::Error::Network("Release missing `tag_name`".into()))?;
     let date_created = release["created_at"]
       .as_str()
-      .ok_or_else(|| format_err!(crate::ErrorKind::Network, "Release missing `created_at`"))?;
+      .ok_or_else(|| crate::Error::Network("Release missing `created_at`".into()))?;
     let name = release["name"].as_str().unwrap_or(tag);
     let body = release["body"].as_str().unwrap_or("");
     let assets = release["assets"]
       .as_array()
-      .ok_or_else(|| format_err!(crate::ErrorKind::Network, "No assets found"))?;
+      .ok_or_else(|| crate::Error::Network("No assets found".into()))?;
     let assets = assets
       .iter()
       .map(ReleaseAsset::from_asset)
@@ -117,12 +114,12 @@ impl ReleaseListBuilder {
       repo_owner: if let Some(ref owner) = self.repo_owner {
         owner.to_owned()
       } else {
-        bail!(crate::ErrorKind::Config, "`repo_owner` required")
+        return Err(crate::Error::Config("`repo_owner`".into()).into());
       },
       repo_name: if let Some(ref name) = self.repo_name {
         name.to_owned()
       } else {
-        bail!(crate::ErrorKind::Config, "`repo_name` required")
+        return Err(crate::Error::Config("`repo_name`".into()).into());
       },
       target: self.target.clone(),
     })
@@ -170,18 +167,19 @@ impl ReleaseList {
     let (status, headers, reader) = attohttpc::get(url).send()?.split();
 
     if !status.is_success() {
-      bail!(
-        crate::ErrorKind::Network,
-        "api request failed with status: {:?} - for: {:?}",
-        status,
-        url
-      )
+      return Err(
+        crate::Error::Network(format!(
+          "api request failed with status: {:?} - for: {:?}",
+          status, url
+        ))
+        .into(),
+      );
     }
 
     let releases = reader.json::<serde_json::Value>()?;
     let releases = releases
       .as_array()
-      .ok_or_else(|| format_err!(crate::ErrorKind::Network, "No releases found"))?;
+      .ok_or_else(|| crate::Error::Network("No releases found".into()))?;
     let mut releases = releases
       .iter()
       .map(Release::parse)

+ 2 - 1
tauri-utils/Cargo.toml

@@ -11,4 +11,5 @@ edition = "2018"
 
 [dependencies]
 sysinfo = "0.10"
-error-chain = "0.12.1"
+anyhow = "1.0.31"
+thiserror = "1.0.19"

+ 19 - 7
tauri-utils/src/lib.rs

@@ -1,11 +1,23 @@
 pub mod platform;
 pub mod process;
 
-use error_chain::error_chain;
+pub use anyhow::Result;
+use thiserror::Error;
 
-error_chain! {
-    foreign_links {
-        Io(::std::io::Error);
-    }
-    errors {}
-}
+#[derive(Error, Debug)]
+pub enum Error {
+    #[error("Unable to determine target-architecture")]
+    Architecture,
+    #[error("Unable to determine target-os")]
+    OS,
+    #[error("Unable to determine target-environment")]
+    Environment,
+    #[error("Unknown target_os")]
+    Unknown,
+    #[error("Could not get parent process")]
+    ParentProcess,
+    #[error("Could not get parent PID")]
+    ParentPID,
+    #[error("Could not get child process")]
+    ChildProcess,
+}

+ 8 - 8
tauri-utils/src/platform.rs

@@ -1,5 +1,7 @@
 use std::path::{PathBuf, MAIN_SEPARATOR};
 
+use anyhow::Result;
+
 /// Try to determine the current target triple.
 ///
 /// Returns a target triple (e.g. `x86_64-unknown-linux-gnu` or `i686-pc-windows-msvc`) or an
@@ -9,7 +11,7 @@ use std::path::{PathBuf, MAIN_SEPARATOR};
 ///
 /// * Errors:
 ///     * Unexpected system config
-pub fn target_triple() -> Result<String, crate::Error> {
+pub fn target_triple() -> Result<String> {
   let arch = if cfg!(target_arch = "x86") {
     "i686"
   } else if cfg!(target_arch = "x86_64") {
@@ -17,9 +19,7 @@ pub fn target_triple() -> Result<String, crate::Error> {
   } else if cfg!(target_arch = "arm") {
     "armv7"
   } else {
-    return Err(crate::Error::from(
-      "Unable to determine target-architecture",
-    ));
+    return Err(crate::Error::Architecture.into());
   };
 
   let os = if cfg!(target_os = "linux") {
@@ -31,7 +31,7 @@ pub fn target_triple() -> Result<String, crate::Error> {
   } else if cfg!(target_os = "freebsd") {
     "unknown-freebsd"
   } else {
-    return Err(crate::Error::from("Unable to determine target-os"));
+    return Err(crate::Error::OS.into());
   };
 
   let os = if cfg!(target_os = "macos") || cfg!(target_os = "freebsd") {
@@ -44,7 +44,7 @@ pub fn target_triple() -> Result<String, crate::Error> {
     } else if cfg!(target_env = "msvc") {
       "msvc"
     } else {
-      return Err(crate::Error::from("Unable to determine target-environment"));
+      return Err(crate::Error::Environment.into());
     };
 
     format!("{}-{}", os, env)
@@ -53,7 +53,7 @@ pub fn target_triple() -> Result<String, crate::Error> {
   Ok(format!("{}-{}", arch, os))
 }
 
-pub fn resource_dir() -> crate::Result<PathBuf> {
+pub fn resource_dir() -> Result<PathBuf> {
   let exe = std::env::current_exe()?;
   let exe_dir = exe.parent().expect("failed to get exe directory");
   let app_name = exe
@@ -81,6 +81,6 @@ pub fn resource_dir() -> crate::Result<PathBuf> {
   } else if cfg!(target_os = "macos") {
     Ok(exe_dir.join("../Resources"))
   } else {
-    Err(crate::Error::from("Unknown target_os"))
+    Err(crate::Error::Unknown.into())
   }
 }

+ 6 - 4
tauri-utils/src/process.rs

@@ -1,17 +1,19 @@
+use crate::Error;
+
 use sysinfo;
 
 pub use sysinfo::{Process, ProcessExt, Signal, System, SystemExt};
 
-pub fn get_parent_process(system: &mut sysinfo::System) -> crate::Result<&Process> {
+pub fn get_parent_process(system: &mut sysinfo::System) -> Result<&Process, Error> {
   let pid = sysinfo::get_current_pid().unwrap();
   system.refresh_process(pid);
   let current_process = system
     .get_process(pid)
-    .ok_or("Could not get current process")?;
-  let parent_pid = current_process.parent().ok_or("Could not get parent PID")?;
+    .ok_or(Error::ParentProcess)?;
+  let parent_pid = current_process.parent().ok_or(Error::ParentPID)?;
   let parent_process = system
     .get_process(parent_pid)
-    .ok_or("Could not get parent process")?;
+    .ok_or(Error::ParentProcess)?;
 
   println!("{}", pid);
   Ok(parent_process)

+ 2 - 1
tauri/Cargo.toml

@@ -22,7 +22,8 @@ lazy_static = "1.4.0"
 tiny_http = { version = "0.7", optional = true }
 threadpool = "1.8"
 uuid = { version = "0.8.1", features = ["v4"] }
-error-chain = "0.12.2"
+anyhow = "1.0.31"
+thiserror = "1.0.19"
 
 tauri-api = { version = "0.5",  path = "../tauri-api" }
 

+ 3 - 3
tauri/src/endpoints.rs

@@ -12,7 +12,7 @@ use web_view::WebView;
 pub(crate) fn handle<T: 'static>(webview: &mut WebView<'_, T>, arg: &str) -> crate::Result<()> {
   use cmd::Cmd::*;
   match serde_json::from_str(arg) {
-    Err(e) => Err(crate::Error::from(e.to_string())),
+    Err(e) => Err(e.into()),
     Ok(command) => {
       match command {
         Init {} => {
@@ -297,7 +297,7 @@ fn load_asset<T: 'static>(
               ));
             }
             None => {
-              return Err(format!("Asset '{}' not found", asset).into());
+              return Err(anyhow::anyhow!("Asset '{}' not found", asset));
             }
           }
         } else {
@@ -330,7 +330,7 @@ fn load_asset<T: 'static>(
               _webview.eval(asset_str)
             }
           })
-          .map_err(|err| crate::ErrorKind::Promise(format!(r#""{}""#, err)).into())
+          .map_err(|err| err.into())
           .map(|_| r#""Asset loaded successfully""#.to_string())
       }
     },

+ 2 - 4
tauri/src/endpoints/dialog.rs

@@ -26,9 +26,7 @@ pub fn open<T: 'static>(
       } else {
         select(options.filter, options.default_path)
       };
-      response
-        .map(map_response)
-        .map_err(|e| crate::ErrorKind::Dialog(e.to_string()).into())
+      response.map(map_response).map_err(|e| e.into())
     },
     callback,
     error,
@@ -46,7 +44,7 @@ pub fn save<T: 'static>(
     move || {
       save_file(options.filter, options.default_path)
         .map(map_response)
-        .map_err(|e| crate::ErrorKind::Dialog(e.to_string()).into())
+        .map_err(|e| e.into())
     },
     callback,
     error,

+ 10 - 14
tauri/src/endpoints/file_system.rs

@@ -27,11 +27,11 @@ pub fn read_dir<T: 'static>(
       };
       if recursive {
         dir::walk_dir(resolve_path(path, dir)?)
-          .map_err(|e| crate::ErrorKind::FileSystem(e.to_string()).into())
+          .map_err(|e| e.into())
           .and_then(|f| serde_json::to_string(&f).map_err(|err| err.into()))
       } else {
         dir::list_dir_contents(resolve_path(path, dir)?)
-          .map_err(|e| crate::ErrorKind::FileSystem(e.to_string()).into())
+          .map_err(|e| e.into())
           .and_then(|f| serde_json::to_string(&f).map_err(|err| err.into()))
       }
     },
@@ -59,7 +59,7 @@ pub fn copy_file<T: 'static>(
         None => (source, destination),
       };
       fs::copy(src, dest)
-        .map_err(|e| crate::ErrorKind::FileSystem(e.to_string()).into())
+        .map_err(|e| e.into())
         .map(|_| "".to_string())
     },
     callback,
@@ -89,9 +89,7 @@ pub fn create_dir<T: 'static>(
         fs::create_dir(resolved_path)
       };
 
-      response
-        .map_err(|e| crate::ErrorKind::FileSystem(e.to_string()).into())
-        .map(|_| "".to_string())
+      response.map_err(|e| e.into()).map(|_| "".to_string())
     },
     callback,
     error,
@@ -120,9 +118,7 @@ pub fn remove_dir<T: 'static>(
         fs::remove_dir(resolved_path)
       };
 
-      response
-        .map_err(|e| crate::ErrorKind::FileSystem(e.to_string()).into())
-        .map(|_| "".to_string())
+      response.map_err(|e| e.into()).map(|_| "".to_string())
     },
     callback,
     error,
@@ -141,7 +137,7 @@ pub fn remove_file<T: 'static>(
     move || {
       let resolved_path = resolve_path(path, options.and_then(|o| o.dir))?;
       fs::remove_file(resolved_path)
-        .map_err(|e| crate::ErrorKind::FileSystem(e.to_string()).into())
+        .map_err(|e| e.into())
         .map(|_| "".to_string())
     },
     callback,
@@ -168,7 +164,7 @@ pub fn rename_file<T: 'static>(
         None => (old_path, new_path),
       };
       fs::rename(old, new)
-        .map_err(|e| crate::ErrorKind::FileSystem(e.to_string()).into())
+        .map_err(|e| e.into())
         .map(|_| "".to_string())
     },
     callback,
@@ -188,7 +184,7 @@ pub fn write_file<T: 'static>(
     webview,
     move || {
       File::create(resolve_path(file, options.and_then(|o| o.dir))?)
-        .map_err(|e| crate::ErrorKind::FileSystem(e.to_string()).into())
+        .map_err(|e| e.into())
         .and_then(|mut f| {
           f.write_all(contents.as_bytes())
             .map_err(|err| err.into())
@@ -211,7 +207,7 @@ pub fn read_text_file<T: 'static>(
     webview,
     move || {
       file::read_string(resolve_path(path, options.and_then(|o| o.dir))?)
-        .map_err(|e| crate::ErrorKind::FileSystem(e.to_string()).into())
+        .map_err(|e| e.into())
         .and_then(|f| serde_json::to_string(&f).map_err(|err| err.into()))
     },
     callback,
@@ -230,7 +226,7 @@ pub fn read_binary_file<T: 'static>(
     webview,
     move || {
       file::read_binary(resolve_path(path, options.and_then(|o| o.dir))?)
-        .map_err(|e| crate::ErrorKind::FileSystem(e.to_string()).into())
+        .map_err(|e| e.into())
         .and_then(|f| serde_json::to_string(&f).map_err(|err| err.into()))
     },
     callback,

+ 6 - 8
tauri/src/endpoints/http.rs

@@ -12,14 +12,12 @@ pub fn make_request<T: 'static>(
     webview,
     move || {
       let response_type = options.response_type.clone();
-      request(options)
-        .map_err(|e| crate::ErrorKind::Http(e.to_string()).into())
-        .map(|response| {
-          match response_type.unwrap_or(ResponseType::Json) {
-            ResponseType::Text => format!(r#""{}""#, response),
-            _ => response
-          }
-        })
+      request(options).map_err(|e| e.into()).map(|response| {
+        match response_type.unwrap_or(ResponseType::Json) {
+          ResponseType::Text => format!(r#""{}""#, response),
+          _ => response,
+        }
+      })
     },
     callback,
     error,

+ 2 - 33
tauri/src/lib.rs

@@ -17,7 +17,7 @@ mod salt;
 
 use std::process::Stdio;
 
-use error_chain::error_chain;
+pub use anyhow::Result;
 use threadpool::ThreadPool;
 
 pub use web_view::Handle;
@@ -26,37 +26,6 @@ use web_view::WebView;
 pub use app::*;
 pub use tauri_api as api;
 
-error_chain! {
-  foreign_links{
-    Api(::tauri_api::Error);
-    Json(::serde_json::Error);
-    Webview(::web_view::Error);
-    Io(::std::io::Error);
-  }
-  errors{
-    Promise(t: String) {
-        description("Promise Error")
-        display("Promise Error: '{}'", t)
-    }
-    Command(t: String) {
-      description("Command Error")
-      display("Command Error: '{}'", t)
-    }
-    Dialog(t: String) {
-      description("Dialog Error")
-      display("Dialog Error: '{}'", t)
-    }
-    FileSystem(t: String) {
-      description("FileSystem Error")
-      display("FileSystem Error: '{}'", t)
-    }
-    Http(t: String) {
-      description("Http Error")
-      display("Http Error: '{}'", t)
-    }
-  }
-}
-
 thread_local!(static POOL: ThreadPool = ThreadPool::new(4));
 
 pub fn spawn<F: FnOnce() -> () + Send + 'static>(task: F) {
@@ -110,7 +79,7 @@ pub fn call<T: 'static>(
     webview,
     || {
       api::command::get_output(command, args, Stdio::piped())
-        .map_err(|err| crate::ErrorKind::Promise(err.to_string()).into())
+        .map_err(|err| err)
         .map(|output| format!(r#""{}""#, output))
     },
     callback,