use std::path::PathBuf;
pub type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
pub enum Error {
#[error("Templating rendering error")]
#[diagnostic(code(template))]
TeraError(#[from] tera::Error),
#[error("Could not parse datetime")]
#[diagnostic(code(datetime_parsing))]
Chrono(#[from] chrono::ParseError),
#[error("Date or duration out of range")]
#[diagnostic(code(duration_out_of_range))]
OutOfRangeDate(#[from] chrono::OutOfRangeError),
#[error("{msg}")]
#[diagnostic(code(io))]
Io {
msg: String,
#[source]
err: std::io::Error,
},
#[error("Could not create temporary directory")]
#[diagnostic(code(tempdir))]
TempDirectory(#[source] std::io::Error),
#[error("{count} source(s) could not be checked")]
#[diagnostic(
code(check::sources_failed),
help("The errors are reported above. Fix them or drop the sources.")
)]
SourcesFailed { count: usize },
#[error("{msg}")]
#[diagnostic(code(git))]
Git {
msg: String,
#[source]
err: git2::Error,
},
#[error("{msg}")]
#[diagnostic(code(github))]
GitHub {
msg: String,
#[source]
err: octocrab::Error,
},
#[error("{msg}")]
#[diagnostic(code(gitlab))]
GitLab {
msg: String,
#[source]
err: gitlab::GitlabError,
},
#[error("{msg}")]
#[diagnostic(code(gitlab))]
GitLabBoxed {
msg: String,
#[source]
err: Box<dyn std::error::Error + Send + Sync + 'static>,
},
#[error("Valid env var required `{name}`: {msg}")]
#[diagnostic(code(env))]
Env {
name: String,
msg: String,
#[source]
err: std::env::VarError,
},
#[error(transparent)]
#[diagnostic(code(http))]
ReqwestError(#[from] reqwest::Error),
#[error(transparent)]
#[diagnostic(code(yaml))]
SerdeYamlError(#[from] serde_yaml_ng::Error),
#[error(transparent)]
#[diagnostic(code(json))]
SerdeJsonError(#[from] serde_json::Error),
#[error(transparent)]
Glob(#[from] wax::BuildError),
#[error(transparent)]
Walk(#[from] wax::walk::WalkError),
#[error(transparent)]
EnvExpand(#[from] shellexpand::LookupError<std::env::VarError>),
#[error("Could not decode config file at `{}`", path.display())]
DeserializeConfig {
path: PathBuf,
#[source]
err: serde_yaml_ng::Error,
},
#[error(
"Unsupported config version (required: {}, got: {})",
required,
current
)]
UnsupportedConfigVersion { required: String, current: String },
#[error("No input in pipe")]
#[diagnostic(code(adnv::no_input_in_pipe))]
NoInputInPipe,
#[error("Could not delete directory: {}", path.display())]
CouldNotDeleteDirectory {
path: PathBuf,
#[source]
err: std::io::Error,
},
#[error("Could not copy directory: {} -> {}", source.display(), destination.display())]
CouldNotCopyDirectory {
source: PathBuf,
destination: PathBuf,
#[source]
err: std::io::Error,
},
#[error("Could not copy file: {} -> {}", source.display(), destination.display())]
CouldNotCopyFile {
source: PathBuf,
destination: PathBuf,
#[source]
err: std::io::Error,
},
#[error("Could not open or create file: {}", path.display())]
CouldNotOpenFile {
path: PathBuf,
#[source]
err: std::io::Error,
},
#[error("Refusing to copy a symbolic link: {}", path.display())]
#[diagnostic(
code(fs::symlink_in_source),
help(
"A tree fetched from a source may not hold symbolic links: copying one verbatim would let a remote source reach outside the generated output."
)
)]
SymlinkInSource { path: PathBuf },
#[error("Could not update {}: the file changed since it was read", path.display())]
#[diagnostic(
code(edit::file_changed),
help("Run the command again so the versions are located in the file as it is now.")
)]
FileChangedSinceParsed {
path: PathBuf,
#[source]
err: ropey::Error,
},
#[error("Could not write content in file: {}", path.display())]
CouldNotWriteFile {
path: PathBuf,
#[source]
err: std::io::Error,
},
#[error("Could not strip prefix: {} from path: {}", path.display(), to_strip.display())]
CouldNotStripPrefix {
path: PathBuf,
to_strip: PathBuf,
#[source]
err: std::path::StripPrefixError,
},
#[error("Could not fetch URL `{url}`: {msg}")]
#[diagnostic(code(fetch_error))]
UrlFetch {
msg: String,
url: url::Url,
#[source]
err: reqwest::Error,
},
#[error("Invalid archive: {msg}")]
#[diagnostic(code(invalid_archive))]
InvalidArchive { msg: String, url: url::Url },
#[error("Could not create intermediate directory")]
IntermediateDirectory(#[source] std::io::Error),
#[error("Unknown source type: {0}")]
UnknownSourceType(url::Url),
#[error("The URL fragment is missing for source: {0}")]
URLFragmentMissing(url::Url),
#[error("{msg}")]
BadQueryString {
msg: String,
#[source]
err: serde_qs::Error,
},
#[error("Could not send post to Mattermost")]
MattermostPost(
#[from]
mattermost_client::openapi::apis::Error<
mattermost_client::openapi::apis::posts_api::CreatePostError,
>,
),
#[error("There is no outputs in config file")]
NoOutputsConfigFile,
#[error("There is no matching output in config file for \"{0}\"")]
NoMatchingOutputConfigFile(String),
#[error(
"The URL '{0}' doesn't match URL structure for npm registry. Must be npm://<PACKAGE NAME> or npm+<REGISTRY URL>/<PACKAGE NAME>"
)]
NpmRegistryURL(String),
#[error("Invalid entry user_id in config file: {user_id}")]
InvalidUserId {
user_id: String,
#[source]
err: matrix_sdk::ruma::IdParseError,
},
#[error("Entry user_password in config file doesn't match user_id : {user_id} ")]
NonMatchingUserIdPassword {
user_id: String,
#[source]
err: matrix_sdk::Error,
},
#[error("Invalid entry room_id in config file: {room_id}")]
InvalidRoomId {
room_id: String,
#[source]
err: matrix_sdk::ruma::IdParseError,
},
#[error("Impossible to join room: {room_id}")]
ImpossibleToJoinRoom {
room_id: String,
#[source]
err: matrix_sdk::Error,
},
#[error("Impossible to get joined room {0} from client")]
ImpossibleToGetRoom(String),
#[error(transparent)]
BuildServer(#[from] matrix_sdk::ClientBuildError),
#[error(transparent)]
SyncServerState(#[from] matrix_sdk::Error),
#[error(transparent)]
ClientLogout(#[from] matrix_sdk::HttpError),
#[error("Invalid sender mail: \"{mail}\"")]
InvalidSenderMail {
mail: String,
#[source]
err: lettre::address::AddressError,
},
#[error("Invalid recipient mail: \"{mail}\"")]
InvalidRecipientMail {
mail: String,
#[source]
err: lettre::address::AddressError,
},
#[error(transparent)]
SMTP(#[from] lettre::transport::smtp::Error),
#[error(transparent)]
TerraformRegistry(#[from] terraform_registry::Error),
#[error("Version should be described following terraform version constraint syntax")]
TerraformVersion(#[from] terraform_version::Error),
#[error("Version requirements is not a valid SemVer version requirement")]
SemVerVersion(#[from] semver::Error),
#[error("There are no version available")]
NoVersionAvailable,
#[error("The URL fragment '{0}' doesn't match any entry from repository '{1}'")]
HelmEntryError(String, String),
#[error("Invalid source `{0}`: {1}")]
InvalidSource(url::Url, String),
#[error("Unsupported Docker Registry: {0}")]
UnsupportedDockerRegistry(url::Url),
#[error("No version described")]
NoVersionDescribed,
#[error("No comparator in the VersionRequirement")]
NoComparator,
#[error(transparent)]
HclParse(#[from] crate::parsers::hcl::Error),
#[error(transparent)]
YamlParse(#[from] marked_yaml::LoadError),
#[error(transparent)]
URLParse(#[from] url::ParseError),
#[error("Invalid URL: {}, parsing impossible", repo_url)]
InvalidUrl {
repo_url: String,
#[source]
err: url::ParseError,
},
#[error("Inline hint error on file {file}")]
HclParsing {
file: String,
#[source]
err: crate::parsers::hcl::ast::error::Error,
},
#[error(
"Invalid inline hint source: {}, see documentation for more information about inline hints format",
repo_url
)]
InvalidInlineHintSource {
repo_url: String,
#[source]
err: Box<Error>,
},
}
impl Error {
pub(crate) fn io(err: std::io::Error, msg: impl Into<String>) -> Error {
Error::Io {
msg: msg.into(),
err,
}
}
pub(crate) fn git(err: git2::Error, msg: impl Into<String>) -> Self {
Self::Git {
msg: msg.into(),
err,
}
}
pub(crate) fn github(err: octocrab::Error, msg: impl Into<String>) -> Error {
Error::GitHub {
msg: msg.into(),
err,
}
}
pub(crate) fn gitlab(err: impl Into<gitlab::GitlabError>, msg: impl Into<String>) -> Error {
Error::GitLab {
msg: msg.into(),
err: err.into(),
}
}
pub(crate) fn gitlab_boxed(
err: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
msg: impl Into<String>,
) -> Error {
Error::GitLabBoxed {
msg: msg.into(),
err: err.into(),
}
}
pub(crate) fn env(
err: std::env::VarError,
name: impl Into<String>,
msg: impl Into<String>,
) -> Error {
Error::Env {
name: name.into(),
msg: msg.into(),
err,
}
}
pub(crate) fn deserialize_config(err: serde_yaml_ng::Error, path: impl Into<PathBuf>) -> Error {
Error::DeserializeConfig {
path: path.into(),
err,
}
}
pub(crate) fn serde_qs(err: serde_qs::Error, msg: impl Into<String>) -> Self {
Self::BadQueryString {
msg: msg.into(),
err,
}
}
#[inline]
pub(crate) fn copy_directory(
err: std::io::Error,
source: impl Into<PathBuf>,
dest: impl Into<PathBuf>,
) -> Error {
Error::CouldNotCopyDirectory {
source: source.into(),
destination: dest.into(),
err,
}
}
#[inline]
pub(crate) fn invalid_archive(url: url::Url, msg: impl Into<String>) -> Error {
Error::InvalidArchive {
msg: msg.into(),
url,
}
}
#[inline]
pub(crate) fn url_fetch(err: reqwest::Error, url: url::Url, msg: impl Into<String>) -> Error {
Error::UrlFetch {
msg: msg.into(),
url,
err,
}
}
pub(crate) fn invalid_source(url: url::Url, msg: impl Into<String>) -> Error {
Error::InvalidSource(url, msg.into())
}
pub(crate) fn invalid_inline_hint(err: Error, repo_url: impl Into<String>) -> Error {
Error::InvalidInlineHintSource {
repo_url: repo_url.into(),
err: Box::new(err),
}
}
}
#[derive(Debug)]
pub struct ErrorWithData<T>(Box<(Error, T)>);
impl<T> ErrorWithData<T> {
pub fn error(&self) -> &Error {
&self.0.0
}
pub fn data(&self) -> &T {
&self.0.1
}
pub fn split(self) -> (Error, T) {
*self.0
}
}
impl Error {
pub fn with_data<T>(self, data: T) -> ErrorWithData<T> {
ErrorWithData(Box::new((self, data)))
}
}
#[derive(Debug)]
pub struct Results<T, E = Error>(Vec<T>, Vec<E>);
impl<T, E> Results<T, E> {
pub fn values(&self) -> &[T] {
&self.0
}
pub fn errors(&self) -> &[E] {
&self.1
}
pub fn split(self) -> (Vec<T>, Vec<E>) {
(self.0, self.1)
}
}
impl<T, E> From<(Vec<T>, Vec<E>)> for Results<T, E> {
fn from((values, errors): (Vec<T>, Vec<E>)) -> Self {
Self(values, errors)
}
}
impl<T, E> From<Vec<Result<T, E>>> for Results<T, E> {
fn from(results: Vec<Result<T, E>>) -> Self {
use itertools::Itertools;
let (values, errors) = results.into_iter().partition_result();
Self(values, errors)
}
}