Skip to content

Commit 156a0ad

Browse files
authored
refactor(tauri): use explicit error types instead of anyhow (#1209)
1 parent df32e18 commit 156a0ad

29 files changed

+269
-205
lines changed

.changes/core-errors.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri": minor
3+
---
4+
5+
Tauri now uses explicit Error variants with `thiserror` instead of relying on `anyhow`.

tauri-api/Cargo.toml

-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ semver = "0.11"
2424
tempfile = "3"
2525
either = "1.6.1"
2626
tar = "0.4"
27-
anyhow = "1.0.38"
2827
flate2 = "1.0"
2928
thiserror = "1.0.23"
3029
rand = "0.8"

tauri-api/src/cli.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ pub fn get_matches(config: &Config) -> crate::Result<Matches> {
5858
.tauri
5959
.cli
6060
.as_ref()
61-
.ok_or_else(|| anyhow::anyhow!("CLI configuration not defined"))?;
61+
.ok_or_else(|| crate::Error::CliNotConfigured)?;
6262

6363
let about = cli
6464
.description()

tauri-api/src/command.rs

+19-8
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ pub fn get_output(cmd: String, args: Vec<String>, stdout: Stdio) -> crate::Resul
1616
if output.status.success() {
1717
Ok(String::from_utf8_lossy(&output.stdout).to_string())
1818
} else {
19-
Err(crate::Error::Command(String::from_utf8_lossy(&output.stderr).to_string()).into())
19+
Err(crate::Error::Command(
20+
String::from_utf8_lossy(&output.stderr).to_string(),
21+
))
2022
}
2123
}
2224

@@ -32,7 +34,9 @@ pub fn get_output(cmd: String, args: Vec<String>, stdout: Stdio) -> crate::Resul
3234
if output.status.success() {
3335
Ok(String::from_utf8_lossy(&output.stdout).to_string())
3436
} else {
35-
Err(crate::Error::Command(String::from_utf8_lossy(&output.stderr).to_string()).into())
37+
Err(crate::Error::Command(
38+
String::from_utf8_lossy(&output.stderr).to_string(),
39+
))
3640
}
3741
}
3842

@@ -41,7 +45,9 @@ pub fn get_output(cmd: String, args: Vec<String>, stdout: Stdio) -> crate::Resul
4145
pub fn command_path(command: String) -> crate::Result<String> {
4246
match std::env::current_exe()?.parent() {
4347
Some(exe_dir) => Ok(format!("{}/{}", exe_dir.display().to_string(), command)),
44-
None => Err(crate::Error::Command("Could not evaluate executable dir".to_string()).into()),
48+
None => Err(crate::Error::Command(
49+
"Could not evaluate executable dir".to_string(),
50+
)),
4551
}
4652
}
4753

