use alloc::{boxed::Box, rc::Rc, string::String};
use core::{
any::Any,
cell::{Ref, RefCell, RefMut},
fmt::{self, Display},
};
use crate::{common_traits::RcShare, context::Context, identifier::Identifier, utils::table::HMap};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum RegionPrintDepthLimit {
#[default]
Unlimited,
Max(u32),
}
struct StateInner {
indent_width: u16,
cur_indent: u16,
region_print_depth_limit: RegionPrintDepthLimit,
aux_data: HMap<Identifier, Box<dyn Any>>,
}
impl Default for StateInner {
fn default() -> Self {
Self {
indent_width: 2,
cur_indent: 0,
aux_data: HMap::default(),
region_print_depth_limit: RegionPrintDepthLimit::Unlimited,
}
}
}
#[derive(Default)]
pub struct State(Rc<RefCell<StateInner>>);
impl RcShare for State {
fn share(&self) -> Self {
State(Rc::clone(&self.0))
}
}
impl State {
pub fn indent_width(&self) -> u16 {
self.0.as_ref().borrow().indent_width
}
pub fn set_indent_width(&self, indent_width: u16) {
self.0.as_ref().borrow_mut().indent_width = indent_width;
}
pub fn current_indent(&self) -> u16 {
self.0.as_ref().borrow().cur_indent
}
pub fn push_indent(&self) {
let mut inner = self.0.as_ref().borrow_mut();
inner.cur_indent += inner.indent_width;
}
pub fn pop_indent(&self) {
let mut inner = self.0.as_ref().borrow_mut();
inner.cur_indent -= inner.indent_width;
}
pub fn set_region_print_depth_limit(&self, limit: RegionPrintDepthLimit) {
self.0.borrow_mut().region_print_depth_limit = limit;
}
pub fn push_region_depth(&self) -> bool {
let mut inner = self.0.borrow_mut();
match &mut inner.region_print_depth_limit {
RegionPrintDepthLimit::Unlimited => true,
RegionPrintDepthLimit::Max(0) => false,
RegionPrintDepthLimit::Max(remaining) => {
*remaining -= 1;
true
}
}
}
pub fn pop_region_depth(&self) {
let mut inner = self.0.borrow_mut();
if let RegionPrintDepthLimit::Max(remaining) = &mut inner.region_print_depth_limit {
*remaining += 1;
}
}
pub fn aux_data_ref(&self) -> Ref<'_, HMap<Identifier, Box<dyn Any>>> {
Ref::map(self.0.borrow(), |inner| &inner.aux_data)
}
pub fn aux_data_mut(&self) -> RefMut<'_, HMap<Identifier, Box<dyn Any>>> {
RefMut::map(self.0.borrow_mut(), |inner| &mut inner.aux_data)
}
}
#[macro_export]
macro_rules! indented_block {
($state:ident, { $($tt:tt)* }) => {
$state.push_indent();
$($tt)*
$state.pop_indent();
}
}
struct Displayable<'t, 'c, T: Printable + ?Sized> {
t: &'t T,
ctx: &'c Context,
state: State,
}
impl<T: Printable + ?Sized> Display for Displayable<'_, '_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.t.fmt(self.ctx, &self.state, f)
}
}
pub trait Printable {
fn fmt(&self, ctx: &Context, state: &State, f: &mut fmt::Formatter<'_>) -> fmt::Result;
fn disp<'t, 'c>(&'t self, ctx: &'c Context) -> Box<dyn Display + 'c>
where
't: 'c,
{
self.print(ctx, &State::default())
}
fn print<'t, 'c>(&'t self, ctx: &'c Context, state: &State) -> Box<dyn Display + 'c>
where
't: 'c,
{
Box::new(Displayable {
t: self,
ctx,
state: state.share(),
})
}
}
#[macro_export]
macro_rules! impl_printable_for_display {
($($ty_name:ty),* $(,)?) => {
$(
impl $crate::printable::Printable for $ty_name {
fn fmt(
&self,
_ctx: &pliron::context::Context,
_state: &pliron::printable::State,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
write!(f, "{}", self)
}
}
)*
};
}
impl_printable_for_display!(
&str, String, usize, u64, u32, u16, u8, i64, i32, i16, i8, bool, char
);
#[macro_export]
macro_rules! impl_printable_for_debug {
($($ty_name:ty),* $(,)?) => {
$(
impl $crate::printable::Printable for $ty_name {
fn fmt(
&self,
_ctx: &pliron::context::Context,
_state: &pliron::printable::State,
f: &mut core::fmt::Formatter<'_>,
) -> core::fmt::Result {
write!(f, "{:?}", self)
}
}
)*
};
}
impl_printable_for_debug!(*const (), *mut ());
impl<T: Printable + ?Sized> Printable for &T {
fn fmt(&self, ctx: &Context, state: &State, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(*self).fmt(ctx, state, f)
}
}
#[derive(Clone, Copy)]
pub enum ListSeparator {
None,
Newline,
CharNewline(char),
Char(char),
CharSpace(char),
}
impl Printable for ListSeparator {
fn fmt(&self, _ctx: &Context, state: &State, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ListSeparator::None => Ok(()),
ListSeparator::Newline => fmt_indented_newline(state, f),
ListSeparator::CharNewline(c) => {
write!(f, "{c}")?;
fmt_indented_newline(state, f)
}
ListSeparator::Char(c) => write!(f, "{c}"),
ListSeparator::CharSpace(c) => write!(f, "{c} "),
}
}
}
pub fn fmt_iter<I>(
mut iter: I,
ctx: &Context,
state: &State,
sep: ListSeparator,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result
where
I: Iterator,
I::Item: Printable,
{
if let Some(first) = iter.next() {
first.fmt(ctx, state, f)?;
}
for item in iter {
sep.fmt(ctx, state, f)?;
item.fmt(ctx, state, f)?;
}
Ok(())
}
pub fn fmt_indented_newline(state: &State, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let align = state.current_indent().into();
write!(f, "\n{:>align$}", "")?;
Ok(())
}
struct IndentedNewliner {
state: State,
}
impl Display for IndentedNewliner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_indented_newline(&self.state, f)
}
}
pub fn indented_nl(state: &State) -> impl Display {
IndentedNewliner {
state: state.share(),
}
}