mirror of
https://github.com/tauri-apps/tauri.git
synced 2025-01-04 17:18:56 +03:00
refactor(tauri): use explicit error types instead of anyhow (#1209)
This commit is contained in:
parent
df32e18be3
commit
156a0ad5cb
5
.changes/core-errors.md
Normal file
5
.changes/core-errors.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"tauri": minor
|
||||
---
|
||||
|
||||
Tauri now uses explicit Error variants with `thiserror` instead of relying on `anyhow`.
|
@ -24,7 +24,6 @@ semver = "0.11"
|
||||
tempfile = "3"
|
||||
either = "1.6.1"
|
||||
tar = "0.4"
|
||||
anyhow = "1.0.38"
|
||||
flate2 = "1.0"
|
||||
thiserror = "1.0.23"
|
||||
rand = "0.8"
|
||||
|
@ -58,7 +58,7 @@ pub fn get_matches(config: &Config) -> crate::Result<Matches> {
|
||||
.tauri
|
||||
.cli
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow::anyhow!("CLI configuration not defined"))?;
|
||||
.ok_or_else(|| crate::Error::CliNotConfigured)?;
|
||||
|
||||
let about = cli
|
||||
.description()
|
||||
|
@ -16,7 +16,9 @@ pub fn get_output(cmd: String, args: Vec<String>, stdout: Stdio) -> crate::Resul
|
||||
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())
|
||||
Err(crate::Error::Command(
|
||||
String::from_utf8_lossy(&output.stderr).to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,7 +34,9 @@ pub fn get_output(cmd: String, args: Vec<String>, stdout: Stdio) -> crate::Resul
|
||||
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())
|
||||
Err(crate::Error::Command(
|
||||
String::from_utf8_lossy(&output.stderr).to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,7 +45,9 @@ pub fn get_output(cmd: String, args: Vec<String>, stdout: Stdio) -> crate::Resul
|
||||
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::Error::Command("Could not evaluate executable dir".to_string()).into()),
|
||||
None => Err(crate::Error::Command(
|
||||
"Could not evaluate executable dir".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,7 +56,9 @@ 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::Error::Command("Could not evaluate executable dir".to_string()).into()),
|
||||
None => Err(crate::Error::Command(
|
||||
"Could not evaluate executable dir".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,14 +94,17 @@ pub fn spawn_relative_command(
|
||||
|
||||
/// Gets the binary command with the current target triple.
|
||||
pub fn binary_command(binary_name: String) -> crate::Result<String> {
|
||||
Ok(format!("{}-{}", binary_name, platform::target_triple()?))
|
||||
Ok(format!(
|
||||
"{}-{}",
|
||||
binary_name,
|
||||
platform::target_triple().map_err(|e| crate::Error::FailedToDetectPlatform(e.to_string()))?
|
||||
))
|
||||
}
|
||||
|
||||
// tests for the commands functions.
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::io;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
#[test]
|
||||
@ -131,7 +142,7 @@ mod test {
|
||||
assert!(res.is_err());
|
||||
|
||||
// destruct the Error to check the ErrorKind and test that it is a Command type.
|
||||
if let Some(Error::Command(e)) = res.unwrap_err().downcast_ref::<Error>() {
|
||||
if let Error::Command(e) = res.unwrap_err() {
|
||||
// assert that the message in the error matches this string.
|
||||
assert_eq!(*e, "cat: test/: Is a directory\n".to_string());
|
||||
}
|
||||
@ -163,7 +174,7 @@ mod test {
|
||||
assert!(res.is_err());
|
||||
|
||||
// after asserting that the result is an error, check that the error kind is ErrorKind::Io
|
||||
if let Some(s) = res.unwrap_err().downcast_ref::<io::Error>() {
|
||||
if let crate::Error::Io(s) = res.unwrap_err() {
|
||||
// assert that the ErrorKind inside of the ErrorKind Io is ErrorKind::NotFound
|
||||
assert_eq!(s.kind(), std::io::ErrorKind::NotFound);
|
||||
}
|
||||
|
@ -16,9 +16,10 @@ fn open_dialog_internal(
|
||||
.map(|s| s.as_ref().to_string_lossy().to_string())
|
||||
.as_deref(),
|
||||
dialog_type,
|
||||
)?;
|
||||
)
|
||||
.map_err(|e| crate::Error::Dialog(e.to_string()))?;
|
||||
match response {
|
||||
Response::Cancel => Err(crate::Error::Dialog("user cancelled".into()).into()),
|
||||
Response::Cancel => Err(crate::Error::DialogCancelled),
|
||||
_ => Ok(response),
|
||||
}
|
||||
}
|
||||
|
58
tauri-api/src/error.rs
Normal file
58
tauri-api/src/error.rs
Normal file
@ -0,0 +1,58 @@
|
||||
/// The error types.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
/// The extract archive error.
|
||||
#[error("Extract Error: {0}")]
|
||||
Extract(String),
|
||||
/// The Command (spawn process) error.
|
||||
#[error("Command Error: {0}")]
|
||||
Command(String),
|
||||
/// The path operation error.
|
||||
#[error("Path Error: {0}")]
|
||||
Path(String),
|
||||
/// Error showing the dialog.
|
||||
#[error("Dialog Error: {0}")]
|
||||
Dialog(String),
|
||||
/// The dialog operation was cancelled by the user.
|
||||
#[error("user cancelled the dialog")]
|
||||
DialogCancelled,
|
||||
/// CLI config not set.
|
||||
#[error("CLI configuration not set on tauri.conf.json")]
|
||||
CliNotConfigured,
|
||||
/// The HTTP response error.
|
||||
#[error("HTTP Response Error: {0}")]
|
||||
Response(attohttpc::StatusCode),
|
||||
/// The network error.
|
||||
#[error("Network Error: {0}")]
|
||||
Network(#[from] attohttpc::Error),
|
||||
/// HTTP method error.
|
||||
#[error("{0}")]
|
||||
HttpMethod(#[from] http::method::InvalidMethod),
|
||||
/// Invalid HTTO header.
|
||||
#[error("{0}")]
|
||||
HttpHeader(#[from] attohttpc::header::InvalidHeaderName),
|
||||
/// Semver error.
|
||||
#[error("{0}")]
|
||||
Semver(#[from] semver::SemVerError),
|
||||
/// JSON error.
|
||||
#[error("{0}")]
|
||||
Json(#[from] serde_json::Error),
|
||||
/// IO error.
|
||||
#[error("{0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
/// ZIP error.
|
||||
#[error("{0}")]
|
||||
Zip(#[from] zip::result::ZipError),
|
||||
/// Notification error.
|
||||
#[error("{0}")]
|
||||
Notification(#[from] notify_rust::error::Error),
|
||||
/// failed to detect the current platform.
|
||||
#[error("failed to detect platform: {0}")]
|
||||
FailedToDetectPlatform(String),
|
||||
}
|
||||
|
||||
impl From<attohttpc::StatusCode> for Error {
|
||||
fn from(error: attohttpc::StatusCode) -> Self {
|
||||
Self::Response(error)
|
||||
}
|
||||
}
|
@ -4,19 +4,17 @@ mod file_move;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::Error;
|
||||
|
||||
pub use extract::*;
|
||||
pub use file_move::*;
|
||||
|
||||
/// Reads a string file.
|
||||
pub fn read_string<P: AsRef<Path>>(file: P) -> crate::Result<String> {
|
||||
fs::read_to_string(file).map_err(|err| Error::File(format!("Read_string failed: {}", err)).into())
|
||||
fs::read_to_string(file).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
/// Reads a binary file.
|
||||
pub fn read_binary<P: AsRef<Path>>(file: P) -> crate::Result<Vec<u8>> {
|
||||
fs::read(file).map_err(|err| Error::File(format!("Read_binary failed: {}", err)).into())
|
||||
fs::read(file).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -45,17 +43,11 @@ mod test {
|
||||
|
||||
assert!(res.is_err());
|
||||
|
||||
if let Some(Error::File(e)) = res.unwrap_err().downcast_ref::<Error>() {
|
||||
if let Error::Io(e) = res.unwrap_err() {
|
||||
#[cfg(windows)]
|
||||
assert_eq!(
|
||||
*e,
|
||||
"Read_string failed: Access is denied. (os error 5)".to_string()
|
||||
);
|
||||
assert_eq!(e.to_string(), "Access is denied. (os error 5)".to_string());
|
||||
#[cfg(not(windows))]
|
||||
assert_eq!(
|
||||
*e,
|
||||
"Read_string failed: Is a directory (os error 21)".to_string()
|
||||
);
|
||||
assert_eq!(e.to_string(), "Is a directory (os error 21)".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,17 +83,11 @@ mod test {
|
||||
|
||||
assert!(res.is_err());
|
||||
|
||||
if let Some(Error::File(e)) = res.unwrap_err().downcast_ref::<Error>() {
|
||||
if let Error::Io(e) = res.unwrap_err() {
|
||||
#[cfg(windows)]
|
||||
assert_eq!(
|
||||
*e,
|
||||
"Read_binary failed: Access is denied. (os error 5)".to_string()
|
||||
);
|
||||
assert_eq!(e.to_string(), "Access is denied. (os error 5)".to_string());
|
||||
#[cfg(not(windows))]
|
||||
assert_eq!(
|
||||
*e,
|
||||
"Read_binary failed: Is a directory (os error 21)".to_string()
|
||||
);
|
||||
assert_eq!(e.to_string(), "Is a directory (os error 21)".to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -265,7 +265,9 @@ pub fn make_request(options: HttpRequestOptions) -> crate::Result<Value> {
|
||||
if let Some(path) = body.as_str() {
|
||||
builder.file(File::open(path)?).send()
|
||||
} else {
|
||||
return Err(crate::Error::Path("Body must be the path to the file".into()).into());
|
||||
return Err(crate::Error::Path(
|
||||
"Body must be the path to the file".into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
BodyType::Auto => {
|
||||
@ -297,6 +299,6 @@ pub fn make_request(options: HttpRequestOptions) -> crate::Result<Value> {
|
||||
};
|
||||
Ok(response_data)
|
||||
} else {
|
||||
Err(crate::Error::Network(response.status()).into())
|
||||
Err(response.status().into())
|
||||
}
|
||||
}
|
||||
|
@ -36,32 +36,12 @@ pub mod notification;
|
||||
|
||||
pub use tauri_utils::*;
|
||||
|
||||
/// Alias for a Result with error type anyhow::Error.
|
||||
pub use anyhow::Result;
|
||||
use thiserror::Error;
|
||||
mod error;
|
||||
|
||||
/// The error types.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
/// The extract archive error.
|
||||
#[error("Extract Error:{0}")]
|
||||
Extract(String),
|
||||
/// The Command (spawn process) error.
|
||||
#[error("Command Error:{0}")]
|
||||
Command(String),
|
||||
/// The file operation error.
|
||||
#[error("File Error:{0}")]
|
||||
File(String),
|
||||
/// The path operation error.
|
||||
#[error("Path Error:{0}")]
|
||||
Path(String),
|
||||
/// The dialog error.
|
||||
#[error("Dialog Error:{0}")]
|
||||
Dialog(String),
|
||||
/// The network error.
|
||||
#[error("Network Error:{0}")]
|
||||
Network(attohttpc::StatusCode),
|
||||
}
|
||||
/// Tauri API error.
|
||||
pub use error::Error;
|
||||
/// Tauri API result type.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
// Not public API
|
||||
#[doc(hidden)]
|
||||
|
@ -77,9 +77,7 @@ impl Notification {
|
||||
notification.app_id(&self.identifier);
|
||||
}
|
||||
}
|
||||
notification
|
||||
.show()
|
||||
.map(|_| ())
|
||||
.map_err(|e| anyhow::anyhow!(e.to_string()))
|
||||
notification.show()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -84,7 +84,9 @@ pub fn resolve_path<P: AsRef<Path>>(path: P, dir: Option<BaseDirectory>) -> crat
|
||||
base_dir_path_value.push(path);
|
||||
Ok(base_dir_path_value)
|
||||
} else {
|
||||
Err(crate::Error::Path("unable to determine base dir path".to_string()).into())
|
||||
Err(crate::Error::Path(
|
||||
"unable to determine base dir path".to_string(),
|
||||
))
|
||||
}
|
||||
} else {
|
||||
let mut dir_path = PathBuf::new();
|
||||
|
@ -21,10 +21,8 @@ pub(crate) fn load_context(input: DeriveInput) -> Result<TokenStream, Error> {
|
||||
.iter()
|
||||
.find(|attr| attr.path.is_ident("config_path"));
|
||||
if let Some(attr) = config_path_attr {
|
||||
if let Ok(meta) = attr.parse_meta() {
|
||||
if let NameValue(MetaNameValue { lit: Str(path), .. }) = meta {
|
||||
config_file_path = path.value()
|
||||
}
|
||||
if let Ok(NameValue(MetaNameValue { lit: Str(path), .. })) = attr.parse_meta() {
|
||||
config_file_path = path.value()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@ edition = "2018"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
sysinfo = "0.10"
|
||||
anyhow = "1.0.31"
|
||||
thiserror = "1.0.19"
|
||||
phf = { version = "0.8", features = ["macros"] }
|
||||
flate2 = "1"
|
||||
|
@ -10,11 +10,11 @@ pub mod platform;
|
||||
/// Process helpers
|
||||
pub mod process;
|
||||
|
||||
pub use anyhow::Result;
|
||||
use thiserror::Error;
|
||||
/// Result type alias using the crate's error type.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// The error types.
|
||||
#[derive(Error, Debug)]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
/// Target triple architecture error
|
||||
#[error("Unable to determine target-architecture")]
|
||||
@ -25,9 +25,9 @@ pub enum Error {
|
||||
/// Target triple environment error
|
||||
#[error("Unable to determine target-environment")]
|
||||
Environment,
|
||||
/// Target triple unknown target-os error
|
||||
#[error("Unknown target_os")]
|
||||
Unknown,
|
||||
/// Tried to get resource on an unsupported platform.
|
||||
#[error("Unsupported platform for reading resources")]
|
||||
UnsupportedPlatform,
|
||||
/// Get parent process error
|
||||
#[error("Could not get parent process")]
|
||||
ParentProcess,
|
||||
@ -37,4 +37,7 @@ pub enum Error {
|
||||
/// Get child process error
|
||||
#[error("Could not get child process")]
|
||||
ChildProcess,
|
||||
/// IO error.
|
||||
#[error("{0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
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
|
||||
@ -11,7 +9,7 @@ use anyhow::Result;
|
||||
///
|
||||
/// * Errors:
|
||||
/// * Unexpected system config
|
||||
pub fn target_triple() -> Result<String> {
|
||||
pub fn target_triple() -> crate::Result<String> {
|
||||
let arch = if cfg!(target_arch = "x86") {
|
||||
"i686"
|
||||
} else if cfg!(target_arch = "x86_64") {
|
||||
@ -19,7 +17,7 @@ pub fn target_triple() -> Result<String> {
|
||||
} else if cfg!(target_arch = "arm") {
|
||||
"armv7"
|
||||
} else {
|
||||
return Err(crate::Error::Architecture.into());
|
||||
return Err(crate::Error::Architecture);
|
||||
};
|
||||
|
||||
let os = if cfg!(target_os = "linux") {
|
||||
@ -31,7 +29,7 @@ pub fn target_triple() -> Result<String> {
|
||||
} else if cfg!(target_os = "freebsd") {
|
||||
"unknown-freebsd"
|
||||
} else {
|
||||
return Err(crate::Error::OS.into());
|
||||
return Err(crate::Error::OS);
|
||||
};
|
||||
|
||||
let os = if cfg!(target_os = "macos") || cfg!(target_os = "freebsd") {
|
||||
@ -44,7 +42,7 @@ pub fn target_triple() -> Result<String> {
|
||||
} else if cfg!(target_env = "msvc") {
|
||||
"msvc"
|
||||
} else {
|
||||
return Err(crate::Error::Environment.into());
|
||||
return Err(crate::Error::Environment);
|
||||
};
|
||||
|
||||
format!("{}-{}", os, env)
|
||||
@ -61,7 +59,7 @@ pub fn target_triple() -> Result<String> {
|
||||
/// and `${exe_dir}/../lib/${exe_name}` when running the app from `src-tauri/target/(debug|release)/`.
|
||||
///
|
||||
/// On MacOS, it's `${exe_dir}../Resources` (inside .app).
|
||||
pub fn resource_dir() -> Result<PathBuf> {
|
||||
pub fn resource_dir() -> crate::Result<PathBuf> {
|
||||
let exe = std::env::current_exe()?;
|
||||
let exe_dir = exe.parent().expect("failed to get exe directory");
|
||||
let app_name = exe
|
||||
@ -89,6 +87,6 @@ pub fn resource_dir() -> Result<PathBuf> {
|
||||
} else if cfg!(target_os = "macos") {
|
||||
Ok(exe_dir.join("../Resources"))
|
||||
} else {
|
||||
Err(crate::Error::Unknown.into())
|
||||
Err(crate::Error::UnsupportedPlatform)
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ tokio = { version = "1.2", features = ["rt", "rt-multi-thread", "sync"] }
|
||||
futures = "0.3"
|
||||
async-trait = "0.1"
|
||||
uuid = { version = "0.8.2", features = [ "v4" ] }
|
||||
anyhow = "1.0.38"
|
||||
thiserror = "1.0.23"
|
||||
once_cell = "1.5.2"
|
||||
tauri-api = { version = "0.7.5", path = "../tauri-api" }
|
||||
|
@ -37,7 +37,7 @@ pub(crate) fn run<A: ApplicationExt + 'static>(application: App<A>) -> crate::Re
|
||||
|
||||
// spawn the embedded server on our server url
|
||||
#[cfg(embedded_server)]
|
||||
spawn_server(server_url, &application.context)?;
|
||||
spawn_server(server_url, &application.context);
|
||||
}
|
||||
|
||||
let splashscreen_content = if application.splashscreen_html().is_some() {
|
||||
@ -114,15 +114,13 @@ fn setup_content(context: &Context) -> crate::Result<Content<String>> {
|
||||
// setup content for embedded server
|
||||
#[cfg(all(embedded_server, not(no_server)))]
|
||||
fn setup_content(context: &Context) -> crate::Result<Content<String>> {
|
||||
let (port, valid) = setup_port(&context)?;
|
||||
let url = (if valid {
|
||||
setup_server_url(port, &context)
|
||||
let (port, valid) = setup_port(&context);
|
||||
if valid {
|
||||
let url = setup_server_url(port, &context);
|
||||
Ok(Content::Url(url))
|
||||
} else {
|
||||
Err(anyhow::anyhow!("invalid port"))
|
||||
})
|
||||
.expect("Unable to setup URL");
|
||||
|
||||
Ok(Content::Url(url))
|
||||
Err(crate::Error::PortNotAvailable(port))
|
||||
}
|
||||
}
|
||||
|
||||
// setup content for no-server
|
||||
@ -137,16 +135,16 @@ fn setup_content(context: &Context) -> crate::Result<Content<String>> {
|
||||
// get the port for the embedded server
|
||||
#[cfg(embedded_server)]
|
||||
#[allow(dead_code)]
|
||||
fn setup_port(context: &Context) -> crate::Result<(String, bool)> {
|
||||
fn setup_port(context: &Context) -> (String, bool) {
|
||||
let config = &context.config;
|
||||
match config.tauri.embedded_server.port {
|
||||
tauri_api::config::Port::Random => match get_available_port() {
|
||||
Some(available_port) => Ok((available_port.to_string(), true)),
|
||||
None => Ok(("0".to_string(), false)),
|
||||
Some(available_port) => (available_port.to_string(), true),
|
||||
None => ("0".to_string(), false),
|
||||
},
|
||||
tauri_api::config::Port::Value(port) => {
|
||||
let port_valid = port_is_available(port);
|
||||
Ok((port.to_string(), port_valid))
|
||||
(port.to_string(), port_valid)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -154,18 +152,18 @@ fn setup_port(context: &Context) -> crate::Result<(String, bool)> {
|
||||
// setup the server url for embedded server
|
||||
#[cfg(embedded_server)]
|
||||
#[allow(dead_code)]
|
||||
fn setup_server_url(port: String, context: &Context) -> crate::Result<String> {
|
||||
fn setup_server_url(port: String, context: &Context) -> String {
|
||||
let config = &context.config;
|
||||
let mut url = format!("{}:{}", config.tauri.embedded_server.host, port);
|
||||
if !url.starts_with("http") {
|
||||
url = format!("http://{}", url);
|
||||
}
|
||||
Ok(url)
|
||||
url
|
||||
}
|
||||
|
||||
// spawn the embedded server
|
||||
#[cfg(embedded_server)]
|
||||
fn spawn_server(server_url: String, context: &Context) -> crate::Result<()> {
|
||||
fn spawn_server(server_url: String, context: &Context) {
|
||||
let assets = context.assets;
|
||||
let public_path = context.config.tauri.embedded_server.public_path.clone();
|
||||
std::thread::spawn(move || {
|
||||
@ -193,7 +191,6 @@ fn spawn_server(server_url: String, context: &Context) -> crate::Result<()> {
|
||||
.expect("unable to setup response");
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// spawn an updater process.
|
||||
@ -440,17 +437,6 @@ mod test {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(embedded_server)]
|
||||
#[test]
|
||||
fn check_setup_port() {
|
||||
let context = Context::new::<TauriContext>().unwrap();
|
||||
let res = super::setup_port(&context);
|
||||
match res {
|
||||
Ok((_s, _b)) => {}
|
||||
_ => panic!("setup port failed"),
|
||||
}
|
||||
}
|
||||
|
||||
proptest! {
|
||||
#![proptest_config(ProptestConfig::with_cases(10000))]
|
||||
#[cfg(embedded_server)]
|
||||
@ -459,12 +445,8 @@ mod test {
|
||||
let p = port.clone();
|
||||
let context = Context::new::<TauriContext>().unwrap();
|
||||
|
||||
let res = super::setup_server_url(port, &context);
|
||||
|
||||
match res {
|
||||
Ok(url) => assert!(url.contains(&p)),
|
||||
Err(e) => panic!("setup_server_url Err {:?}", e.to_string())
|
||||
}
|
||||
let url = super::setup_server_url(port, &context);
|
||||
assert!(url.contains(&p));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -283,7 +283,7 @@ pub(crate) async fn handle<D: ApplicationDispatcherExt + 'static>(
|
||||
CliMatches { callback, error } => {
|
||||
#[cfg(cli)]
|
||||
{
|
||||
let matches = tauri_api::cli::get_matches(&context.config);
|
||||
let matches = tauri_api::cli::get_matches(&context.config).map_err(|e| e.into());
|
||||
crate::execute_promise(dispatcher, async move { matches }, callback, error).await;
|
||||
}
|
||||
#[cfg(not(cli))]
|
||||
|
@ -1,7 +1,8 @@
|
||||
use crate::{ApplicationDispatcherExt, Context};
|
||||
use std::io::Read;
|
||||
use tauri_api::assets::{AssetFetch, Assets};
|
||||
|
||||
use std::io::Read;
|
||||
|
||||
#[allow(clippy::option_env_unwrap)]
|
||||
pub async fn load<D: ApplicationDispatcherExt + 'static>(
|
||||
dispatcher: &mut D,
|
||||
@ -18,11 +19,7 @@ pub async fn load<D: ApplicationDispatcherExt + 'static>(
|
||||
dispatcher,
|
||||
async move {
|
||||
// strip "about:" uri scheme if it exists
|
||||
let asset = if asset.starts_with("about:") {
|
||||
&asset[6..]
|
||||
} else {
|
||||
&asset
|
||||
};
|
||||
let asset = asset.strip_prefix("about:").unwrap_or(&asset);
|
||||
|
||||
// handle public path setting from tauri.conf > tauri > embeddedServer > publicPath
|
||||
let asset = if asset.starts_with(&public_path) {
|
||||
@ -37,9 +34,9 @@ pub async fn load<D: ApplicationDispatcherExt + 'static>(
|
||||
.to_string();
|
||||
|
||||
// how should that condition be handled now?
|
||||
let asset_bytes = assets
|
||||
let asset_bytes: Vec<u8> = assets
|
||||
.get(&Assets::format_key(&asset), AssetFetch::Decompress)
|
||||
.ok_or_else(|| anyhow::anyhow!("Asset '{}' not found", asset))
|
||||
.ok_or_else(|| crate::Error::AssetNotFound(asset.clone()))
|
||||
.and_then(|(read, _)| {
|
||||
read
|
||||
.bytes()
|
||||
@ -63,7 +60,7 @@ pub async fn load<D: ApplicationDispatcherExt + 'static>(
|
||||
} else {
|
||||
"jpeg"
|
||||
};
|
||||
Ok(format!(
|
||||
crate::Result::Ok(format!(
|
||||
r#""data:image/{};base64,{}""#,
|
||||
mime_type,
|
||||
base64::encode(&asset_bytes)
|
||||
@ -90,7 +87,7 @@ pub async fn load<D: ApplicationDispatcherExt + 'static>(
|
||||
} else {
|
||||
dispatcher_.eval(asset_str);
|
||||
}
|
||||
Ok("Asset loaded successfully".to_string())
|
||||
crate::Result::Ok("Asset loaded successfully".to_string())
|
||||
}
|
||||
},
|
||||
callback,
|
||||
|
@ -34,7 +34,8 @@ pub fn open<D: ApplicationDispatcherExt + 'static>(
|
||||
} else {
|
||||
select(options.filter, options.default_path)
|
||||
};
|
||||
response.map(map_response)
|
||||
let res = response.map(map_response)?;
|
||||
Ok(res)
|
||||
},
|
||||
callback,
|
||||
error,
|
||||
@ -52,7 +53,11 @@ pub fn save<D: ApplicationDispatcherExt + 'static>(
|
||||
) -> crate::Result<()> {
|
||||
crate::execute_promise_sync(
|
||||
dispatcher,
|
||||
move || save_file(options.filter, options.default_path).map(map_response),
|
||||
move || {
|
||||
save_file(options.filter, options.default_path)
|
||||
.map(map_response)
|
||||
.map_err(|e| e.into())
|
||||
},
|
||||
callback,
|
||||
error,
|
||||
);
|
||||
@ -75,8 +80,8 @@ pub fn ask<D: ApplicationDispatcherExt + 'static>(
|
||||
crate::execute_promise_sync(
|
||||
dispatcher,
|
||||
move || match ask_dialog(message, title) {
|
||||
DialogSelection::Yes => Ok(true),
|
||||
_ => Ok(false),
|
||||
DialogSelection::Yes => crate::Result::Ok(true),
|
||||
_ => crate::Result::Ok(false),
|
||||
},
|
||||
callback,
|
||||
error,
|
||||
|
@ -28,7 +28,7 @@ pub async fn read_dir<D: ApplicationDispatcherExt>(
|
||||
} else {
|
||||
(false, None)
|
||||
};
|
||||
dir::read_dir(resolve_path(path, dir)?, recursive)
|
||||
dir::read_dir(resolve_path(path, dir)?, recursive).map_err(crate::Error::FailedToExecuteApi)
|
||||
},
|
||||
callback,
|
||||
error,
|
||||
@ -56,7 +56,8 @@ pub async fn copy_file<D: ApplicationDispatcherExt>(
|
||||
),
|
||||
None => (source, destination),
|
||||
};
|
||||
fs::copy(src, dest).map_err(|e| e.into())
|
||||
fs::copy(src, dest)?;
|
||||
crate::Result::Ok(())
|
||||
},
|
||||
callback,
|
||||
error,
|
||||
@ -82,13 +83,13 @@ pub async fn create_dir<D: ApplicationDispatcherExt>(
|
||||
(false, None)
|
||||
};
|
||||
let resolved_path = resolve_path(path, dir)?;
|
||||
let response = if recursive {
|
||||
fs::create_dir_all(resolved_path)
|
||||
if recursive {
|
||||
fs::create_dir_all(resolved_path)?;
|
||||
} else {
|
||||
fs::create_dir(resolved_path)
|
||||
};
|
||||
fs::create_dir(resolved_path)?;
|
||||
}
|
||||
|
||||
response.map_err(|e| e.into())
|
||||
crate::Result::Ok(())
|
||||
},
|
||||
callback,
|
||||
error,
|
||||
@ -114,13 +115,13 @@ pub async fn remove_dir<D: ApplicationDispatcherExt>(
|
||||
(false, None)
|
||||
};
|
||||
let resolved_path = resolve_path(path, dir)?;
|
||||
let response = if recursive {
|
||||
fs::remove_dir_all(resolved_path)
|
||||
if recursive {
|
||||
fs::remove_dir_all(resolved_path)?;
|
||||
} else {
|
||||
fs::remove_dir(resolved_path)
|
||||
};
|
||||
fs::remove_dir(resolved_path)?;
|
||||
}
|
||||
|
||||
response.map_err(|e| e.into())
|
||||
crate::Result::Ok(())
|
||||
},
|
||||
callback,
|
||||
error,
|
||||
@ -141,7 +142,8 @@ pub async fn remove_file<D: ApplicationDispatcherExt>(
|
||||
dispatcher,
|
||||
async move {
|
||||
let resolved_path = resolve_path(path, options.and_then(|o| o.dir))?;
|
||||
fs::remove_file(resolved_path).map_err(|e| e.into())
|
||||
fs::remove_file(resolved_path)?;
|
||||
crate::Result::Ok(())
|
||||
},
|
||||
callback,
|
||||
error,
|
||||
@ -169,7 +171,7 @@ pub async fn rename_file<D: ApplicationDispatcherExt>(
|
||||
),
|
||||
None => (old_path, new_path),
|
||||
};
|
||||
fs::rename(old, new).map_err(|e| e.into())
|
||||
fs::rename(old, new).map_err(crate::Error::Io)
|
||||
},
|
||||
callback,
|
||||
error,
|
||||
@ -191,8 +193,9 @@ pub async fn write_file<D: ApplicationDispatcherExt>(
|
||||
dispatcher,
|
||||
async move {
|
||||
File::create(resolve_path(path, options.and_then(|o| o.dir))?)
|
||||
.map_err(|e| e.into())
|
||||
.and_then(|mut f| f.write_all(contents.as_bytes()).map_err(|err| err.into()))
|
||||
.map_err(crate::Error::Io)
|
||||
.and_then(|mut f| f.write_all(contents.as_bytes()).map_err(|err| err.into()))?;
|
||||
crate::Result::Ok(())
|
||||
},
|
||||
callback,
|
||||
error,
|
||||
@ -214,12 +217,13 @@ pub async fn write_binary_file<D: ApplicationDispatcherExt>(
|
||||
dispatcher,
|
||||
async move {
|
||||
base64::decode(contents)
|
||||
.map_err(|e| e.into())
|
||||
.map_err(crate::Error::Base64Decode)
|
||||
.and_then(|c| {
|
||||
File::create(resolve_path(path, options.and_then(|o| o.dir))?)
|
||||
.map_err(|e| e.into())
|
||||
.and_then(|mut f| f.write_all(&c).map_err(|err| err.into()))
|
||||
})
|
||||
})?;
|
||||
crate::Result::Ok(())
|
||||
},
|
||||
callback,
|
||||
error,
|
||||
@ -238,7 +242,10 @@ pub async fn read_text_file<D: ApplicationDispatcherExt>(
|
||||
) {
|
||||
crate::execute_promise(
|
||||
dispatcher,
|
||||
async move { file::read_string(resolve_path(path, options.and_then(|o| o.dir))?) },
|
||||
async move {
|
||||
file::read_string(resolve_path(path, options.and_then(|o| o.dir))?)
|
||||
.map_err(crate::Error::FailedToExecuteApi)
|
||||
},
|
||||
callback,
|
||||
error,
|
||||
)
|
||||
@ -256,7 +263,10 @@ pub async fn read_binary_file<D: ApplicationDispatcherExt>(
|
||||
) {
|
||||
crate::execute_promise(
|
||||
dispatcher,
|
||||
async move { file::read_binary(resolve_path(path, options.and_then(|o| o.dir))?) },
|
||||
async move {
|
||||
file::read_binary(resolve_path(path, options.and_then(|o| o.dir))?)
|
||||
.map_err(crate::Error::FailedToExecuteApi)
|
||||
},
|
||||
callback,
|
||||
error,
|
||||
)
|
||||
|
@ -8,5 +8,11 @@ pub async fn make_request<D: ApplicationDispatcherExt>(
|
||||
callback: String,
|
||||
error: String,
|
||||
) {
|
||||
crate::execute_promise(dispatcher, async move { request(options) }, callback, error).await;
|
||||
crate::execute_promise(
|
||||
dispatcher,
|
||||
async move { request(options).map_err(|e| e.into()) },
|
||||
callback,
|
||||
error,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ pub async fn send<D: ApplicationDispatcherExt>(
|
||||
notification = notification.icon(icon);
|
||||
}
|
||||
notification.show()?;
|
||||
Ok(JsonValue::Null)
|
||||
crate::Result::Ok(JsonValue::Null)
|
||||
},
|
||||
callback,
|
||||
error,
|
||||
@ -42,9 +42,9 @@ pub async fn is_permission_granted<D: ApplicationDispatcherExt>(
|
||||
async move {
|
||||
let settings = crate::settings::read_settings()?;
|
||||
if let Some(allow_notification) = settings.allow_notification {
|
||||
Ok(JsonValue::String(allow_notification.to_string()))
|
||||
crate::Result::Ok(JsonValue::String(allow_notification.to_string()))
|
||||
} else {
|
||||
Ok(JsonValue::Null)
|
||||
crate::Result::Ok(JsonValue::Null)
|
||||
}
|
||||
},
|
||||
callback,
|
||||
@ -65,7 +65,7 @@ pub fn request_permission<D: ApplicationDispatcherExt + 'static>(
|
||||
let granted = "granted".to_string();
|
||||
let denied = "denied".to_string();
|
||||
if let Some(allow_notification) = settings.allow_notification {
|
||||
return Ok(if allow_notification { granted } else { denied });
|
||||
return crate::Result::Ok(if allow_notification { granted } else { denied });
|
||||
}
|
||||
let answer = tauri_api::dialog::ask(
|
||||
"This app wants to show notifications. Do you allow?",
|
||||
@ -75,14 +75,14 @@ pub fn request_permission<D: ApplicationDispatcherExt + 'static>(
|
||||
tauri_api::dialog::DialogSelection::Yes => {
|
||||
settings.allow_notification = Some(true);
|
||||
crate::settings::write_settings(settings)?;
|
||||
Ok(granted)
|
||||
crate::Result::Ok(granted)
|
||||
}
|
||||
tauri_api::dialog::DialogSelection::No => {
|
||||
settings.allow_notification = Some(false);
|
||||
crate::settings::write_settings(settings)?;
|
||||
Ok(denied)
|
||||
crate::Result::Ok(denied)
|
||||
}
|
||||
_ => Ok("default".to_string()),
|
||||
_ => crate::Result::Ok("default".to_string()),
|
||||
}
|
||||
},
|
||||
callback,
|
||||
|
@ -12,7 +12,7 @@ pub async fn resolve_path<D: ApplicationDispatcherExt>(
|
||||
) {
|
||||
crate::execute_promise(
|
||||
dispatcher,
|
||||
async move { path::resolve_path(path, directory) },
|
||||
async move { path::resolve_path(path, directory).map_err(|e| e.into()) },
|
||||
callback,
|
||||
error,
|
||||
)
|
||||
|
41
tauri/src/error.rs
Normal file
41
tauri/src/error.rs
Normal file
@ -0,0 +1,41 @@
|
||||
/// The plugin error type.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
/// Failed to create webview.
|
||||
#[error("failed to create webview")]
|
||||
CreateWebview,
|
||||
/// Failed to create window.
|
||||
#[error("failed to create window")]
|
||||
CreateWindow,
|
||||
/// Embedded asset not found.
|
||||
#[error("asset not found: {0}")]
|
||||
AssetNotFound(String),
|
||||
/// Embedded server port not available.
|
||||
#[error("failed to setup server, port {0} not available")]
|
||||
PortNotAvailable(String),
|
||||
/// Failed to serialize/deserialize.
|
||||
#[error("JSON error: {0}")]
|
||||
Json(serde_json::Error),
|
||||
/// Unknown API type.
|
||||
#[error("unknown API")]
|
||||
UnknownApi,
|
||||
/// Failed to execute tauri API.
|
||||
#[error("failed to execute API: {0}")]
|
||||
FailedToExecuteApi(#[from] tauri_api::Error),
|
||||
/// IO error.
|
||||
#[error("{0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
/// Failed to decode base64.
|
||||
#[error("Failed to decode base64 string: {0}")]
|
||||
Base64Decode(#[from] base64::DecodeError),
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for Error {
|
||||
fn from(error: serde_json::Error) -> Self {
|
||||
if error.to_string().contains("unknown variant") {
|
||||
Self::UnknownApi
|
||||
} else {
|
||||
Self::Json(error)
|
||||
}
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ pub mod settings;
|
||||
mod app;
|
||||
/// The Tauri API endpoints.
|
||||
mod endpoints;
|
||||
mod error;
|
||||
/// The plugin manager module contains helpers to manage runtime plugins.
|
||||
pub mod plugin;
|
||||
/// The salt helpers.
|
||||
@ -25,13 +26,16 @@ mod salt;
|
||||
/// Webview interface.
|
||||
mod webview;
|
||||
|
||||
/// The Tauri error enum.
|
||||
pub use error::Error;
|
||||
/// Tauri result type.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
pub(crate) mod async_runtime;
|
||||
|
||||
/// A task to run on the main thread.
|
||||
pub type SyncTask = Box<dyn FnOnce() + Send>;
|
||||
|
||||
/// Alias for a Result with error type anyhow::Error.
|
||||
pub use anyhow::Result;
|
||||
pub use app::*;
|
||||
pub use tauri_api as api;
|
||||
pub use tauri_macros::FromTauriContext;
|
||||
@ -66,10 +70,12 @@ pub fn execute_promise_sync<
|
||||
let callback_string =
|
||||
match format_callback_result(task().map_err(|err| err.to_string()), &callback, &error) {
|
||||
Ok(js) => js,
|
||||
Err(e) => {
|
||||
format_callback_result(Result::<(), String>::Err(e.to_string()), &callback, &error)
|
||||
.unwrap()
|
||||
}
|
||||
Err(e) => format_callback_result(
|
||||
std::result::Result::<(), String>::Err(e.to_string()),
|
||||
&callback,
|
||||
&error,
|
||||
)
|
||||
.unwrap(),
|
||||
};
|
||||
dispatcher_.eval(callback_string.as_str());
|
||||
})));
|
||||
@ -111,7 +117,7 @@ pub async fn call<D: ApplicationDispatcherExt>(
|
||||
) {
|
||||
execute_promise(
|
||||
dispatcher,
|
||||
async move { api::command::get_output(command, args, Stdio::piped()) },
|
||||
async move { api::command::get_output(command, args, Stdio::piped()).map_err(|e| e.into()) },
|
||||
callback,
|
||||
error,
|
||||
)
|
||||
|
@ -6,27 +6,6 @@ use futures::future::join_all;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
/// The plugin error type.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
/// Failed to serialize/deserialize.
|
||||
#[error("JSON error: {0}")]
|
||||
Json(serde_json::Error),
|
||||
/// Unknown API type.
|
||||
#[error("unknown API")]
|
||||
UnknownApi,
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for Error {
|
||||
fn from(error: serde_json::Error) -> Self {
|
||||
if error.to_string().contains("unknown variant") {
|
||||
Self::UnknownApi
|
||||
} else {
|
||||
Self::Json(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The plugin interface.
|
||||
#[async_trait::async_trait]
|
||||
pub trait Plugin<D: ApplicationDispatcherExt + 'static>: Send + Sync {
|
||||
@ -35,7 +14,7 @@ pub trait Plugin<D: ApplicationDispatcherExt + 'static>: Send + Sync {
|
||||
|
||||
/// Initialize the plugin.
|
||||
#[allow(unused_variables)]
|
||||
async fn initialize(&mut self, config: String) -> Result<(), Error> {
|
||||
async fn initialize(&mut self, config: String) -> crate::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -58,8 +37,8 @@ pub trait Plugin<D: ApplicationDispatcherExt + 'static>: Send + Sync {
|
||||
|
||||
/// Add invoke_handler API extension commands.
|
||||
#[allow(unused_variables)]
|
||||
async fn extend_api(&mut self, dispatcher: D, payload: &str) -> Result<(), Error> {
|
||||
Err(Error::UnknownApi)
|
||||
async fn extend_api(&mut self, dispatcher: D, payload: &str) -> crate::Result<()> {
|
||||
Err(crate::Error::UnknownApi)
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,7 +121,7 @@ pub(crate) async fn extend_api<D: ApplicationDispatcherExt + 'static>(
|
||||
store: &PluginStore<D>,
|
||||
dispatcher: &mut D,
|
||||
arg: &str,
|
||||
) -> Result<bool, Error> {
|
||||
) -> crate::Result<bool> {
|
||||
let mut plugins = store.lock().await;
|
||||
for ext in plugins.iter_mut() {
|
||||
match ext.extend_api(dispatcher.clone(), arg).await {
|
||||
@ -150,7 +129,7 @@ pub(crate) async fn extend_api<D: ApplicationDispatcherExt + 'static>(
|
||||
return Ok(true);
|
||||
}
|
||||
Err(e) => match e {
|
||||
Error::UnknownApi => {}
|
||||
crate::Error::UnknownApi => {}
|
||||
_ => return Err(e),
|
||||
},
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
use anyhow::anyhow;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
@ -27,10 +26,10 @@ pub(crate) fn write_settings(settings: Settings) -> crate::Result<()> {
|
||||
std::fs::create_dir(settings_folder)?;
|
||||
}
|
||||
File::create(settings_path)
|
||||
.map_err(|e| anyhow!(e))
|
||||
.map_err(|e| e.into())
|
||||
.and_then(|mut f| {
|
||||
f.write_all(serde_json::to_string(&settings)?.as_bytes())
|
||||
.map_err(|err| anyhow!(err))
|
||||
.map_err(|e| e.into())
|
||||
})
|
||||
}
|
||||
|
||||
@ -39,7 +38,8 @@ pub fn read_settings() -> crate::Result<Settings> {
|
||||
let settings_path = get_settings_path()?;
|
||||
if settings_path.exists() {
|
||||
read_string(settings_path)
|
||||
.and_then(|settings| serde_json::from_str(settings.as_str()).map_err(|e| anyhow!(e)))
|
||||
.and_then(|settings| serde_json::from_str(settings.as_str()).map_err(|e| e.into()))
|
||||
.map_err(|e| e.into())
|
||||
} else {
|
||||
Ok(Default::default())
|
||||
}
|
||||
|
@ -152,8 +152,7 @@ impl ApplicationExt for WryApplication {
|
||||
}
|
||||
|
||||
fn new() -> crate::Result<Self> {
|
||||
let app =
|
||||
wry::Application::new().map_err(|_| anyhow::anyhow!("failed to create application"))?;
|
||||
let app = wry::Application::new().map_err(|_| crate::Error::CreateWebview)?;
|
||||
let dispatcher = app.dispatcher();
|
||||
let windows = Arc::new(Mutex::new(HashMap::new()));
|
||||
|
||||
@ -176,7 +175,7 @@ impl ApplicationExt for WryApplication {
|
||||
let window = self
|
||||
.inner
|
||||
.create_window(window_builder.finish()?)
|
||||
.map_err(|_| anyhow::anyhow!("failed to create window"))?;
|
||||
.map_err(|_| crate::Error::CreateWindow)?;
|
||||
Ok(window)
|
||||
}
|
||||
|
||||
@ -212,7 +211,7 @@ impl ApplicationExt for WryApplication {
|
||||
self
|
||||
.inner
|
||||
.create_webview(window, webview_builder.finish()?, Some(wry_callbacks))
|
||||
.map_err(|_| anyhow::anyhow!("failed to create webview"))?;
|
||||
.map_err(|_| crate::Error::CreateWebview)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user