perseus_cli/
errors.rs

1#![allow(missing_docs)]
2
3use thiserror::Error;
4
5/// All errors that can be returned by the CLI.
6#[derive(Error, Debug)]
7pub enum Error {
8    #[error("couldn't find `cargo`, which is a dependency of this cli (set 'PERSEUS_CARGO_PATH' to another location if you've installed it elsewhere)")]
9    CargoNotPresent {
10        #[source]
11        source: std::io::Error,
12    },
13    #[error(
14        "couldn't install `wasm32-unknown-unknown` target (do you have an internet connection?)"
15    )]
16    RustupTargetAddFailed { code: i32 },
17    #[error("couldn't get current directory (have you just deleted it?)")]
18    CurrentDirUnavailable {
19        #[source]
20        source: std::io::Error,
21    },
22    #[error(transparent)]
23    ExecutionError(#[from] ExecutionError),
24    #[error(transparent)]
25    ExportError(#[from] ExportError),
26    #[error(transparent)]
27    DeployError(#[from] DeployError),
28    #[error(transparent)]
29    WatchError(#[from] WatchError),
30    #[error(transparent)]
31    InitError(#[from] InitError),
32    #[error(transparent)]
33    NewError(#[from] NewError),
34    #[error(transparent)]
35    InstallError(#[from] InstallError),
36}
37
38/// Errors that can occur while attempting to execute a Perseus app with
39/// `build`/`serve` (export errors are separate).
40#[derive(Error, Debug)]
41pub enum ExecutionError {
42    #[error("couldn't execute command '{cmd}' (this doesn't mean it threw an error, it means it couldn't be run at all)")]
43    CmdExecFailed {
44        cmd: String,
45        #[source]
46        source: std::io::Error,
47    },
48    #[error("couldn't execute command because it prematurely stopped reporting (if this persists, please report it as a bug)")]
49    NextStdoutLineNone,
50    #[error("couldn't get path to server executable (if this persists, please report it as a bug, especially if you've just updated `cargo`)")]
51    GetServerExecutableFailed {
52        #[source]
53        source: serde_json::Error,
54    },
55    #[error("couldn't get path to server executable (if this persists, try `perseus clean`)")]
56    GetServerExecutableFailedSimple,
57    #[error("expected second-last message from Cargo to contain server executable path, none existed (too few messages) (report this as a bug if it persists)")]
58    ServerExecutableMsgNotFound,
59    #[error("couldn't parse server executable path from Cargo (report this as a bug if it persists): {err}")]
60    ParseServerExecutableFailed { err: String },
61    #[error("couldn't remove and replace internal build artifact directory '{target:?}' (run `perseus clean` if this persists)")]
62    RemoveArtifactsFailed {
63        target: Option<String>,
64        #[source]
65        source: std::io::Error,
66    },
67    #[error("failed to wait on thread (please report this as a bug if it persists)")]
68    ThreadWaitFailed,
69    #[error("value in `PORT` environment variable couldn't be parsed as a number")]
70    PortNotNumber {
71        #[source]
72        source: std::num::ParseIntError,
73    },
74    #[error("couldn't parse `Cargo.toml` (are you running in the right directory?)")]
75    GetManifestFailed {
76        #[source]
77        source: cargo_toml::Error,
78    },
79    #[error("couldn't get crate name from `[package]` section of `Cargo.toml` (are you running in the right directory?)")]
80    CrateNameNotPresentInManifest,
81    #[error("couldn't create directory for distribution artifacts (do you have the necessary permissions?)")]
82    CreateDistFailed {
83        #[source]
84        source: std::io::Error,
85    },
86}
87
88/// Errors that can occur while running `perseus export`.
89#[derive(Error, Debug)]
90pub enum ExportError {
91    #[error("couldn't create directory structure necessary for exporting (do you have the necessary permissions?)")]
92    DirStructureCreationFailed {
93        #[source]
94        source: std::io::Error,
95    },
96    #[error("couldn't copy asset from '{from}' to '{to}' for exporting")]
97    MoveAssetFailed {
98        to: String,
99        from: String,
100        #[source]
101        source: std::io::Error,
102    },
103    #[error("couldn't copy directory from '{from}' to '{to}' for exporting")]
104    MoveDirFailed {
105        to: String,
106        from: String,
107        #[source]
108        source: fs_extra::error::Error,
109    },
110    // We need to execute in exports
111    #[error(transparent)]
112    ExecutionError(#[from] ExecutionError),
113}
114
115/// Errors that can occur while running `perseus deploy`.
116#[derive(Error, Debug)]
117pub enum DeployError {
118    #[error("couldn't copy exported static files from '{from:?}' to '{to}'")]
119    MoveExportDirFailed {
120        to: String,
121        from: String,
122        #[source]
123        source: fs_extra::error::Error,
124    },
125    #[error("couldn't delete and recreate output directory '{path}'")]
126    ReplaceOutputDirFailed {
127        path: String,
128        #[source]
129        source: std::io::Error,
130    },
131    #[error("couldn't copy file from '{from}' to '{to}' for deployment packaging")]
132    MoveAssetFailed {
133        to: String,
134        from: String,
135        #[source]
136        source: std::io::Error,
137    },
138    #[error("couldn't copy directory from '{from}' to '{to}' for deployment packaging")]
139    MoveDirFailed {
140        to: String,
141        from: String,
142        #[source]
143        source: fs_extra::error::Error,
144    },
145    #[error("couldn't read contents of export directory '{path}' for packaging (if this persists, try `perseus clean`)")]
146    ReadExportDirFailed {
147        path: String,
148        #[source]
149        source: std::io::Error,
150    },
151    #[error("couldn't create distribution artifacts directory for deployment (if this persists, try `perseus clean`)")]
152    CreateDistDirFailed {
153        #[source]
154        source: std::io::Error,
155    },
156    #[error("failed to minify javascript bundle (this is probably an upstream bug, re-try with `--no-minify-js`)")]
157    MinifyError {
158        #[source]
159        source: minify_js::MinifyError,
160    },
161    #[error("minified js was not utf-8 (this is a bug, re-try with `--no-minify-js` for now)")]
162    MinifyNotUtf8 {
163        #[source]
164        source: std::string::FromUtf8Error,
165    },
166    #[error("failed to read unminified js (if this persists, try `perseus clean`)")]
167    ReadUnminifiedJsFailed {
168        #[source]
169        source: std::io::Error,
170    },
171    #[error("failed to write minified js (if this persists, try `perseus clean`)")]
172    WriteMinifiedJsFailed {
173        #[source]
174        source: std::io::Error,
175    },
176}
177
178#[derive(Error, Debug)]
179pub enum WatchError {
180    #[error("couldn't set up a file watcher, try re-running this command")]
181    WatcherSetupFailed {
182        #[source]
183        source: notify::Error,
184    },
185    #[error("couldn't read your current directory to watch files, do you have the necessary permissions?")]
186    ReadCurrentDirFailed {
187        #[source]
188        source: std::io::Error,
189    },
190    #[error("couldn't read entry in your current directory, try re-running this command")]
191    ReadDirEntryFailed {
192        #[source]
193        source: std::io::Error,
194    },
195    #[error("the file/folder '{filename}' could not be resolved to an absolute path (does the file/folder exist?)")]
196    WatchFileNotResolved {
197        filename: String,
198        source: std::io::Error,
199    },
200    #[error("couldn't watch file at '{filename}', try re-running the command")]
201    WatchFileFailed {
202        filename: String,
203        #[source]
204        source: notify::Error,
205    },
206    #[error("couldn't unwatch file at '{filename}', try re-running the command")]
207    UnwatchFileFailed {
208        filename: String,
209        #[source]
210        source: notify::Error,
211    },
212    #[error("an error occurred while watching files")]
213    WatcherError {
214        #[source]
215        source: std::sync::mpsc::RecvError,
216    },
217    #[error("couldn't spawn a child process to build your app in watcher mode")]
218    SpawnSelfFailed {
219        #[source]
220        source: std::io::Error,
221    },
222    #[error("couldn't get the path to the cli's executable, try re-running the command")]
223    GetSelfPathFailed {
224        #[source]
225        source: std::io::Error,
226    },
227    #[error("couldn't read an entry in the targeted custom directory for watching, do you have the necessary permissions?")]
228    ReadCustomDirEntryFailed {
229        #[source]
230        source: walkdir::Error,
231    },
232}
233
234#[derive(Error, Debug)]
235pub enum InitError {
236    #[error("couldn't create directory structure for new project, do you have the necessary permissions?")]
237    CreateDirStructureFailed {
238        #[source]
239        source: std::io::Error,
240    },
241    #[error("couldn't create file '{filename}' for project initialization")]
242    CreateInitFileFailed {
243        #[source]
244        source: std::io::Error,
245        filename: String,
246    },
247}
248
249#[derive(Error, Debug)]
250pub enum NewError {
251    // The `new` command calls the `init` command in effect
252    #[error(transparent)]
253    InitError(#[from] InitError),
254    #[error("couldn't create directory for new project, do you have the necessary permissions?")]
255    CreateProjectDirFailed {
256        #[source]
257        source: std::io::Error,
258    },
259    #[error("fetching the custom initialization template failed")]
260    GetCustomInitFailed {
261        #[source]
262        source: ExecutionError,
263    },
264    #[error(
265        "fetching the custom initialization template returned non-zero exit code ({exit_code})"
266    )]
267    GetCustomInitNonZeroExitCode { exit_code: i32 },
268    #[error(
269        "couldn't remove git internals at '{target_dir:?}' for custom initialization template"
270    )]
271    RemoveCustomInitGitFailed {
272        target_dir: Option<String>,
273        #[source]
274        source: std::io::Error,
275    },
276}
277
278#[derive(Error, Debug)]
279pub enum InstallError {
280    #[error("couldn't create `dist/tools/` for external dependency installation")]
281    CreateToolsDirFailed {
282        #[source]
283        source: std::io::Error,
284    },
285    // This will only be called after we've checked if the user has already installed the tool
286    // themselves
287    #[error("couldn't install '{tool}', as there are no precompiled binaries for your platform and it's not currently installed; please install this tool manually (see https://framesurge.sh/perseus/en-US/docs/0.4.x/reference/faq)")]
288    ExternalToolUnavailable {
289        tool: String,
290        // This is from checking if the tool is installed at the usual path
291        #[source]
292        source: std::io::Error,
293    },
294    #[error("couldn't download binary for '{tool}' (do you have an internet connection?)")]
295    BinaryDownloadRequestFailed {
296        tool: String,
297        #[source]
298        source: reqwest::Error,
299    },
300    #[error(
301        "couldn't create destination for tool download (do you have the necessary permissions?)"
302    )]
303    CreateToolDownloadDestFailed {
304        #[source]
305        source: tokio::io::Error,
306    },
307    #[error("couldn't chunk tool download properly (do you have an internet connection?)")]
308    ChunkBinaryDownloadFailed {
309        #[source]
310        source: reqwest::Error,
311    },
312    #[error(
313        "couldn't write downloaded chunk of external tool (do you have the necessary permissions?)"
314    )]
315    WriteBinaryDownloadChunkFailed {
316        #[source]
317        source: tokio::io::Error,
318    },
319    #[error("couldn't determine latest version of '{tool}' (do you have an internet connection?)")]
320    GetLatestToolVersionFailed {
321        tool: String,
322        #[source]
323        source: reqwest::Error,
324    },
325    #[error("couldn't parse latest version of '{tool}' (if this error persists, please report it as a bug)")]
326    ParseToolVersionFailed { tool: String },
327    #[error("couldn't create destination for extraction of external tool (do you have the necessary permissions?)")]
328    CreateToolExtractDestFailed {
329        #[source]
330        source: std::io::Error,
331    },
332    #[error("couldn't extract '{tool}' (do you have the necessary permissions?)")]
333    ToolExtractFailed {
334        tool: String,
335        #[source]
336        source: std::io::Error,
337    },
338    #[error("couldn't delete archive from tool deletion")]
339    ArchiveDeletionFailed {
340        #[source]
341        source: std::io::Error,
342    },
343    #[error("couldn't rename directory for external tool binaries")]
344    DirRenameFailed {
345        #[source]
346        source: std::io::Error,
347    },
348    #[error("couldn't read `dist/tools/` to determine which tool versions were installed (do you have the necessary permissions?)")]
349    ReadToolsDirFailed {
350        #[source]
351        source: std::io::Error,
352    },
353    #[error("directory found in `dist/tools/` with invalid name (running `perseus clean` should resolve this)")]
354    InvalidToolsDirName { name: String },
355    #[error("generating `Cargo.lock` returned non-zero exit code")]
356    LockfileGenerationNonZero { code: i32 },
357    #[error("couldn't generate `Cargo.lock`")]
358    LockfileGenerationFailed {
359        #[source]
360        source: ExecutionError,
361    },
362    #[error("couldn't fetch metadata for current crate (have you run `perseus init` yet?)")]
363    MetadataFailed {
364        #[source]
365        source: cargo_metadata::Error,
366    },
367    #[error("couldn't load `Cargo.lock` from workspace root")]
368    LockfileLoadFailed {
369        #[source]
370        source: cargo_lock::Error,
371    },
372}