1use std::{
10 io,
11 path::{Path, PathBuf},
12 process::ExitStatus,
13};
14
15use guppy::errors::Error as GuppyError;
16use guppy::graph::PackageLink;
17use thiserror::Error;
18use toml_edit::TomlError as TomlEditError;
19use url::ParseError;
20
21use super::DependencyType;
22
23#[derive(Debug, Error)]
25pub enum Error {
26 #[error(transparent)]
28 WorkspaceError(WorkspaceError),
29
30 #[error("Workspace has at least one cycle that includes as least {crate1} and {crate2}")]
32 WorkspaceCycles {
33 crate1: String,
35
36 crate2: String,
38 },
39
40 #[error("Conditions for a release are not satisfied: {reason}")]
42 VerifyError {
43 reason: String,
45 },
46
47 #[error("{typ} of {from} on {to} prevents publication of {from}")]
51 BadDependency {
52 from: String,
54
55 to: String,
57
58 typ: DependencyType,
60 },
61
62 #[error("Unable to read file {}", path.display())]
64 FileReadError {
65 #[source]
67 inner: io::Error,
68
69 path: PathBuf,
71 },
72
73 #[error("Unable to write file {}", path.display())]
75 FileWriteError {
76 #[source]
78 inner: io::Error,
79
80 path: PathBuf,
82 },
83
84 #[error(transparent)]
86 TomlError(TomlError),
87
88 #[error("Unexpected contents of {manifest_path}")]
90 CargoTomlError {
91 #[source]
93 inner: CargoTomlError,
94
95 manifest_path: PathBuf,
97 },
98
99 #[error("Unable to run \"cargo publish\" for {manifest_path}")]
101 CargoPublish {
102 #[source]
104 inner: io::Error,
105
106 manifest_path: PathBuf,
108 },
109
110 #[error("\"cargo publish\" exited with a failure for {manifest_path}: {status}")]
112 CargoPublishStatus {
113 status: ExitStatus,
115
116 manifest_path: PathBuf,
118 },
119
120 #[error(transparent)]
122 UrlError(UrlError),
123
124 #[error(transparent)]
126 WriteReleaseError(WriteReleaseError),
127
128 #[error("Unable to update Cargo lockfile")]
130 CargoLockfileUpdate {
131 reason: String,
133 package_name: String,
135 },
136}
137
138pub type Result<T> = std::result::Result<T, anyhow::Error>;
141#[derive(Debug, Error)]
146#[error("Unable to parse the workspace structure starting at {manifest_path}")]
147pub struct WorkspaceError {
148 #[source]
149 metadata_error: GuppyError,
150 manifest_path: PathBuf,
151}
152
153#[derive(Debug, Error)]
155#[error("Unable to parse {} as a TOML file", path.display())]
156pub struct TomlError {
157 #[source]
158 inner: TomlEditError,
159 path: PathBuf,
160}
161
162#[derive(Debug, Error)]
164pub enum CargoTomlError {
165 #[error("Unable to locate expected table {table_name}")]
167 NoTable {
168 table_name: String,
170 },
171
172 #[error("Unable to located expected value {value_name}")]
174 NoValue {
175 value_name: String,
177 },
178
179 #[error("Unable to set the version for {name} to {version}")]
181 SetVersion {
182 name: String,
184
185 version: String,
187 },
188}
189
190#[derive(Debug, Error)]
192#[error("Unable to parse url for displaying release record.")]
193pub struct UrlError {
194 #[source]
195 inner: ParseError,
196}
197
198#[derive(Debug, Error)]
200#[error("Unable to write the release record for {main_crate} as JSON.")]
201pub struct WriteReleaseError {
202 #[source]
203 inner: serde_json::Error,
204
205 main_crate: String,
206}
207
208impl Error {
209 pub(crate) fn workspace_error(metadata_error: GuppyError, manifest_path: PathBuf) -> Error {
210 Error::WorkspaceError(WorkspaceError {
211 metadata_error,
212 manifest_path,
213 })
214 }
215
216 pub(crate) fn verify_error(reason: impl Into<String>) -> Error {
217 Error::VerifyError {
218 reason: reason.into(),
219 }
220 }
221
222 pub(crate) fn bad_dependency(link: &PackageLink, typ: DependencyType) -> Error {
223 Error::BadDependency {
224 from: link.from().name().to_string(),
225 to: link.to().name().to_string(),
226 typ,
227 }
228 }
229
230 pub(crate) fn file_read_error(inner: io::Error, path: impl AsRef<Path>) -> Error {
231 Error::FileReadError {
232 inner,
233 path: path.as_ref().to_owned(),
234 }
235 }
236
237 pub(crate) fn file_write_error(inner: io::Error, path: impl AsRef<Path>) -> Error {
238 Error::FileWriteError {
239 inner,
240 path: path.as_ref().to_owned(),
241 }
242 }
243
244 pub(crate) fn toml_error(inner: TomlEditError, path: impl AsRef<Path>) -> Error {
245 Error::TomlError(TomlError {
246 inner,
247 path: path.as_ref().to_owned(),
248 })
249 }
250
251 pub(crate) fn cargo_publish(inner: io::Error, manifest_path: &Path) -> Error {
252 Error::CargoPublish {
253 inner,
254 manifest_path: manifest_path.to_owned(),
255 }
256 }
257
258 pub(crate) fn cargo_publish_status(status: ExitStatus, manifest_path: &Path) -> Error {
259 Error::CargoPublishStatus {
260 status,
261 manifest_path: manifest_path.to_owned(),
262 }
263 }
264
265 pub(crate) fn url_parse_error(inner: ParseError) -> Error {
266 Error::UrlError(UrlError { inner })
267 }
268
269 pub(crate) fn write_release_error(inner: serde_json::Error, main_crate: &str) -> Error {
270 Error::WriteReleaseError(WriteReleaseError {
271 inner,
272 main_crate: main_crate.to_owned(),
273 })
274 }
275}
276
277impl CargoTomlError {
278 pub(crate) fn no_table(table: &str) -> Self {
279 Self::NoTable {
280 table_name: table.to_owned(),
281 }
282 }
283
284 pub(crate) fn no_value(value: &str) -> Self {
285 Self::NoValue {
286 value_name: value.to_owned(),
287 }
288 }
289
290 pub(crate) fn set_version(name: &str, version: &str) -> Self {
291 Self::SetVersion {
292 name: name.to_owned(),
293 version: version.to_owned(),
294 }
295 }
296
297 pub(crate) fn into_error(self, path: impl AsRef<Path>) -> Error {
298 Error::CargoTomlError {
299 inner: self,
300 manifest_path: path.as_ref().to_owned(),
301 }
302 }
303}