use std::{fmt, path::PathBuf};
#[derive(Debug)]
pub struct SetupError(Box<dyn std::error::Error>);
impl From<Box<dyn std::error::Error>> for SetupError {
fn from(error: Box<dyn std::error::Error>) -> Self {
Self(error)
}
}
unsafe impl Send for SetupError {}
unsafe impl Sync for SetupError {}
impl fmt::Display for SetupError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl std::error::Error for SetupError {}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("runtime error: {0}")]
Runtime(#[from] millennium_runtime::Error),
#[error("failed to create window")]
CreateWindow,
#[error("a window with label `{0}` already exists")]
WindowLabelAlreadyExists(String),
#[error("webview not found: invalid label or it was closed")]
WebviewNotFound,
#[error("failed to send message to the webview")]
FailedToSendMessage,
#[error("asset not found: {0}")]
AssetNotFound(String),
#[error("JSON error: {0}")]
Json(serde_json::Error),
#[error("unknown API: {0:?}")]
UnknownApi(Option<serde_json::Error>),
#[error("failed to execute API: {0}")]
FailedToExecuteApi(#[from] crate::api::Error),
#[error("{0}")]
Io(#[from] std::io::Error),
#[cfg(feature = "updater")]
#[error("Failed to decode base64 string: {0}")]
Base64Decode(#[from] base64::DecodeError),
#[error("invalid icon: {0}")]
InvalidIcon(std::io::Error),
#[error("http client dropped or not initialized")]
HttpClientNotInitialized,
#[error("The API '{0}' is not enabled in the allowlist; check your Cargo.toml & .millenniumrc")]
ApiNotAllowlisted(String),
#[error("invalid args `{1}` for command `{0}`: {2}")]
InvalidArgs(&'static str, &'static str, serde_json::Error),
#[error("error encountered during setup hook: {0}")]
Setup(SetupError),
#[cfg(updater)]
#[cfg_attr(doc_cfg, doc(cfg(feature = "updater")))]
#[error("Updater: {0}")]
MillenniumUpdater(#[from] crate::updater::Error),
#[error("failed to initialize plugin `{0}`: {1}")]
PluginInitialization(String, String),
#[error("invalid url: {0}")]
InvalidUrl(url::ParseError),
#[error(transparent)]
JoinError(#[from] tokio::task::JoinError),
#[error("path not allowed on the configured scope: {0}")]
PathNotAllowed(PathBuf),
#[error("sending notification was not allowed by the user")]
NotificationNotAllowed,
#[error("url not allowed on the configured scope: {0}")]
UrlNotAllowed(url::Url),
#[error("sidecar not configured under `.millenniumrc > millennium > bundle > externalBin`: {0}")]
SidecarNotAllowed(PathBuf),
#[cfg(shell_scope)]
#[error("sidecar configuration found, but unable to create a path to it: {0}")]
SidecarNotFound(#[from] Box<crate::ShellScopeError>),
#[error("program not allowed on the configured shell scope: {0}")]
ProgramNotAllowed(PathBuf),
#[cfg(feature = "isolation")]
#[error("isolation pattern error: {0}")]
IsolationPattern(#[from] millennium_utils::pattern::isolation::Error),
#[error("invalid window url: {0}")]
InvalidWindowUrl(&'static str),
#[error("invalid glob pattern: {0}")]
GlobPattern(#[from] glob::PatternError),
#[cfg(feature = "icon-png")]
#[error("failed to decode PNG: {0}")]
PngDecode(#[from] png::DecodingError)
}
pub(crate) fn into_anyhow<T: std::fmt::Display>(err: T) -> anyhow::Error {
anyhow::anyhow!(err.to_string())
}
impl Error {
#[allow(dead_code)]
pub(crate) fn into_anyhow(self) -> anyhow::Error {
anyhow::anyhow!(self.to_string())
}
}
impl From<serde_json::Error> for Error {
fn from(error: serde_json::Error) -> Self {
if error.to_string().contains("unknown variant") {
Self::UnknownApi(Some(error))
} else {
Self::Json(error)
}
}
}