Skip to main content

ephemeris_release/
error.rs

1use std::error::Error as StdError;
2
3use ephemeris_files::{FilesError, RepositoryPath, RepositoryRoot};
4
5use crate::adapters::ResolveError;
6use crate::edits::EditError;
7use crate::proposal::ProposalError;
8use crate::publication::PublicationError;
9use crate::state::BranchError;
10
11/// A release operation failed.
12///
13/// The error only reports what went wrong. Its [`Display`](core::fmt::Display)
14/// output and its source chain carry the details.
15#[derive(Debug, thiserror::Error)]
16#[error(transparent)]
17#[expect(
18    clippy::error_impl_error,
19    reason = "the crate's entry points all return this one error"
20)]
21pub struct Error(ErrorKind);
22
23/// What failed, kept private so the failure modes can change freely.
24#[derive(Debug, thiserror::Error)]
25enum ErrorKind {
26    #[error(transparent)]
27    Adapter(#[from] ResolveError),
28    #[error(transparent)]
29    Branch(#[from] BranchError),
30    #[error(transparent)]
31    Edit(#[from] EditError),
32    #[error(transparent)]
33    Proposal(#[from] ProposalError),
34    #[error(transparent)]
35    Publication(Box<PublicationError>),
36}
37
38impl From<PublicationError> for ErrorKind {
39    fn from(error: PublicationError) -> Self {
40        Self::Publication(Box::new(error))
41    }
42}
43
44impl<E> From<E> for Error
45where
46    ErrorKind: From<E>,
47{
48    fn from(error: E) -> Self {
49        Self(ErrorKind::from(error))
50    }
51}
52
53/// A failure of a Git operation, kept as its source.
54#[derive(Debug, thiserror::Error)]
55#[error(transparent)]
56pub(crate) struct GitFailure(Box<dyn StdError + Send + Sync>);
57
58/// A failure of a forge call, kept as its source.
59#[derive(Debug, thiserror::Error)]
60#[error(transparent)]
61pub(crate) struct ForgeFailure(Box<dyn StdError + Send + Sync>);
62
63/// A failure of a package registry or package publication, kept as its
64/// source.
65#[derive(Debug, thiserror::Error)]
66#[error(transparent)]
67pub(crate) struct PackageFailure(Box<dyn StdError + Send + Sync>);
68
69impl GitFailure {
70    /// Wrap `error`.
71    pub(crate) fn new<E: StdError + Send + Sync + 'static>(error: E) -> Self {
72        Self(Box::new(error))
73    }
74
75    #[cfg(test)]
76    pub(crate) fn downcast_ref<E: StdError + 'static>(&self) -> Option<&E> {
77        self.0.downcast_ref()
78    }
79}
80
81impl ForgeFailure {
82    /// Wrap `error`.
83    pub(crate) fn new<E: StdError + Send + Sync + 'static>(error: E) -> Self {
84        Self(Box::new(error))
85    }
86}
87
88impl PackageFailure {
89    /// Wrap `error`.
90    pub(crate) fn new<E: StdError + Send + Sync + 'static>(error: E) -> Self {
91        Self(Box::new(error))
92    }
93}
94
95/// Reading a file that must exist failed.
96#[derive(Debug, thiserror::Error)]
97pub(crate) enum RequiredFileError {
98    /// The file cannot be read.
99    #[error(transparent)]
100    Files(#[from] FilesError),
101    /// The file does not exist.
102    #[error("{} does not exist", path.as_path().display())]
103    Missing {
104        /// The file.
105        path: RepositoryPath,
106    },
107}
108
109impl RequiredFileError {
110    /// The content of the file at `path`.
111    pub(crate) fn read(root: &RepositoryRoot, path: &RepositoryPath) -> Result<Vec<u8>, Self> {
112        root.read(path)?
113            .ok_or_else(|| Self::Missing { path: path.clone() })
114    }
115}