1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
use std::cell::{Cell, RefCell};
use std::io::{Error, Write};

use codespan_reporting::diagnostic::Severity;

use kodept_ast::graph::GenericASTNode;
use kodept_ast::visitor::TraversingResult;
use kodept_ast::visitor::visit_side::{RefVisitGuard, VisitSide};
use kodept_core::Named;

use crate::analyzer::Analyzer;
use crate::error::report::ReportMessage;
use crate::traits::{Context, UnrecoverableError};

pub struct ASTFormatter<W: Write> {
    writer: RefCell<W>,
    indent: Cell<usize>,
}

impl<W: Write> Named for ASTFormatter<W> {}

struct IOError(Error);

impl From<IOError> for ReportMessage {
    fn from(value: IOError) -> Self {
        Self::new(Severity::Bug, "IO000", value.0.to_string())
    }
}

impl<W: Write> ASTFormatter<W> {
    pub fn new(writer: W) -> Self {
        Self {
            writer: RefCell::new(writer),
            indent: Cell::new(0),
        }
    }
}

#[inline]
fn report_io_error<'a, 'c, C: Context<'c>>(
    ctx: &'a C,
) -> impl Fn(Error) -> Result<(), UnrecoverableError> + 'a {
    move |e| ctx.report_and_fail(vec![], IOError(e)).map(|_| ())
}

impl<W: Write> Analyzer for ASTFormatter<W> {
    type Error = UnrecoverableError;
    type Node<'n> = &'n GenericASTNode;

    fn analyze<'n, 'c, C: Context<'c>>(
        &self,
        guard: RefVisitGuard<Self::Node<'n>>,
        context: &mut C,
    ) -> TraversingResult<Self::Error> {
        let (node, _, side) = guard.allow_all();
        let mut f = self.writer.borrow_mut();

        match side {
            VisitSide::Entering => {
                write!(f, "{}", "  ".repeat(self.indent.get()))
                    .or_else(report_io_error(context))?;
                self.indent.set(self.indent.get() + 1);
            }
            VisitSide::Exiting => {
                self.indent.set(self.indent.get() - 1);
                write!(f, "{}", "  ".repeat(self.indent.get()))
                    .or_else(report_io_error(context))?;
            }
            _ => {
                write!(f, "{}", "  ".repeat(self.indent.get()))
                    .or_else(report_io_error(context))?;
            }
        }

        match side {
            VisitSide::Entering => writeln!(f, "{:?} {{", node),
            VisitSide::Leaf => writeln!(f, "{:?};", node),
            VisitSide::Exiting => writeln!(f, "}}"),
        }
        .or_else(report_io_error(context))?;

        Ok(())
    }
}