1use std::error::Error as StdError;
2use std::fmt;
3use std::io;
4use std::path::PathBuf;
5
6use crate::CheckResult;
7
8pub type Result<T> = std::result::Result<T, Error>;
10
11#[derive(Debug)]
13pub enum Error {
14 InvalidRustLibName(String),
15 InvalidRustLibPath {
16 path: String,
17 reason: &'static str,
18 },
19 RustLibAlreadyExists(String),
20 RustLibNotFound(String),
21 RustLibIsNotDirectory(String),
22 DuplicateWritePath(String),
23 NonUtf8Path(PathBuf),
24 NonUtf8File(PathBuf),
25 SymlinkNotAllowed(PathBuf),
26 UnsupportedFileType(PathBuf),
27 RootsOverlap {
28 rust_libs_root: PathBuf,
29 work_root: PathBuf,
30 },
31 MissingRequiredFile(&'static str),
32 InvalidVersion(String),
33 InvalidCargoManifest(String),
34 VersionMismatch {
35 version_file: String,
36 cargo_manifest: String,
37 },
38 MissingRegistryToken(PathBuf),
39 InvalidRegistryToken(PathBuf),
40 CheckFailed(CheckResult),
41 Io {
42 action: &'static str,
43 path: PathBuf,
44 source: io::Error,
45 },
46 Sandbox {
47 stage: String,
48 message: String,
49 },
50 Publish(String),
51}
52
53impl Error {
54 pub(crate) fn io(action: &'static str, path: impl Into<PathBuf>, source: io::Error) -> Self {
55 Self::Io {
56 action,
57 path: path.into(),
58 source,
59 }
60 }
61}
62
63impl fmt::Display for Error {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 match self {
66 Self::InvalidRustLibName(name) => write!(
67 f,
68 "invalid Rust library name {name:?}; use ASCII letters, digits, '-' or '_'"
69 ),
70 Self::InvalidRustLibPath { path, reason } => {
71 write!(f, "invalid Rust library path {path:?}: {reason}")
72 }
73 Self::RustLibAlreadyExists(name) => {
74 write!(f, "Rust library {name:?} already exists")
75 }
76 Self::RustLibNotFound(name) => write!(f, "Rust library {name:?} was not found"),
77 Self::RustLibIsNotDirectory(name) => {
78 write!(f, "Rust library {name:?} is not a directory")
79 }
80 Self::DuplicateWritePath(path) => {
81 write!(f, "write batch contains duplicate path {path:?}")
82 }
83 Self::NonUtf8Path(path) => {
84 write!(
85 f,
86 "Rust library contains a non-UTF-8 path: {}",
87 path.display()
88 )
89 }
90 Self::NonUtf8File(path) => {
91 write!(f, "Rust library file is not UTF-8: {}", path.display())
92 }
93 Self::SymlinkNotAllowed(path) => {
94 write!(
95 f,
96 "Rust library symlinks are not allowed: {}",
97 path.display()
98 )
99 }
100 Self::UnsupportedFileType(path) => write!(
101 f,
102 "Rust library entry is not a regular file or directory: {}",
103 path.display()
104 ),
105 Self::RootsOverlap {
106 rust_libs_root,
107 work_root,
108 } => write!(
109 f,
110 "Rust libraries root ({}) and work root ({}) must not overlap",
111 rust_libs_root.display(),
112 work_root.display()
113 ),
114 Self::MissingRequiredFile(path) => {
115 write!(f, "Rust library is missing required file {path}")
116 }
117 Self::InvalidVersion(value) => write!(
118 f,
119 "invalid Version.txt value {value:?}; expected canonical major.minor.patch with an optional trailing newline"
120 ),
121 Self::InvalidCargoManifest(message) => {
122 write!(f, "invalid Cargo.toml for publication: {message}")
123 }
124 Self::VersionMismatch {
125 version_file,
126 cargo_manifest,
127 } => write!(
128 f,
129 "Version.txt version {version_file} does not match Cargo.toml package version {cargo_manifest}"
130 ),
131 Self::MissingRegistryToken(path) => {
132 write!(
133 f,
134 "crates.io registry token file was not found at {}",
135 path.display()
136 )
137 }
138 Self::InvalidRegistryToken(path) => write!(
139 f,
140 "crates.io registry token file is empty or is not UTF-8: {}",
141 path.display()
142 ),
143 Self::CheckFailed(result) => {
144 if let Some(stage) = result.failure() {
145 write!(
146 f,
147 "Rust library cannot be published because its {} check failed",
148 stage.stage
149 )
150 } else {
151 f.write_str(
152 "Rust library cannot be published because its full check did not pass",
153 )
154 }
155 }
156 Self::Io {
157 action,
158 path,
159 source,
160 } => write!(f, "failed to {action} {}: {source}", path.display()),
161 Self::Sandbox { stage, message } => {
162 write!(f, "Podman failed during {stage}: {message}")
163 }
164 Self::Publish(message) => write!(f, "crates.io publication failed: {message}"),
165 }
166 }
167}
168
169impl StdError for Error {
170 fn source(&self) -> Option<&(dyn StdError + 'static)> {
171 match self {
172 Self::Io { source, .. } => Some(source),
173 _ => None,
174 }
175 }
176}