use std::path::{Path, PathBuf};
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum LeanLoaderDiagnosticCode {
MissingManifest,
MalformedManifest,
UnsupportedManifestSchema,
MissingPrimaryDylib,
MissingTransitiveDependency,
UnsupportedArchitecture,
UnsupportedToolchainFingerprint,
StaleManifest,
MissingInitializer,
MissingImportedSymbol,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum LeanExportSymbolKind {
Function,
Global,
}
impl LeanExportSymbolKind {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Function => "function",
Self::Global => "global",
}
}
pub(crate) fn from_str(value: &str) -> Option<Self> {
match value {
"function" => Some(Self::Function),
"global" => Some(Self::Global),
_ => None,
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum LeanExportAbiRepr {
LeanObject,
U8,
U16,
U32,
U64,
USize,
I8,
I16,
I32,
I64,
ISize,
F64,
}
impl LeanExportAbiRepr {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::LeanObject => "lean_object",
Self::U8 => "u8",
Self::U16 => "u16",
Self::U32 => "u32",
Self::U64 => "u64",
Self::USize => "usize",
Self::I8 => "i8",
Self::I16 => "i16",
Self::I32 => "i32",
Self::I64 => "i64",
Self::ISize => "isize",
Self::F64 => "f64",
}
}
pub(crate) fn from_str(value: &str) -> Option<Self> {
match value {
"lean_object" => Some(Self::LeanObject),
"u8" => Some(Self::U8),
"u16" => Some(Self::U16),
"u32" => Some(Self::U32),
"u64" => Some(Self::U64),
"usize" => Some(Self::USize),
"i8" => Some(Self::I8),
"i16" => Some(Self::I16),
"i32" => Some(Self::I32),
"i64" => Some(Self::I64),
"isize" => Some(Self::ISize),
"f64" => Some(Self::F64),
_ => None,
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum LeanExportOwnership {
None,
Owned,
}
impl LeanExportOwnership {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::None => "none",
Self::Owned => "owned",
}
}
pub(crate) fn from_str(value: &str) -> Option<Self> {
match value {
"none" => Some(Self::None),
"owned" => Some(Self::Owned),
_ => None,
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct LeanExportArgAbi {
repr: LeanExportAbiRepr,
ownership: LeanExportOwnership,
}
impl LeanExportArgAbi {
#[must_use]
pub const fn new(repr: LeanExportAbiRepr, ownership: LeanExportOwnership) -> Self {
Self { repr, ownership }
}
#[must_use]
pub const fn repr(self) -> LeanExportAbiRepr {
self.repr
}
#[must_use]
pub const fn ownership(self) -> LeanExportOwnership {
self.ownership
}
#[must_use]
pub fn to_json(self) -> serde_json::Value {
serde_json::json!({
"repr": self.repr.as_str(),
"ownership": self.ownership.as_str(),
})
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum LeanExportResultConvention {
Pure,
IoResult,
}
impl LeanExportResultConvention {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Pure => "pure",
Self::IoResult => "io_result",
}
}
pub(crate) fn from_str(value: &str) -> Option<Self> {
match value {
"pure" => Some(Self::Pure),
"io_result" => Some(Self::IoResult),
_ => None,
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct LeanExportReturnAbi {
repr: LeanExportAbiRepr,
ownership: LeanExportOwnership,
convention: LeanExportResultConvention,
}
impl LeanExportReturnAbi {
#[must_use]
pub const fn new(
repr: LeanExportAbiRepr,
ownership: LeanExportOwnership,
convention: LeanExportResultConvention,
) -> Self {
Self {
repr,
ownership,
convention,
}
}
#[must_use]
pub const fn repr(self) -> LeanExportAbiRepr {
self.repr
}
#[must_use]
pub const fn ownership(self) -> LeanExportOwnership {
self.ownership
}
#[must_use]
pub const fn convention(self) -> LeanExportResultConvention {
self.convention
}
#[must_use]
pub fn to_json(self) -> serde_json::Value {
serde_json::json!({
"repr": self.repr.as_str(),
"ownership": self.ownership.as_str(),
"convention": self.convention.as_str(),
})
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LeanExportSignature {
symbol: String,
kind: LeanExportSymbolKind,
args: Vec<LeanExportArgAbi>,
result: LeanExportReturnAbi,
}
impl LeanExportSignature {
#[must_use]
pub fn function(
symbol: impl Into<String>,
args: impl Into<Vec<LeanExportArgAbi>>,
result: LeanExportReturnAbi,
) -> Self {
Self {
symbol: symbol.into(),
kind: LeanExportSymbolKind::Function,
args: args.into(),
result,
}
}
#[must_use]
pub fn global(symbol: impl Into<String>, result: LeanExportReturnAbi) -> Self {
Self {
symbol: symbol.into(),
kind: LeanExportSymbolKind::Global,
args: Vec::new(),
result,
}
}
#[must_use]
pub fn symbol(&self) -> &str {
&self.symbol
}
#[must_use]
pub const fn kind(&self) -> LeanExportSymbolKind {
self.kind
}
#[must_use]
pub fn args(&self) -> &[LeanExportArgAbi] {
&self.args
}
#[must_use]
pub const fn result(&self) -> LeanExportReturnAbi {
self.result
}
#[must_use]
pub fn to_json(&self) -> serde_json::Value {
serde_json::json!({
"symbol": self.symbol,
"kind": self.kind.as_str(),
"args": self.args.iter().map(|arg| arg.to_json()).collect::<Vec<_>>(),
"return": self.result.to_json(),
})
}
}
impl LeanLoaderDiagnosticCode {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::MissingManifest => "lean_rs.loader.missing_manifest",
Self::MalformedManifest => "lean_rs.loader.malformed_manifest",
Self::UnsupportedManifestSchema => "lean_rs.loader.unsupported_manifest_schema",
Self::MissingPrimaryDylib => "lean_rs.loader.missing_primary_dylib",
Self::MissingTransitiveDependency => "lean_rs.loader.missing_transitive_dependency",
Self::UnsupportedArchitecture => "lean_rs.loader.unsupported_architecture",
Self::UnsupportedToolchainFingerprint => "lean_rs.loader.unsupported_toolchain_fingerprint",
Self::StaleManifest => "lean_rs.loader.stale_manifest",
Self::MissingInitializer => "lean_rs.loader.missing_initializer",
Self::MissingImportedSymbol => "lean_rs.loader.missing_imported_symbol",
}
}
}
impl std::fmt::Display for LeanLoaderDiagnosticCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum LeanLoaderSeverity {
Info,
Warning,
Error,
}
pub const LOADER_DIAGNOSTIC_TEXT_LIMIT: usize = 4 * 1024;
#[must_use]
pub fn bound_loader_text(mut text: String) -> String {
if text.len() <= LOADER_DIAGNOSTIC_TEXT_LIMIT {
return text;
}
let mut cut = LOADER_DIAGNOSTIC_TEXT_LIMIT;
while cut > 0 && !text.is_char_boundary(cut) {
cut = cut.saturating_sub(1);
}
text.truncate(cut);
text
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LeanLoaderCheck {
code: LeanLoaderDiagnosticCode,
severity: LeanLoaderSeverity,
subject: String,
message: String,
repair_hint: String,
}
impl LeanLoaderCheck {
#[must_use]
pub fn error(
code: LeanLoaderDiagnosticCode,
subject: impl Into<String>,
message: impl Into<String>,
repair_hint: impl Into<String>,
) -> Self {
Self {
code,
severity: LeanLoaderSeverity::Error,
subject: bound_loader_text(subject.into()),
message: bound_loader_text(message.into()),
repair_hint: bound_loader_text(repair_hint.into()),
}
}
#[must_use]
pub fn code(&self) -> LeanLoaderDiagnosticCode {
self.code
}
#[must_use]
pub fn severity(&self) -> LeanLoaderSeverity {
self.severity
}
#[must_use]
pub fn subject(&self) -> &str {
&self.subject
}
#[must_use]
pub fn message(&self) -> &str {
&self.message
}
#[must_use]
pub fn repair_hint(&self) -> &str {
&self.repair_hint
}
}
impl std::fmt::Display for LeanLoaderCheck {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{} [{:?}] {}: {} (repair: {})",
self.code.as_str(),
self.severity,
self.subject,
self.message,
self.repair_hint
)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LeanLoaderReport {
manifest_path: Option<PathBuf>,
checks: Vec<LeanLoaderCheck>,
}
impl LeanLoaderReport {
#[must_use]
pub fn new(manifest_path: Option<PathBuf>, checks: Vec<LeanLoaderCheck>) -> Self {
Self { manifest_path, checks }
}
#[must_use]
pub fn manifest_path(&self) -> Option<&Path> {
self.manifest_path.as_deref()
}
#[must_use]
pub fn checks(&self) -> &[LeanLoaderCheck] {
&self.checks
}
pub fn errors(&self) -> impl Iterator<Item = &LeanLoaderCheck> {
self.checks
.iter()
.filter(|check| check.severity == LeanLoaderSeverity::Error)
}
#[must_use]
pub fn is_ok(&self) -> bool {
self.errors().next().is_none()
}
#[must_use]
pub fn first_error(&self) -> Option<&LeanLoaderCheck> {
self.errors().next()
}
#[must_use]
pub fn into_checks(self) -> Vec<LeanLoaderCheck> {
self.checks
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LeanModuleInitializer {
package: String,
module: String,
}
impl LeanModuleInitializer {
#[must_use]
pub fn new(package: impl Into<String>, module: impl Into<String>) -> Self {
Self {
package: package.into(),
module: module.into(),
}
}
#[must_use]
pub fn package_name(&self) -> &str {
&self.package
}
#[must_use]
pub fn module_name(&self) -> &str {
&self.module
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LeanLibraryDependency {
path: PathBuf,
exports_symbols_for_dependents: bool,
initializer: Option<LeanModuleInitializer>,
}
impl LeanLibraryDependency {
#[must_use]
pub fn path(path: impl Into<PathBuf>) -> Self {
Self {
path: path.into(),
exports_symbols_for_dependents: false,
initializer: None,
}
}
#[must_use]
pub fn export_symbols_for_dependents(mut self) -> Self {
self.exports_symbols_for_dependents = true;
self
}
#[must_use]
pub fn initializer(mut self, package: impl Into<String>, module: impl Into<String>) -> Self {
self.initializer = Some(LeanModuleInitializer::new(package, module));
self
}
#[must_use]
pub fn path_ref(&self) -> &Path {
&self.path
}
#[must_use]
pub fn exports_symbols_for_dependents(&self) -> bool {
self.exports_symbols_for_dependents
}
#[must_use]
pub fn module_initializer(&self) -> Option<&LeanModuleInitializer> {
self.initializer.as_ref()
}
#[must_use]
pub fn into_module_initializer(self) -> Option<LeanModuleInitializer> {
self.initializer
}
}