use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use pyo3_stub_gen::define_stub_info_gatherer;
use pyo3_stub_gen::derive::{gen_stub_pyclass, gen_stub_pymethods};
use crate::RenderedBlock;
#[gen_stub_pyclass]
#[pyclass(name = "RenderedBlock", module = "term_maths")]
pub struct PyRenderedBlock(pub RenderedBlock);
#[gen_stub_pymethods]
#[pymethods]
impl PyRenderedBlock {
#[staticmethod]
fn from_char(ch: &str) -> PyResult<PyRenderedBlock> {
let c = ch.chars().next().ok_or_else(|| {
PyValueError::new_err("from_char expects a single character, got an empty string")
})?;
Ok(PyRenderedBlock(RenderedBlock::from_char(c)))
}
#[staticmethod]
fn from_text(text: &str) -> PyRenderedBlock {
PyRenderedBlock(RenderedBlock::from_text(text))
}
#[staticmethod]
fn empty() -> PyRenderedBlock {
PyRenderedBlock(RenderedBlock::empty())
}
#[staticmethod]
fn hline(ch: &str, width: usize) -> PyResult<PyRenderedBlock> {
let c = ch.chars().next().ok_or_else(|| {
PyValueError::new_err("hline expects a single character, got an empty string")
})?;
Ok(PyRenderedBlock(RenderedBlock::hline(c, width)))
}
#[getter]
fn width(&self) -> usize {
self.0.width()
}
#[getter]
fn height(&self) -> usize {
self.0.height()
}
#[getter]
fn baseline(&self) -> usize {
self.0.baseline()
}
fn cells(&self) -> Vec<Vec<String>> {
self.0.cells().to_vec()
}
fn is_empty(&self) -> bool {
self.0.is_empty()
}
fn beside(&self, other: &PyRenderedBlock) -> PyRenderedBlock {
PyRenderedBlock(self.0.beside(&other.0))
}
#[staticmethod]
fn above(
top: &PyRenderedBlock,
bottom: &PyRenderedBlock,
baseline_row: usize,
) -> PyRenderedBlock {
PyRenderedBlock(RenderedBlock::above(&top.0, &bottom.0, baseline_row))
}
fn pad(&self, left: usize, right: usize, top: usize, bottom: usize) -> PyRenderedBlock {
PyRenderedBlock(self.0.pad(left, right, top, bottom))
}
fn center_in(&self, target_width: usize) -> PyRenderedBlock {
PyRenderedBlock(self.0.center_in(target_width))
}
fn __str__(&self) -> String {
format!("{}", self.0)
}
fn __repr__(&self) -> String {
format!(
"RenderedBlock(width={}, height={}, baseline={})",
self.0.width(),
self.0.height(),
self.0.baseline(),
)
}
}
fn parse_font_kind(font: &str) -> PyResult<rust_latex_parser::MathFontKind> {
use rust_latex_parser::MathFontKind;
match font {
"bold" => Ok(MathFontKind::Bold),
"blackboard" => Ok(MathFontKind::Blackboard),
"calligraphic" => Ok(MathFontKind::Calligraphic),
"fraktur" => Ok(MathFontKind::Fraktur),
"roman" => Ok(MathFontKind::Roman),
"sans_serif" => Ok(MathFontKind::SansSerif),
"monospace" => Ok(MathFontKind::Monospace),
other => Err(PyValueError::new_err(format!(
"Unknown font kind {other:?}. \
Valid options: bold, blackboard, calligraphic, fraktur, roman, sans_serif, monospace"
))),
}
}
#[pymodule]
pub mod _term_maths {
use super::*;
#[pymodule_export]
use super::PyRenderedBlock;
#[pyfunction]
pub fn render(latex: &str) -> PyRenderedBlock {
PyRenderedBlock(crate::render(latex))
}
#[pyfunction]
pub fn to_latex(latex: &str) -> String {
crate::to_latex(latex)
}
#[pyfunction]
pub fn map_char(font: &str, ch: &str) -> PyResult<String> {
let kind = parse_font_kind(font)?;
let c = ch.chars().next().ok_or_else(|| {
PyValueError::new_err("map_char expects a single character, got an empty string")
})?;
Ok(crate::mathfont::map_char(&kind, c).to_string())
}
#[pyfunction]
pub fn map_str(font: &str, s: &str) -> PyResult<String> {
let kind = parse_font_kind(font)?;
Ok(crate::mathfont::map_str(&kind, s))
}
}
define_stub_info_gatherer!(stub_info_gatherer);