1use std::path::{Path, PathBuf};
10
11#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
16pub enum LeanLoaderDiagnosticCode {
17 MissingManifest,
19 MalformedManifest,
21 UnsupportedManifestSchema,
23 MissingPrimaryDylib,
25 MissingTransitiveDependency,
27 UnsupportedArchitecture,
29 UnsupportedToolchainFingerprint,
31 StaleManifest,
33 MissingInitializer,
35 MissingImportedSymbol,
37}
38
39impl LeanLoaderDiagnosticCode {
40 #[must_use]
42 pub const fn as_str(self) -> &'static str {
43 match self {
44 Self::MissingManifest => "lean_rs.loader.missing_manifest",
45 Self::MalformedManifest => "lean_rs.loader.malformed_manifest",
46 Self::UnsupportedManifestSchema => "lean_rs.loader.unsupported_manifest_schema",
47 Self::MissingPrimaryDylib => "lean_rs.loader.missing_primary_dylib",
48 Self::MissingTransitiveDependency => "lean_rs.loader.missing_transitive_dependency",
49 Self::UnsupportedArchitecture => "lean_rs.loader.unsupported_architecture",
50 Self::UnsupportedToolchainFingerprint => "lean_rs.loader.unsupported_toolchain_fingerprint",
51 Self::StaleManifest => "lean_rs.loader.stale_manifest",
52 Self::MissingInitializer => "lean_rs.loader.missing_initializer",
53 Self::MissingImportedSymbol => "lean_rs.loader.missing_imported_symbol",
54 }
55 }
56}
57
58impl std::fmt::Display for LeanLoaderDiagnosticCode {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 f.write_str(self.as_str())
61 }
62}
63
64#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
66pub enum LeanLoaderSeverity {
67 Info,
69 Warning,
71 Error,
73}
74
75pub const LOADER_DIAGNOSTIC_TEXT_LIMIT: usize = 4 * 1024;
80
81#[must_use]
84pub fn bound_loader_text(mut text: String) -> String {
85 if text.len() <= LOADER_DIAGNOSTIC_TEXT_LIMIT {
86 return text;
87 }
88 let mut cut = LOADER_DIAGNOSTIC_TEXT_LIMIT;
89 while cut > 0 && !text.is_char_boundary(cut) {
90 cut = cut.saturating_sub(1);
91 }
92 text.truncate(cut);
93 text
94}
95
96#[derive(Clone, Debug, Eq, PartialEq)]
98pub struct LeanLoaderCheck {
99 code: LeanLoaderDiagnosticCode,
100 severity: LeanLoaderSeverity,
101 subject: String,
102 message: String,
103 repair_hint: String,
104}
105
106impl LeanLoaderCheck {
107 #[must_use]
109 pub fn error(
110 code: LeanLoaderDiagnosticCode,
111 subject: impl Into<String>,
112 message: impl Into<String>,
113 repair_hint: impl Into<String>,
114 ) -> Self {
115 Self {
116 code,
117 severity: LeanLoaderSeverity::Error,
118 subject: bound_loader_text(subject.into()),
119 message: bound_loader_text(message.into()),
120 repair_hint: bound_loader_text(repair_hint.into()),
121 }
122 }
123
124 #[must_use]
126 pub fn code(&self) -> LeanLoaderDiagnosticCode {
127 self.code
128 }
129
130 #[must_use]
132 pub fn severity(&self) -> LeanLoaderSeverity {
133 self.severity
134 }
135
136 #[must_use]
138 pub fn subject(&self) -> &str {
139 &self.subject
140 }
141
142 #[must_use]
144 pub fn message(&self) -> &str {
145 &self.message
146 }
147
148 #[must_use]
150 pub fn repair_hint(&self) -> &str {
151 &self.repair_hint
152 }
153}
154
155impl std::fmt::Display for LeanLoaderCheck {
156 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157 write!(
158 f,
159 "{} [{:?}] {}: {} (repair: {})",
160 self.code.as_str(),
161 self.severity,
162 self.subject,
163 self.message,
164 self.repair_hint
165 )
166 }
167}
168
169#[derive(Clone, Debug, Eq, PartialEq)]
171pub struct LeanLoaderReport {
172 manifest_path: Option<PathBuf>,
173 checks: Vec<LeanLoaderCheck>,
174}
175
176impl LeanLoaderReport {
177 #[must_use]
179 pub fn new(manifest_path: Option<PathBuf>, checks: Vec<LeanLoaderCheck>) -> Self {
180 Self { manifest_path, checks }
181 }
182
183 #[must_use]
185 pub fn manifest_path(&self) -> Option<&Path> {
186 self.manifest_path.as_deref()
187 }
188
189 #[must_use]
191 pub fn checks(&self) -> &[LeanLoaderCheck] {
192 &self.checks
193 }
194
195 pub fn errors(&self) -> impl Iterator<Item = &LeanLoaderCheck> {
197 self.checks
198 .iter()
199 .filter(|check| check.severity == LeanLoaderSeverity::Error)
200 }
201
202 #[must_use]
204 pub fn is_ok(&self) -> bool {
205 self.errors().next().is_none()
206 }
207
208 #[must_use]
210 pub fn first_error(&self) -> Option<&LeanLoaderCheck> {
211 self.errors().next()
212 }
213
214 #[must_use]
216 pub fn into_checks(self) -> Vec<LeanLoaderCheck> {
217 self.checks
218 }
219}
220
221#[derive(Clone, Debug, Eq, PartialEq)]
223pub struct LeanModuleInitializer {
224 package: String,
225 module: String,
226}
227
228impl LeanModuleInitializer {
229 #[must_use]
231 pub fn new(package: impl Into<String>, module: impl Into<String>) -> Self {
232 Self {
233 package: package.into(),
234 module: module.into(),
235 }
236 }
237
238 #[must_use]
240 pub fn package_name(&self) -> &str {
241 &self.package
242 }
243
244 #[must_use]
246 pub fn module_name(&self) -> &str {
247 &self.module
248 }
249}
250
251#[derive(Clone, Debug, Eq, PartialEq)]
253pub struct LeanLibraryDependency {
254 path: PathBuf,
255 exports_symbols_for_dependents: bool,
256 initializer: Option<LeanModuleInitializer>,
257}
258
259impl LeanLibraryDependency {
260 #[must_use]
262 pub fn path(path: impl Into<PathBuf>) -> Self {
263 Self {
264 path: path.into(),
265 exports_symbols_for_dependents: false,
266 initializer: None,
267 }
268 }
269
270 #[must_use]
278 pub fn export_symbols_for_dependents(mut self) -> Self {
279 self.exports_symbols_for_dependents = true;
280 self
281 }
282
283 #[must_use]
285 pub fn initializer(mut self, package: impl Into<String>, module: impl Into<String>) -> Self {
286 self.initializer = Some(LeanModuleInitializer::new(package, module));
287 self
288 }
289
290 #[must_use]
292 pub fn path_ref(&self) -> &Path {
293 &self.path
294 }
295
296 #[must_use]
299 pub fn exports_symbols_for_dependents(&self) -> bool {
300 self.exports_symbols_for_dependents
301 }
302
303 #[must_use]
305 pub fn module_initializer(&self) -> Option<&LeanModuleInitializer> {
306 self.initializer.as_ref()
307 }
308
309 #[must_use]
314 pub fn into_module_initializer(self) -> Option<LeanModuleInitializer> {
315 self.initializer
316 }
317}