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
39#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
41pub enum LeanExportSymbolKind {
42 Function,
44 Global,
46}
47
48impl LeanExportSymbolKind {
49 #[must_use]
51 pub const fn as_str(self) -> &'static str {
52 match self {
53 Self::Function => "function",
54 Self::Global => "global",
55 }
56 }
57
58 pub(crate) fn from_str(value: &str) -> Option<Self> {
59 match value {
60 "function" => Some(Self::Function),
61 "global" => Some(Self::Global),
62 _ => None,
63 }
64 }
65}
66
67#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
69pub enum LeanExportAbiRepr {
70 LeanObject,
72 U8,
74 U16,
76 U32,
78 U64,
80 USize,
82 I8,
84 I16,
86 I32,
88 I64,
90 ISize,
92 F64,
94}
95
96impl LeanExportAbiRepr {
97 #[must_use]
99 pub const fn as_str(self) -> &'static str {
100 match self {
101 Self::LeanObject => "lean_object",
102 Self::U8 => "u8",
103 Self::U16 => "u16",
104 Self::U32 => "u32",
105 Self::U64 => "u64",
106 Self::USize => "usize",
107 Self::I8 => "i8",
108 Self::I16 => "i16",
109 Self::I32 => "i32",
110 Self::I64 => "i64",
111 Self::ISize => "isize",
112 Self::F64 => "f64",
113 }
114 }
115
116 pub(crate) fn from_str(value: &str) -> Option<Self> {
117 match value {
118 "lean_object" => Some(Self::LeanObject),
119 "u8" => Some(Self::U8),
120 "u16" => Some(Self::U16),
121 "u32" => Some(Self::U32),
122 "u64" => Some(Self::U64),
123 "usize" => Some(Self::USize),
124 "i8" => Some(Self::I8),
125 "i16" => Some(Self::I16),
126 "i32" => Some(Self::I32),
127 "i64" => Some(Self::I64),
128 "isize" => Some(Self::ISize),
129 "f64" => Some(Self::F64),
130 _ => None,
131 }
132 }
133}
134
135#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
137pub enum LeanExportOwnership {
138 None,
140 Owned,
142}
143
144impl LeanExportOwnership {
145 #[must_use]
147 pub const fn as_str(self) -> &'static str {
148 match self {
149 Self::None => "none",
150 Self::Owned => "owned",
151 }
152 }
153
154 pub(crate) fn from_str(value: &str) -> Option<Self> {
155 match value {
156 "none" => Some(Self::None),
157 "owned" => Some(Self::Owned),
158 _ => None,
159 }
160 }
161}
162
163#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
165pub struct LeanExportArgAbi {
166 repr: LeanExportAbiRepr,
167 ownership: LeanExportOwnership,
168}
169
170impl LeanExportArgAbi {
171 #[must_use]
173 pub const fn new(repr: LeanExportAbiRepr, ownership: LeanExportOwnership) -> Self {
174 Self { repr, ownership }
175 }
176
177 #[must_use]
179 pub const fn repr(self) -> LeanExportAbiRepr {
180 self.repr
181 }
182
183 #[must_use]
185 pub const fn ownership(self) -> LeanExportOwnership {
186 self.ownership
187 }
188
189 #[must_use]
191 pub fn to_json(self) -> serde_json::Value {
192 serde_json::json!({
193 "repr": self.repr.as_str(),
194 "ownership": self.ownership.as_str(),
195 })
196 }
197}
198
199#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
201pub enum LeanExportResultConvention {
202 Pure,
204 IoResult,
206}
207
208impl LeanExportResultConvention {
209 #[must_use]
211 pub const fn as_str(self) -> &'static str {
212 match self {
213 Self::Pure => "pure",
214 Self::IoResult => "io_result",
215 }
216 }
217
218 pub(crate) fn from_str(value: &str) -> Option<Self> {
219 match value {
220 "pure" => Some(Self::Pure),
221 "io_result" => Some(Self::IoResult),
222 _ => None,
223 }
224 }
225}
226
227#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
229pub struct LeanExportReturnAbi {
230 repr: LeanExportAbiRepr,
231 ownership: LeanExportOwnership,
232 convention: LeanExportResultConvention,
233}
234
235impl LeanExportReturnAbi {
236 #[must_use]
238 pub const fn new(
239 repr: LeanExportAbiRepr,
240 ownership: LeanExportOwnership,
241 convention: LeanExportResultConvention,
242 ) -> Self {
243 Self {
244 repr,
245 ownership,
246 convention,
247 }
248 }
249
250 #[must_use]
252 pub const fn repr(self) -> LeanExportAbiRepr {
253 self.repr
254 }
255
256 #[must_use]
258 pub const fn ownership(self) -> LeanExportOwnership {
259 self.ownership
260 }
261
262 #[must_use]
264 pub const fn convention(self) -> LeanExportResultConvention {
265 self.convention
266 }
267
268 #[must_use]
270 pub fn to_json(self) -> serde_json::Value {
271 serde_json::json!({
272 "repr": self.repr.as_str(),
273 "ownership": self.ownership.as_str(),
274 "convention": self.convention.as_str(),
275 })
276 }
277}
278
279#[derive(Clone, Debug, Eq, PartialEq)]
281pub struct LeanExportSignature {
282 symbol: String,
283 kind: LeanExportSymbolKind,
284 args: Vec<LeanExportArgAbi>,
285 result: LeanExportReturnAbi,
286}
287
288impl LeanExportSignature {
289 #[must_use]
291 pub fn function(
292 symbol: impl Into<String>,
293 args: impl Into<Vec<LeanExportArgAbi>>,
294 result: LeanExportReturnAbi,
295 ) -> Self {
296 Self {
297 symbol: symbol.into(),
298 kind: LeanExportSymbolKind::Function,
299 args: args.into(),
300 result,
301 }
302 }
303
304 #[must_use]
306 pub fn global(symbol: impl Into<String>, result: LeanExportReturnAbi) -> Self {
307 Self {
308 symbol: symbol.into(),
309 kind: LeanExportSymbolKind::Global,
310 args: Vec::new(),
311 result,
312 }
313 }
314
315 #[must_use]
317 pub fn symbol(&self) -> &str {
318 &self.symbol
319 }
320
321 #[must_use]
323 pub const fn kind(&self) -> LeanExportSymbolKind {
324 self.kind
325 }
326
327 #[must_use]
329 pub fn args(&self) -> &[LeanExportArgAbi] {
330 &self.args
331 }
332
333 #[must_use]
335 pub const fn result(&self) -> LeanExportReturnAbi {
336 self.result
337 }
338
339 #[must_use]
341 pub fn to_json(&self) -> serde_json::Value {
342 serde_json::json!({
343 "symbol": self.symbol,
344 "kind": self.kind.as_str(),
345 "args": self.args.iter().map(|arg| arg.to_json()).collect::<Vec<_>>(),
346 "return": self.result.to_json(),
347 })
348 }
349}
350
351impl LeanLoaderDiagnosticCode {
352 #[must_use]
354 pub const fn as_str(self) -> &'static str {
355 match self {
356 Self::MissingManifest => "lean_rs.loader.missing_manifest",
357 Self::MalformedManifest => "lean_rs.loader.malformed_manifest",
358 Self::UnsupportedManifestSchema => "lean_rs.loader.unsupported_manifest_schema",
359 Self::MissingPrimaryDylib => "lean_rs.loader.missing_primary_dylib",
360 Self::MissingTransitiveDependency => "lean_rs.loader.missing_transitive_dependency",
361 Self::UnsupportedArchitecture => "lean_rs.loader.unsupported_architecture",
362 Self::UnsupportedToolchainFingerprint => "lean_rs.loader.unsupported_toolchain_fingerprint",
363 Self::StaleManifest => "lean_rs.loader.stale_manifest",
364 Self::MissingInitializer => "lean_rs.loader.missing_initializer",
365 Self::MissingImportedSymbol => "lean_rs.loader.missing_imported_symbol",
366 }
367 }
368}
369
370impl std::fmt::Display for LeanLoaderDiagnosticCode {
371 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
372 f.write_str(self.as_str())
373 }
374}
375
376#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
378pub enum LeanLoaderSeverity {
379 Info,
381 Warning,
383 Error,
385}
386
387pub const LOADER_DIAGNOSTIC_TEXT_LIMIT: usize = 4 * 1024;
392
393#[must_use]
396pub fn bound_loader_text(mut text: String) -> String {
397 if text.len() <= LOADER_DIAGNOSTIC_TEXT_LIMIT {
398 return text;
399 }
400 let mut cut = LOADER_DIAGNOSTIC_TEXT_LIMIT;
401 while cut > 0 && !text.is_char_boundary(cut) {
402 cut = cut.saturating_sub(1);
403 }
404 text.truncate(cut);
405 text
406}
407
408#[derive(Clone, Debug, Eq, PartialEq)]
410pub struct LeanLoaderCheck {
411 code: LeanLoaderDiagnosticCode,
412 severity: LeanLoaderSeverity,
413 subject: String,
414 message: String,
415 repair_hint: String,
416}
417
418impl LeanLoaderCheck {
419 #[must_use]
421 pub fn error(
422 code: LeanLoaderDiagnosticCode,
423 subject: impl Into<String>,
424 message: impl Into<String>,
425 repair_hint: impl Into<String>,
426 ) -> Self {
427 Self {
428 code,
429 severity: LeanLoaderSeverity::Error,
430 subject: bound_loader_text(subject.into()),
431 message: bound_loader_text(message.into()),
432 repair_hint: bound_loader_text(repair_hint.into()),
433 }
434 }
435
436 #[must_use]
438 pub fn code(&self) -> LeanLoaderDiagnosticCode {
439 self.code
440 }
441
442 #[must_use]
444 pub fn severity(&self) -> LeanLoaderSeverity {
445 self.severity
446 }
447
448 #[must_use]
450 pub fn subject(&self) -> &str {
451 &self.subject
452 }
453
454 #[must_use]
456 pub fn message(&self) -> &str {
457 &self.message
458 }
459
460 #[must_use]
462 pub fn repair_hint(&self) -> &str {
463 &self.repair_hint
464 }
465}
466
467impl std::fmt::Display for LeanLoaderCheck {
468 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
469 write!(
470 f,
471 "{} [{:?}] {}: {} (repair: {})",
472 self.code.as_str(),
473 self.severity,
474 self.subject,
475 self.message,
476 self.repair_hint
477 )
478 }
479}
480
481#[derive(Clone, Debug, Eq, PartialEq)]
483pub struct LeanLoaderReport {
484 manifest_path: Option<PathBuf>,
485 checks: Vec<LeanLoaderCheck>,
486}
487
488impl LeanLoaderReport {
489 #[must_use]
491 pub fn new(manifest_path: Option<PathBuf>, checks: Vec<LeanLoaderCheck>) -> Self {
492 Self { manifest_path, checks }
493 }
494
495 #[must_use]
497 pub fn manifest_path(&self) -> Option<&Path> {
498 self.manifest_path.as_deref()
499 }
500
501 #[must_use]
503 pub fn checks(&self) -> &[LeanLoaderCheck] {
504 &self.checks
505 }
506
507 pub fn errors(&self) -> impl Iterator<Item = &LeanLoaderCheck> {
509 self.checks
510 .iter()
511 .filter(|check| check.severity == LeanLoaderSeverity::Error)
512 }
513
514 #[must_use]
516 pub fn is_ok(&self) -> bool {
517 self.errors().next().is_none()
518 }
519
520 #[must_use]
522 pub fn first_error(&self) -> Option<&LeanLoaderCheck> {
523 self.errors().next()
524 }
525
526 #[must_use]
528 pub fn into_checks(self) -> Vec<LeanLoaderCheck> {
529 self.checks
530 }
531}
532
533#[derive(Clone, Debug, Eq, PartialEq)]
535pub struct LeanModuleInitializer {
536 package: String,
537 module: String,
538}
539
540impl LeanModuleInitializer {
541 #[must_use]
543 pub fn new(package: impl Into<String>, module: impl Into<String>) -> Self {
544 Self {
545 package: package.into(),
546 module: module.into(),
547 }
548 }
549
550 #[must_use]
552 pub fn package_name(&self) -> &str {
553 &self.package
554 }
555
556 #[must_use]
558 pub fn module_name(&self) -> &str {
559 &self.module
560 }
561}
562
563#[derive(Clone, Debug, Eq, PartialEq)]
565pub struct LeanLibraryDependency {
566 path: PathBuf,
567 exports_symbols_for_dependents: bool,
568 initializer: Option<LeanModuleInitializer>,
569}
570
571impl LeanLibraryDependency {
572 #[must_use]
574 pub fn path(path: impl Into<PathBuf>) -> Self {
575 Self {
576 path: path.into(),
577 exports_symbols_for_dependents: false,
578 initializer: None,
579 }
580 }
581
582 #[must_use]
590 pub fn export_symbols_for_dependents(mut self) -> Self {
591 self.exports_symbols_for_dependents = true;
592 self
593 }
594
595 #[must_use]
597 pub fn initializer(mut self, package: impl Into<String>, module: impl Into<String>) -> Self {
598 self.initializer = Some(LeanModuleInitializer::new(package, module));
599 self
600 }
601
602 #[must_use]
604 pub fn path_ref(&self) -> &Path {
605 &self.path
606 }
607
608 #[must_use]
611 pub fn exports_symbols_for_dependents(&self) -> bool {
612 self.exports_symbols_for_dependents
613 }
614
615 #[must_use]
617 pub fn module_initializer(&self) -> Option<&LeanModuleInitializer> {
618 self.initializer.as_ref()
619 }
620
621 #[must_use]
626 pub fn into_module_initializer(self) -> Option<LeanModuleInitializer> {
627 self.initializer
628 }
629}