use std::{
any::Any,
cell::{Ref, RefCell, RefMut},
fmt::{self, Display},
rc::Rc,
};
use rustc_hash::FxHashMap;
use crate::{common_traits::RcShare, context::Context, identifier::Identifier};
struct StateInner {
indent_width: u16,
cur_indent: u16,
aux_data: FxHashMap<Identifier, Box<dyn Any>>,
}
impl Default for StateInner {
fn default() -> Self {
Self {
indent_width: 2,
cur_indent: 0,
aux_data: FxHashMap::default(),
}
}
}
#[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 aux_data_ref(&self) -> Ref<'_, FxHashMap<Identifier, Box<dyn Any>>> {
Ref::map(self.0.borrow(), |inner| &inner.aux_data)
}
pub fn aux_data_mut(&self) -> RefMut<'_, FxHashMap<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 std::fmt::Formatter<'_>,
) -> std::fmt::Result {
write!(f, "{}", self)
}
}
};
}
impl_printable_for_display!(&str);
impl_printable_for_display!(String);
impl_printable_for_display!(usize);
impl_printable_for_display!(u64);
impl_printable_for_display!(u32);
impl_printable_for_display!(i64);
impl_printable_for_display!(i32);
impl_printable_for_display!(bool);
impl_printable_for_display!(char);
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(),
}
}