1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
//! Errors produced by the `patchsense` library.
//!
//! The binary maps these into `anyhow` errors and an exit code. Library
//! code never prints them itself.
use std::path::PathBuf;
use thiserror::Error;
/// Recoverable failure while reading a project, talking to a data source, or
/// building a report.
#[derive(Debug, Error)]
pub enum CoreError {
/// Filesystem read or write failed.
#[error("failed to read {path}: {source}")]
Io {
/// Path that could not be read or written.
path: PathBuf,
/// Underlying OS error.
#[source]
source: std::io::Error,
},
/// A lockfile or config file could not be parsed.
#[error("failed to parse {path}{}: {message}", line.map(|n| format!(":{n}")).unwrap_or_default())]
Parse {
/// File that failed to parse.
path: PathBuf,
/// Human-readable cause.
message: String,
/// 1-based line, when the format exposes one.
line: Option<usize>,
},
/// JSON from a file or an API body was not the shape this crate expects.
#[error("invalid JSON from {context}: {message}")]
Json {
/// File path or URL the JSON came from.
context: String,
/// Serde or structural cause.
message: String,
},
/// An HTTP call failed, or the status was not usable after retries.
#[error("HTTP request to {url} failed: {message}")]
Http {
/// Request URL.
url: String,
/// Status or transport cause.
message: String,
},
/// Config or CLI named an ecosystem this build does not know.
#[error("unknown ecosystem `{name}`")]
UnknownEcosystem {
/// The name that was rejected.
name: String,
},
/// Scan root contained no supported lockfile.
#[error("no supported lockfile found in {path}{hint}")]
NoLockfile {
/// Directory or file that was scanned.
path: PathBuf,
/// Extra text, or empty. Includes its own leading separator.
hint: String,
},
/// `--offline` needed an OSV cache row that was missing or expired.
#[error("{message}")]
OfflineCache {
/// What was missing and how to fill the cache.
message: String,
},
/// OSV response was missing a field or disagreed with the request.
#[error("invalid OSV response from {url}: {message}")]
Osv {
/// Endpoint URL.
url: String,
/// What was wrong.
message: String,
},
/// The SQLite cache could not be opened or updated.
#[error("cache error at {path}: {message}")]
Cache {
/// Database file.
path: PathBuf,
/// SQLite or schema cause.
message: String,
},
}
impl CoreError {
/// Parse error that names `path` and an optional 1-based line.
pub fn parse(
path: impl Into<PathBuf>,
line: Option<usize>,
message: impl Into<String>,
) -> Self {
Self::Parse {
path: path.into(),
message: message.into(),
line,
}
}
/// JSON error that names the file or URL it came from.
pub fn json(context: impl Into<String>, message: impl Into<String>) -> Self {
Self::Json {
context: context.into(),
message: message.into(),
}
}
}