Skip to main content

chrome_for_testing_manager/
error.rs

1use crate::{Port, VersionRequest};
2use chrome_for_testing::{Platform, Version};
3use std::{
4    fmt::{Display, Formatter},
5    path::PathBuf,
6    time::Duration,
7};
8use thiserror::Error;
9use tokio::runtime::RuntimeFlavor;
10
11/// Convenience alias for `Result<T, rootcause::Report<ChromeForTestingManagerError>>`.
12///
13/// Use this in your application's signatures to avoid spelling out the wrapped error type:
14///
15/// ```no_run
16/// use chrome_for_testing_manager::{Chromedriver, ChromedriverRunConfig, Result};
17///
18/// async fn launch() -> Result<Chromedriver> {
19///     Chromedriver::run(ChromedriverRunConfig::default()).await
20/// }
21/// ```
22pub type Result<T> = std::result::Result<T, rootcause::Report<ChromeForTestingManagerError>>;
23
24/// The chrome-for-testing artifact involved in an operation.
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26#[non_exhaustive]
27pub enum ChromeForTestingArtifact {
28    /// The Chrome browser binary package.
29    Chrome,
30
31    /// The Chromedriver package.
32    ChromeDriver,
33}
34
35impl Display for ChromeForTestingArtifact {
36    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
37        match self {
38            Self::Chrome => f.write_str("chrome"),
39            Self::ChromeDriver => f.write_str("chromedriver"),
40        }
41    }
42}
43
44/// Error contexts reported by chrome-for-testing-manager operations.
45#[derive(Debug, Error)]
46#[non_exhaustive]
47pub enum ChromeForTestingManagerError {
48    /* Runtime and platform. */
49    /// The current Tokio runtime does not support async drop cleanup.
50    #[error("chromedriver requires a multi-threaded Tokio runtime; detected {runtime_flavor:?}")]
51    UnsupportedRuntime {
52        /// The detected runtime flavor.
53        runtime_flavor: RuntimeFlavor,
54    },
55
56    /// The current platform is unsupported by chrome-for-testing.
57    #[error("unsupported chrome-for-testing platform")]
58    UnsupportedPlatform,
59
60    // Cache and version resolution.
61    /// The cache directory could not be determined.
62    #[error("failed to determine cache directory; is $HOME set?")]
63    DetermineCacheDir,
64
65    /// The cache directory could not be created.
66    #[error("failed to create cache directory {}", .cache_dir.display())]
67    CreateCacheDir {
68        /// The cache directory path.
69        cache_dir: PathBuf,
70    },
71
72    /// The cache directory could not be removed.
73    #[error("failed to remove cache directory {}", .cache_dir.display())]
74    RemoveCacheDir {
75        /// The cache directory path.
76        cache_dir: PathBuf,
77    },
78
79    /// The cache directory could not be recreated.
80    #[error("failed to recreate cache directory {}", .cache_dir.display())]
81    RecreateCacheDir {
82        /// The cache directory path.
83        cache_dir: PathBuf,
84    },
85
86    /// The known-good version manifest could not be requested.
87    #[error("failed to request versions for {version_request:?}")]
88    RequestVersions {
89        /// The requested version selection.
90        version_request: VersionRequest,
91    },
92
93    /// No known-good version matched the requested selection.
94    #[error("could not determine a version for {version_request:?}")]
95    NoMatchingVersion {
96        /// The requested version selection.
97        version_request: VersionRequest,
98    },
99
100    /// No Chrome download exists for the selected version and platform.
101    #[error("no chrome download for version {version} on {platform}")]
102    NoChromeDownload {
103        /// The selected Chrome version.
104        version: Version,
105        /// The detected platform.
106        platform: Platform,
107    },
108
109    /// No Chromedriver download exists for the selected version and platform.
110    #[error("no chromedriver download for version {version} on {platform}")]
111    NoChromedriverDownload {
112        /// The selected Chrome version.
113        version: Version,
114        /// The detected platform.
115        platform: Platform,
116    },
117
118    /// The platform-specific package directory could not be created.
119    #[error("failed to create platform directory {}", .platform_dir.display())]
120    CreatePlatformDir {
121        /// The platform-specific package directory.
122        platform_dir: PathBuf,
123    },
124
125    /* Downloads and archives. */
126    /// The download request failed or returned a non-success status.
127    #[error("failed to download {artifact} from {url}")]
128    Download {
129        /// The artifact being downloaded.
130        artifact: ChromeForTestingArtifact,
131        /// The download URL.
132        url: String,
133    },
134
135    /// The downloaded archive file could not be created.
136    #[error("failed to create {artifact} download file {}", .path.display())]
137    CreateDownloadFile {
138        /// The artifact being downloaded.
139        artifact: ChromeForTestingArtifact,
140        /// The archive path.
141        path: PathBuf,
142    },
143
144    /// A chunk could not be written into the downloaded archive.
145    #[error("failed to write {artifact} download chunk")]
146    WriteDownloadFile {
147        /// The artifact being downloaded.
148        artifact: ChromeForTestingArtifact,
149    },
150
151    /// The downloaded archive file could not be flushed.
152    #[error("failed to flush {artifact} download file")]
153    FlushDownloadFile {
154        /// The artifact being downloaded.
155        artifact: ChromeForTestingArtifact,
156    },
157
158    /// The download stalled for too long.
159    #[error(
160        "{artifact} download timed out after {consecutive_stalls} consecutive stalls of {chunk_timeout:?}"
161    )]
162    DownloadStalled {
163        /// The artifact being downloaded.
164        artifact: ChromeForTestingArtifact,
165        /// The number of consecutive stalls observed.
166        consecutive_stalls: u32,
167        /// The timeout for each stall.
168        chunk_timeout: Duration,
169    },
170
171    /// The downloaded archive could not be opened.
172    #[error("failed to open downloaded ZIP archive {}", .path.display())]
173    OpenDownloadedZip {
174        /// The archive path.
175        path: PathBuf,
176    },
177
178    /// The downloaded archive was not a valid ZIP file.
179    #[error("downloaded file {} is not a valid ZIP archive", .path.display())]
180    InvalidZip {
181        /// The archive path.
182        path: PathBuf,
183    },
184
185    /// The downloaded archive exceeded the decompressed size safety limit.
186    #[error(
187        "downloaded ZIP archive {} decompressed size {size} exceeds safety limit {max_size}",
188        .path.display()
189    )]
190    ZipTooLarge {
191        /// The archive path.
192        path: PathBuf,
193        /// The reported decompressed size in bytes.
194        size: u128,
195        /// The configured maximum decompressed size in bytes.
196        max_size: u128,
197    },
198
199    /// The downloaded archive could not be extracted.
200    #[error(
201        "failed to extract ZIP archive {} to {}",
202        .path.display(),
203        .unpack_dir.display()
204    )]
205    ExtractZip {
206        /// The archive path.
207        path: PathBuf,
208        /// The destination directory.
209        unpack_dir: PathBuf,
210    },
211
212    /// The downloaded archive could not be removed after extraction.
213    #[error("failed to remove downloaded ZIP archive {}", .path.display())]
214    RemoveDownloadedZip {
215        /// The archive path.
216        path: PathBuf,
217    },
218
219    /* Chromedriver process lifecycle. */
220    /// The chromedriver process could not be spawned.
221    #[error("failed to spawn chromedriver process {}", .path.display())]
222    SpawnChromedriver {
223        /// The chromedriver executable path.
224        path: PathBuf,
225    },
226
227    /// Chromedriver did not report startup before the timeout.
228    #[error("failed while waiting for chromedriver {} to start", .path.display())]
229    WaitForChromedriverStartup {
230        /// The chromedriver executable path.
231        path: PathBuf,
232    },
233
234    /// The chromedriver process could not be terminated.
235    #[error("failed to terminate chromedriver process on port {port}")]
236    TerminateChromedriver {
237        /// The chromedriver port.
238        port: Port,
239    },
240
241    /* Session lifecycle. */
242    /// Chrome capabilities could not be prepared.
243    #[error(
244        "failed to prepare Chrome capabilities for {}",
245        .chrome_executable.display()
246    )]
247    PrepareChromeCapabilities {
248        /// The Chrome executable path.
249        chrome_executable: PathBuf,
250    },
251
252    /// User-provided capability setup failed.
253    #[error("failed to configure Chrome capabilities")]
254    ConfigureSessionCapabilities,
255
256    /// The `WebDriver` session could not be started.
257    #[error("failed to start WebDriver session on port {port}")]
258    StartWebDriverSession {
259        /// The chromedriver port.
260        port: Port,
261    },
262
263    /// User-provided session callback returned an error.
264    #[error("session callback failed")]
265    RunSessionCallback,
266
267    /// The `WebDriver` session could not be closed.
268    #[error("failed to quit WebDriver session")]
269    QuitSession,
270}