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}\n{stderr}")]
112 CargoPublishStatus {
113 status: ExitStatus,
115
116 manifest_path: PathBuf,
118
119 stderr: String,
121 },
122
123 #[error(transparent)]
125 UrlError(UrlError),
126
127 #[error(transparent)]
129 WriteReleaseError(WriteReleaseError),
130
131 #[error("Unable to update Cargo lockfile")]
133 CargoLockfileUpdate {
134 reason: String,
136 package_name: String,
138 },
139}
140
141pub type Result<T> = std::result::Result<T, anyhow::Error>;
144#[derive(Debug, Error)]
149#[error("Unable to parse the workspace structure starting at {manifest_path}")]
150pub struct WorkspaceError {
151 #[source]
152 metadata_error: GuppyError,
153 manifest_path: PathBuf,
154}
155
156#[derive(Debug, Error)]
158#[error("Unable to parse {} as a TOML file", path.display())]
159pub struct TomlError {
160 #[source]
161 inner: TomlEditError,
162 path: PathBuf,
163}
164
165#[derive(Debug, Error)]
167pub enum CargoTomlError {
168 #[error("Unable to locate expected table {table_name}")]
170 NoTable {
171 table_name: String,
173 },
174
175 #[error("Unable to located expected value {value_name}")]
177 NoValue {
178 value_name: String,
180 },
181
182 #[error("Unable to set the version for {name} to {version}")]
184 SetVersion {
185 name: String,
187
188 version: String,
190 },
191}
192
193#[derive(Debug, Error)]
195#[error("Unable to parse url for displaying release record.")]
196pub struct UrlError {
197 #[source]
198 inner: ParseError,
199}
200
201#[derive(Debug, Error)]
203#[error("Unable to write the release record for {main_crate} as JSON.")]
204pub struct WriteReleaseError {
205 #[source]
206 inner: serde_json::Error,
207
208 main_crate: String,
209}
210
211impl Error {
212 pub(crate) fn workspace_error(metadata_error: GuppyError, manifest_path: PathBuf) -> Error {
213 Error::WorkspaceError(WorkspaceError {
214 metadata_error,
215 manifest_path,
216 })
217 }
218
219 pub(crate) fn verify_error(reason: impl Into<String>) -> Error {
220 Error::VerifyError {
221 reason: reason.into(),
222 }
223 }
224
225 pub(crate) fn bad_dependency(link: &PackageLink, typ: DependencyType) -> Error {
226 Error::BadDependency {
227 from: link.from().name().to_string(),
228 to: link.to().name().to_string(),
229 typ,
230 }
231 }
232
233 pub(crate) fn file_read_error(inner: io::Error, path: impl AsRef<Path>) -> Error {
234 Error::FileReadError {
235 inner,
236 path: path.as_ref().to_owned(),
237 }
238 }
239
240 pub(crate) fn file_write_error(inner: io::Error, path: impl AsRef<Path>) -> Error {
241 Error::FileWriteError {
242 inner,
243 path: path.as_ref().to_owned(),
244 }
245 }
246
247 pub(crate) fn toml_error(inner: TomlEditError, path: impl AsRef<Path>) -> Error {
248 Error::TomlError(TomlError {
249 inner,
250 path: path.as_ref().to_owned(),
251 })
252 }
253
254 pub(crate) fn cargo_publish(inner: io::Error, manifest_path: &Path) -> Error {
255 Error::CargoPublish {
256 inner,
257 manifest_path: manifest_path.to_owned(),
258 }
259 }
260
261 pub(crate) fn cargo_publish_status(
262 status: ExitStatus,
263 manifest_path: &Path,
264 stderr: &[u8],
265 ) -> Error {
266 Error::CargoPublishStatus {
267 status,
268 manifest_path: manifest_path.to_owned(),
269 stderr: String::from_utf8_lossy(stderr).into_owned(),
270 }
271 }
272
273 pub(crate) fn url_parse_error(inner: ParseError) -> Error {
274 Error::UrlError(UrlError { inner })
275 }
276
277 pub(crate) fn write_release_error(inner: serde_json::Error, main_crate: &str) -> Error {
278 Error::WriteReleaseError(WriteReleaseError {
279 inner,
280 main_crate: main_crate.to_owned(),
281 })
282 }
283}
284
285impl CargoTomlError {
286 pub(crate) fn no_table(table: &str) -> Self {
287 Self::NoTable {
288 table_name: table.to_owned(),
289 }
290 }
291
292 pub(crate) fn no_value(value: &str) -> Self {
293 Self::NoValue {
294 value_name: value.to_owned(),
295 }
296 }
297
298 pub(crate) fn set_version(name: &str, version: &str) -> Self {
299 Self::SetVersion {
300 name: name.to_owned(),
301 version: version.to_owned(),
302 }
303 }
304
305 pub(crate) fn into_error(self, path: impl AsRef<Path>) -> Error {
306 Error::CargoTomlError {
307 inner: self,
308 manifest_path: path.as_ref().to_owned(),
309 }
310 }
311}