-
-
Notifications
You must be signed in to change notification settings - Fork 595
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: improve storage error message (#8740)
- Loading branch information
1 parent
e60bd61
commit f7ba52f
Showing
27 changed files
with
1,287 additions
and
872 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
use rspack_error::miette; | ||
|
||
use crate::fs::{BatchFSError, FSError}; | ||
|
||
#[derive(Debug)] | ||
pub struct InvalidDetail { | ||
pub reason: String, | ||
pub packs: Vec<String>, | ||
} | ||
|
||
#[derive(Debug)] | ||
pub enum ValidateResult { | ||
NotExists, | ||
Valid, | ||
Invalid(InvalidDetail), | ||
} | ||
|
||
impl ValidateResult { | ||
pub fn invalid(reason: &str) -> Self { | ||
Self::Invalid(InvalidDetail { | ||
reason: reason.to_string(), | ||
packs: vec![], | ||
}) | ||
} | ||
pub fn invalid_with_packs(reason: &str, packs: Vec<String>) -> Self { | ||
Self::Invalid(InvalidDetail { | ||
reason: reason.to_string(), | ||
packs, | ||
}) | ||
} | ||
pub fn is_valid(&self) -> bool { | ||
matches!(self, ValidateResult::Valid) | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
enum ErrorReason { | ||
Reason(String), | ||
Detail(InvalidDetail), | ||
Error(Box<dyn std::error::Error + Send + Sync>), | ||
} | ||
|
||
#[derive(Debug)] | ||
pub enum ErrorType { | ||
Validate, | ||
Save, | ||
Load, | ||
} | ||
|
||
impl std::fmt::Display for ErrorType { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
match self { | ||
ErrorType::Validate => write!(f, "validate"), | ||
ErrorType::Save => write!(f, "save"), | ||
ErrorType::Load => write!(f, "load"), | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct Error { | ||
r#type: Option<ErrorType>, | ||
scope: Option<&'static str>, | ||
inner: ErrorReason, | ||
} | ||
|
||
impl From<FSError> for Error { | ||
fn from(e: FSError) -> Self { | ||
Self { | ||
r#type: None, | ||
scope: None, | ||
inner: ErrorReason::Error(Box::new(e)), | ||
} | ||
} | ||
} | ||
|
||
impl From<BatchFSError> for Error { | ||
fn from(e: BatchFSError) -> Self { | ||
Self { | ||
r#type: None, | ||
scope: None, | ||
inner: ErrorReason::Error(Box::new(e)), | ||
} | ||
} | ||
} | ||
|
||
impl Error { | ||
pub fn from_detail( | ||
r#type: Option<ErrorType>, | ||
scope: Option<&'static str>, | ||
detail: InvalidDetail, | ||
) -> Self { | ||
Self { | ||
r#type, | ||
scope, | ||
inner: ErrorReason::Detail(detail), | ||
} | ||
} | ||
pub fn from_error( | ||
r#type: Option<ErrorType>, | ||
scope: Option<&'static str>, | ||
error: Box<dyn std::error::Error + Send + Sync>, | ||
) -> Self { | ||
Self { | ||
r#type, | ||
scope, | ||
inner: ErrorReason::Error(error), | ||
} | ||
} | ||
pub fn from_reason( | ||
r#type: Option<ErrorType>, | ||
scope: Option<&'static str>, | ||
reason: String, | ||
) -> Self { | ||
Self { | ||
r#type, | ||
scope, | ||
inner: ErrorReason::Reason(reason), | ||
} | ||
} | ||
} | ||
|
||
impl std::fmt::Display for Error { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
if let Some(t) = &self.r#type { | ||
write!(f, "{} ", t)?; | ||
} | ||
if let Some(scope) = self.scope { | ||
write!(f, "scope `{}` ", scope)?; | ||
} | ||
write!(f, "failed due to")?; | ||
|
||
match &self.inner { | ||
ErrorReason::Detail(detail) => { | ||
write!(f, " {}", detail.reason)?; | ||
let mut pack_info_lines = detail | ||
.packs | ||
.iter() | ||
.map(|p| format!("- {}", p)) | ||
.collect::<Vec<_>>(); | ||
if pack_info_lines.len() > 5 { | ||
pack_info_lines.truncate(5); | ||
pack_info_lines.push("...".to_string()); | ||
} | ||
if !pack_info_lines.is_empty() { | ||
write!(f, ":\n{}", pack_info_lines.join("\n"))?; | ||
} | ||
} | ||
ErrorReason::Error(e) => { | ||
write!(f, " {}", e)?; | ||
} | ||
ErrorReason::Reason(e) => { | ||
write!(f, " {}", e)?; | ||
} | ||
} | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl std::error::Error for Error {} | ||
|
||
impl miette::Diagnostic for Error { | ||
fn code<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> { | ||
Some(Box::new(format!( | ||
"Error::{}", | ||
self | ||
.r#type | ||
.as_ref() | ||
.map_or("".to_string(), |t| t.to_string()) | ||
))) | ||
} | ||
fn severity(&self) -> Option<miette::Severity> { | ||
Some(miette::Severity::Warning) | ||
} | ||
} | ||
|
||
pub type Result<T> = std::result::Result<T, Error>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
use std::io::ErrorKind; | ||
|
||
use cow_utils::CowUtils; | ||
use rspack_paths::Utf8Path; | ||
use tokio::task::JoinError; | ||
|
||
#[derive(Debug)] | ||
pub enum FSOperation { | ||
Read, | ||
Write, | ||
Dir, | ||
Remove, | ||
Stat, | ||
Move, | ||
Redirect, | ||
} | ||
|
||
impl std::fmt::Display for FSOperation { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
match self { | ||
Self::Read => write!(f, "read"), | ||
Self::Write => write!(f, "write"), | ||
Self::Dir => write!(f, "create dir"), | ||
Self::Remove => write!(f, "remove"), | ||
Self::Stat => write!(f, "stat"), | ||
Self::Move => write!(f, "move"), | ||
Self::Redirect => write!(f, "redirect"), | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct FSError { | ||
file: String, | ||
inner: rspack_fs::Error, | ||
opt: FSOperation, | ||
} | ||
|
||
impl std::error::Error for FSError {} | ||
|
||
impl FSError { | ||
pub fn from_fs_error(file: &Utf8Path, opt: FSOperation, error: rspack_fs::Error) -> Self { | ||
Self { | ||
file: file.to_string(), | ||
inner: error, | ||
opt, | ||
} | ||
} | ||
pub fn from_message(file: &Utf8Path, opt: FSOperation, message: String) -> Self { | ||
Self { | ||
file: file.to_string(), | ||
inner: rspack_fs::Error::Io(std::io::Error::new(std::io::ErrorKind::Other, message)), | ||
opt, | ||
} | ||
} | ||
pub fn is_not_found(&self) -> bool { | ||
if matches!(self.kind(), ErrorKind::NotFound) { | ||
return true; | ||
} | ||
let error_content = self.inner.to_string(); | ||
let lower_case_error_content = error_content.cow_to_lowercase(); | ||
lower_case_error_content.contains("no such file") | ||
|| lower_case_error_content.contains("file not exists") | ||
} | ||
pub fn kind(&self) -> ErrorKind { | ||
match &self.inner { | ||
rspack_fs::Error::Io(e) => e.kind(), | ||
} | ||
} | ||
} | ||
|
||
impl std::fmt::Display for FSError { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
write!( | ||
f, | ||
"{} `{}` failed due to `{}`", | ||
self.opt, | ||
self.file, | ||
match &self.inner { | ||
rspack_fs::Error::Io(e) => e, | ||
} | ||
) | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct BatchFSError { | ||
message: String, | ||
join_error: Option<JoinError>, | ||
errors: Vec<Box<dyn std::error::Error + std::marker::Send + Sync>>, | ||
} | ||
|
||
impl From<FSError> for BatchFSError { | ||
fn from(error: FSError) -> Self { | ||
Self { | ||
message: "".to_string(), | ||
join_error: None, | ||
errors: vec![Box::new(error)], | ||
} | ||
} | ||
} | ||
|
||
impl std::fmt::Display for BatchFSError { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
write!(f, "{}", self.message)?; | ||
if let Some(join_error) = &self.join_error { | ||
write!(f, " due to `{}`", join_error)?; | ||
} | ||
if self.errors.len() == 1 { | ||
write!(f, "{}", self.errors[0])?; | ||
} else { | ||
for error in &self.errors { | ||
write!(f, "\n- {}", error)?; | ||
} | ||
} | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
impl BatchFSError { | ||
pub fn try_from_joined_result<T: std::error::Error + std::marker::Send + Sync + 'static, R>( | ||
message: &str, | ||
res: Result<Vec<Result<R, T>>, JoinError>, | ||
) -> Result<Vec<R>, Self> { | ||
match res { | ||
Ok(res) => Self::try_from_results(message, res), | ||
Err(join_error) => Err(Self { | ||
message: message.to_string(), | ||
errors: vec![], | ||
join_error: Some(join_error), | ||
}), | ||
} | ||
} | ||
|
||
pub fn try_from_results<T: std::error::Error + std::marker::Send + Sync + 'static, R>( | ||
message: &str, | ||
results: Vec<Result<R, T>>, | ||
) -> Result<Vec<R>, Self> { | ||
let mut errors = vec![]; | ||
let mut res = vec![]; | ||
for r in results { | ||
match r { | ||
Ok(r) => res.push(r), | ||
Err(e) => errors.push(Box::new(e).into()), | ||
} | ||
} | ||
if errors.is_empty() { | ||
Ok(res) | ||
} else { | ||
Err(Self { | ||
message: message.to_string(), | ||
errors, | ||
join_error: None, | ||
}) | ||
} | ||
} | ||
} | ||
|
||
impl std::error::Error for BatchFSError {} |
Oops, something went wrong.
f7ba52f
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 Ran ecosystem CI: Open
f7ba52f
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 Benchmark detail: Open