@@ -50,7 +56,9 @@ pub fn command_path(command: String) -> crate::Result<String> {
5056
pub fn command_path(command: String) -> crate::Result<String> {
5157
match std::env::current_exe()?.parent() {
5258
Some(exe_dir) => Ok(format!("{}/{}.exe", exe_dir.display().to_string(), command)),
53-
None => Err(crate::Error::Command("Could not evaluate executable dir".to_string()).into()),
59+
None => Err(crate::Error::Command(
60+
"Could not evaluate executable dir".to_string(),
61+
)),
5462
}
5563
}
5664

@@ -86,14 +94,17 @@ pub fn spawn_relative_command(
8694

8795
/// Gets the binary command with the current target triple.
8896
pub fn binary_command(binary_name: String) -> crate::Result<String> {
89-
Ok(format!("{}-{}", binary_name, platform::target_triple()?))
97+
Ok(format!(
98+
"{}-{}",
99+
binary_name,
100+
platform::target_triple().map_err(|e| crate::Error::FailedToDetectPlatform(e.to_string()))?
101+
))
90102
}
91103

92104
// tests for the commands functions.
93105
#[cfg(test)]
94106
mod test {
95107
use super::*;
96-
use std::io;
97108

98109
#[cfg(not(windows))]
99110
#[test]
@@ -131,7 +142,7 @@ mod test {
131142
assert!(res.is_err());
132143

133144
// destruct the Error to check the ErrorKind and test that it is a Command type.
134-
if let Some(Error::Command(e)) = res.unwrap_err().downcast_ref::<Error>() {
145+
if let Error::Command(e) = res.unwrap_err() {
135146
// assert that the message in the error matches this string.
136147
assert_eq!(*e, "cat: test/: Is a directory\n".to_string());
137148
}
@@ -163,7 +174,7 @@ mod test {
163174
assert!(res.is_err());
164175

165176
// after asserting that the result is an error, check that the error kind is ErrorKind::Io
166-
if let Some(s) = res.unwrap_err().downcast_ref::<io::Error>() {
177+
if let crate::Error::Io(s) = res.unwrap_err() {
167178
// assert that the ErrorKind inside of the ErrorKind Io is ErrorKind::NotFound
168179
assert_eq!(s.kind(), std::io::ErrorKind::NotFound);
169180
}

tauri-api/src/dialog.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ fn open_dialog_internal(
1616
.map(|s| s.as_ref().to_string_lossy().to_string())
1717
.as_deref(),
1818
dialog_type,
19-
)?;
19+
)
20+
.map_err(|e| crate::Error::Dialog(e.to_string()))?;
2021
match response {
21-
Response::Cancel => Err(crate::Error::Dialog("user cancelled".into()).into()),
22+
Response::Cancel => Err(crate::Error::DialogCancelled),
2223
_ => Ok(response),
2324
}
2425
}

tauri-api/src/error.rs

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/// The error types.
2+
#[derive(thiserror::Error, Debug)]
3+
pub enum Error {
4+
/// The extract archive error.
5+
#[error("Extract Error: {0}")]
6+
Extract(String),
7+
/// The Command (spawn process) error.
8+
#[error("Command Error: {0}")]
9+
Command(String),
10+
/// The path operation error.
11+
#[error("Path Error: {0}")]
12+
Path(String),
13+
/// Error showing the dialog.
14+
#[error("Dialog Error: {0}")]
15+
Dialog(String),
16+
/// The dialog operation was cancelled by the user.
17+
#[error("user cancelled the dialog")]
18+
DialogCancelled,
19+
/// CLI config not set.
20+
#[error("CLI configuration not set on tauri.conf.json")]
21+
CliNotConfigured,
22+
/// The HTTP response error.
23+
#[error("HTTP Response Error: {0}")]
24+
Response(attohttpc::StatusCode),
25+
/// The network error.
26+
#[error("Network Error: {0}")]
27+
Network(#[from] attohttpc::Error),
28+
/// HTTP method error.
29+
#[error("{0}")]
30+
HttpMethod(#[from] http::method::InvalidMethod),
31+
/// Invalid HTTO header.
32+
#[error("{0}")]
33+
HttpHeader(#[from] attohttpc::header::InvalidHeaderName),
34+
/// Semver error.
35+
#[error("{0}")]
36+
Semver(#[from] semver::SemVerError),
37+
/// JSON error.
38+
#[error("{0}")]
39+
Json(#[from] serde_json::Error),
40+
/// IO error.
41+
#[error("{0}")]
42+
Io(#[from] std::io::Error),
43+
/// ZIP error.
44+
#[error("{0}")]
45+
Zip(#[from] zip::result::ZipError),
46+
/// Notification error.
47+
#[error("{0}")]
48+
Notification(#[from] notify_rust::error::Error),
49+
/// failed to detect the current platform.
50+
#[error("failed to detect platform: {0}")]
51+
FailedToDetectPlatform(String),
52+
}
53+
54+
impl From<attohttpc::StatusCode> for Error {
55+
fn from(error: attohttpc::StatusCode) -> Self {
56+
Self::Response(error)
57+
}
58+
}

tauri-api/src/file.rs

+8-22
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,17 @@ mod file_move;
44
use std::fs;
55
use std::path::Path;
66

7-
use crate::Error;
8-
97
pub use extract::*;
108
pub use file_move::*;
119

1210
/// Reads a string file.
1311
pub fn read_string<P: AsRef<Path>>(file: P) -> crate::Result<String> {
14-
fs::read_to_string(file).map_err(|err| Error::File(format!("Read_string failed: {}", err)).into())
12+
fs::read_to_string(file).map_err(|e| e.into())
1513
}
1614

1715
/// Reads a binary file.
1816
pub fn read_binary<P: AsRef<Path>>(file: P) -> crate::Result<Vec<u8>> {
19-
fs::read(file).map_err(|err| Error::File(format!("Read_binary failed: {}", err)).into())
17+
fs::read(file).map_err(|e| e.into())
2018
}
2119

2220
#[cfg(test)]
@@ -45,17 +43,11 @@ mod test {
4543

4644
assert!(res.is_err());
4745

48-
if let Some(Error::File(e)) = res.unwrap_err().downcast_ref::<Error>() {
46+
if let Error::Io(e) = res.unwrap_err() {
4947
#[cfg(windows)]
50-
assert_eq!(
51-
*e,
52-
"Read_string failed: Access is denied. (os error 5)".to_string()
53-
);
48+
assert_eq!(e.to_string(), "Access is denied. (os error 5)".to_string());
5449
#[cfg(not(windows))]
55-
assert_eq!(
56-
*e,
57-
"Read_string failed: Is a directory (os error 21)".to_string()
58-
);
50+
assert_eq!(e.to_string(), "Is a directory (os error 21)".to_string());
5951
}
6052
}
6153

@@ -91,17 +83,11 @@ mod test {
9183

9284
assert!(res.is_err());
9385

94-
if let Some(Error::File(e)) = res.unwrap_err().downcast_ref::<Error>() {
86+
if let Error::Io(e) = res.unwrap_err() {
9587
#[cfg(windows)]
96-
assert_eq!(
97-
*e,
98-
"Read_binary failed: Access is denied. (os error 5)".to_string()
99-
);
88+
assert_eq!(e.to_string(), "Access is denied. (os error 5)".to_string());
10089
#[cfg(not(windows))]
101-
assert_eq!(
102-
*e,
103-
"Read_binary failed: Is a directory (os error 21)".to_string()
104-
);
90+
assert_eq!(e.to_string(), "Is a directory (os error 21)".to_string());
10591
}
10692
}
10793
}

tauri-api/src/http.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,9 @@ pub fn make_request(options: HttpRequestOptions) -> crate::Result<Value> {
265265
if let Some(path) = body.as_str() {
266266
builder.file(File::open(path)?).send()
267267
} else {
268-
return Err(crate::Error::Path("Body must be the path to the file".into()).into());
268+
return Err(crate::Error::Path(
269+
"Body must be the path to the file".into(),
270+
));
269271
}
270272
}
271273
BodyType::Auto => {
@@ -297,6 +299,6 @@ pub fn make_request(options: HttpRequestOptions) -> crate::Result<Value> {
297299
};
298300
Ok(response_data)
299301
} else {
300-
Err(crate::Error::Network(response.status()).into())
302+
Err(response.status().into())
301303
}
302304
}

tauri-api/src/lib.rs

+5-25
Original file line numberDiff line numberDiff line change
@@ -36,32 +36,12 @@ pub mod notification;
3636

3737
pub use tauri_utils::*;
3838

39-
/// Alias for a Result with error type anyhow::Error.
40-
pub use anyhow::Result;
41-
use thiserror::Error;
39+
mod error;
4240

43-
/// The error types.
44-
#[derive(Error, Debug)]
45-
pub enum Error {
46-
/// The extract archive error.
47-
#[error("Extract Error:{0}")]
48-
Extract(String),
49-
/// The Command (spawn process) error.
50-
#[error("Command Error:{0}")]
51-
Command(String),
52-
/// The file operation error.
53-
#[error("File Error:{0}")]
54-
File(String),
55-
/// The path operation error.
56-
#[error("Path Error:{0}")]
57-
Path(String),
58-
/// The dialog error.
59-
#[error("Dialog Error:{0}")]
60-
Dialog(String),
61-
/// The network error.
62-
#[error("Network Error:{0}")]
63-
Network(attohttpc::StatusCode),
64-
}
41+
/// Tauri API error.
42+
pub use error::Error;
43+
/// Tauri API result type.
44+
pub type Result<T> = std::result::Result<T, Error>;
6545

6646
// Not public API
6747
#[doc(hidden)]

tauri-api/src/notification.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,7 @@ impl Notification {
7777
notification.app_id(&self.identifier);
7878
}
7979
}
80-
notification
81-
.show()
82-
.map(|_| ())
83-
.map_err(|e| anyhow::anyhow!(e.to_string()))
80+
notification.show()?;
81+
Ok(())
8482
}
8583
}

tauri-api/src/path.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ pub fn resolve_path<P: AsRef<Path>>(path: P, dir: Option<BaseDirectory>) -> crat
8484
base_dir_path_value.push(path);
8585
Ok(base_dir_path_value)
8686
} else {
87-
Err(crate::Error::Path("unable to determine base dir path".to_string()).into())
87+
Err(crate::Error::Path(
88+
"unable to determine base dir path".to_string(),
89+
))
8890
}
8991
} else {
9092
let mut dir_path = PathBuf::new();

tauri-macros/src/expand.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,8 @@ pub(crate) fn load_context(input: DeriveInput) -> Result<TokenStream, Error> {
2121
.iter()
2222
.find(|attr| attr.path.is_ident("config_path"));
2323
if let Some(attr) = config_path_attr {
24-
if let Ok(meta) = attr.parse_meta() {
25-
if let NameValue(MetaNameValue { lit: Str(path), .. }) = meta {
26-
config_file_path = path.value()
27-
}
24+
if let Ok(NameValue(MetaNameValue { lit: Str(path), .. })) = attr.parse_meta() {
25+
config_file_path = path.value()
2826
}
2927
}
3028

tauri-utils/Cargo.toml

-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ edition = "2018"
1212
serde = "1.0"
1313
serde_json = "1.0"
1414
sysinfo = "0.10"
15-
anyhow = "1.0.31"
1615
thiserror = "1.0.19"
1716
phf = { version = "0.8", features = ["macros"] }
1817
flate2 = "1"

tauri-utils/src/lib.rs

+9-6
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ pub mod platform;
1010
/// Process helpers
1111
pub mod process;
1212

13-
pub use anyhow::Result;
14-
use thiserror::Error;
13+
/// Result type alias using the crate's error type.
14+
pub type Result<T> = std::result::Result<T, Error>;
1515

1616
/// The error types.
17-
#[derive(Error, Debug)]
17+
#[derive(Debug, thiserror::Error)]
1818
pub enum Error {
1919
/// Target triple architecture error
2020
#[error("Unable to determine target-architecture")]
@@ -25,9 +25,9 @@ pub enum Error {
2525
/// Target triple environment error
2626
#[error("Unable to determine target-environment")]
2727
Environment,
28-
/// Target triple unknown target-os error
29-
#[error("Unknown target_os")]
30-
Unknown,
28+
/// Tried to get resource on an unsupported platform.
29+
#[error("Unsupported platform for reading resources")]
30+
UnsupportedPlatform,
3131
/// Get parent process error
3232
#[error("Could not get parent process")]
3333
ParentProcess,
@@ -37,4 +37,7 @@ pub enum Error {
3737
/// Get child process error
3838
#[error("Could not get child process")]
3939
ChildProcess,
40+
/// IO error.
41+
#[error("{0}")]
42+
Io(#[from] std::io::Error),
4043
}

0 commit comments

Comments
 (0)