use lean_rs::Obj;
use lean_rs::abi::nat;
use lean_rs::abi::structure::{take_ctor_objects, view};
use lean_rs::abi::traits::{TryFromLean, conversion_error};
use lean_rs::error::{LeanResult, bound_message};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
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> {
let diagnostic = view(&obj).ctor_shape(0, 3, "Diagnostic")?;
let severity_byte = diagnostic.uint8(0, "Diagnostic.severity")?;
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 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 obj_view = view(&obj);
if obj_view.is_scalar() {
let payload = obj_view.scalar_payload(label)?;
return match payload {
0 => Ok(None),
other => Err(conversion_error(format!(
"{label}: expected Option.none (scalar tag 0), found scalar payload {other}"
))),
};
}
let ctor = obj_view.ctor()?;
if ctor.tag() != 1 {
return Err(conversion_error(format!(
"{label}: expected Option.some ctor (tag 1), found heap tag {}",
ctor.tag()
)));
}
let [inner] = take_ctor_objects::<1>(obj, 1, "Option Nat")?;
Ok(Some(decode_nat_u32(inner, label)?))
}