Skip to main content

kcode_rust_libs/
error.rs

1use std::error::Error as StdError;
2use std::fmt;
3use std::io;
4use std::path::PathBuf;
5
6use crate::CheckResult;
7
8/// The result type returned by this crate.
9pub type Result<T> = std::result::Result<T, Error>;
10
11/// An error that prevented a Rust library operation, validation run, or publication.
12#[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}