#![allow(unsafe_code)]
use lean_rs_sys::ctor::lean_ctor_get_uint8;
use lean_rs_sys::object::{lean_is_scalar, lean_obj_tag, lean_unbox};
use lean_rs::Obj;
use lean_rs::abi::nat;
use lean_rs::abi::structure::{ctor_tag, take_ctor_objects};
use lean_rs::abi::traits::{TryFromLean, conversion_error};
use lean_rs::error::{LeanResult, bound_message};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum LeanSeverity {
Info,
Warning,
Error,
}
impl LeanSeverity {
fn from_byte(byte: u8) -> LeanResult<Self> {
match byte {
0 => Ok(Self::Info),
1 => Ok(Self::Warning),
2 => Ok(Self::Error),
other => Err(conversion_error(format!(
"Lean Severity tag {other} is not in {{0=info, 1=warning, 2=error}}"
))),
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct LeanPosition {
line: u32,
column: u32,
end_line: Option<u32>,
end_column: Option<u32>,
}
impl LeanPosition {
#[must_use]
pub fn line(&self) -> u32 {
self.line
}
#[must_use]
pub fn column(&self) -> u32 {
self.column
}
#[must_use]
pub fn end_line(&self) -> Option<u32> {
self.end_line
}
#[must_use]
pub fn end_column(&self) -> Option<u32> {
self.end_column
}
}
impl<'lean> TryFromLean<'lean> for LeanPosition {
fn try_from_lean(obj: Obj<'lean>) -> LeanResult<Self> {
let [line_o, column_o, end_line_o, end_column_o] = take_ctor_objects::<4>(obj, 0, "DiagnosticPos")?;
Ok(Self {
line: decode_nat_u32(line_o, "DiagnosticPos.line")?,
column: decode_nat_u32(column_o, "DiagnosticPos.column")?,
end_line: decode_option_nat_u32(end_line_o, "DiagnosticPos.endLine")?,
end_column: decode_option_nat_u32(end_column_o, "DiagnosticPos.endColumn")?,
})
}
}
#[derive(Clone, Debug)]
pub struct LeanDiagnostic {
severity: LeanSeverity,
message: String,
position: Option<LeanPosition>,
file_label: String,
}
impl LeanDiagnostic {
#[must_use]
pub fn severity(&self) -> LeanSeverity {
self.severity
}
#[must_use]
pub fn message(&self) -> &str {
&self.message
}
#[must_use]
pub fn position(&self) -> Option<&LeanPosition> {
self.position.as_ref()
}
#[must_use]
pub fn file_label(&self) -> &str {
&self.file_label
}
pub(crate) fn synthetic_error(message: String, file_label: String) -> Self {
Self {
severity: LeanSeverity::Error,
message: bound_message(message),
position: None,
file_label,
}
}
}
impl<'lean> TryFromLean<'lean> for LeanDiagnostic {
fn try_from_lean(obj: Obj<'lean>) -> LeanResult<Self> {
require_diagnostic_ctor(&obj)?;
let ptr = obj.as_raw_borrowed();
let severity_byte = unsafe { lean_ctor_get_uint8(ptr, 0) };
let [msg_o, pos_o, label_o] = take_ctor_objects::<3>(obj, 0, "Diagnostic")?;
let severity = LeanSeverity::from_byte(severity_byte)?;
let message = bound_message(String::try_from_lean(msg_o)?);
let position = Option::<LeanPosition>::try_from_lean(pos_o)?;
let file_label = String::try_from_lean(label_o)?;
Ok(Self {
severity,
message,
position,
file_label,
})
}
}
fn require_diagnostic_ctor(obj: &Obj<'_>) -> LeanResult<()> {
let tag = ctor_tag(obj)?;
if tag == 0 {
Ok(())
} else {
Err(conversion_error(format!(
"expected Lean Diagnostic ctor (tag 0), found tag {tag}"
)))
}
}
fn decode_nat_u32(obj: Obj<'_>, label: &str) -> LeanResult<u32> {
let raw = nat::try_to_u64(obj)?;
u32::try_from(raw).map_err(|_| conversion_error(format!("{label} = {raw} exceeds u32 range")))
}
fn decode_option_nat_u32(obj: Obj<'_>, label: &str) -> LeanResult<Option<u32>> {
let ptr = obj.as_raw_borrowed();
if unsafe { lean_is_scalar(ptr) } {
let payload = unsafe { lean_unbox(ptr) };
return match payload {
0 => Ok(None),
other => Err(conversion_error(format!(
"{label}: expected Option.none (scalar tag 0), found scalar payload {other}"
))),
};
}
let tag = unsafe { lean_obj_tag(ptr) };
if tag != 1 {
return Err(conversion_error(format!(
"{label}: expected Option.some ctor (tag 1), found heap tag {tag}"
)));
}
let [inner] = take_ctor_objects::<1>(obj, 1, "Option Nat")?;
Ok(Some(decode_nat_u32(inner, label)?))
}