1use std::any::Any;
2
3use kodept_ast::graph::{ChangeSet, AnyNode, PermTkn, RefMut, RefNode};
4use kodept_ast::utils::Execution;
5use kodept_ast::visit_side::{VisitGuard, VisitSide};
6use kodept_core::ConvertibleToRef;
7use kodept_core::structure::Located;
8
9use crate::error::report::ReportMessage;
10use crate::Macro;
11use crate::traits::{Context, UnrecoverableError};
12
13pub trait CanErase<C: Context> {
14 type Error;
15
16 fn erase(self) -> BoxedMacro<C, Self::Error>;
17 fn into_any(self: Box<Self>) -> Box<dyn Any>;
18}
19
20pub trait ErasedMacro<C: Context>: CanErase<C> {
21 fn transform(
22 &mut self,
23 node: RefNode,
24 side: VisitSide,
25 token: &mut PermTkn,
26 context: &mut C,
27 ) -> Execution<Self::Error, ChangeSet>;
28}
29
30pub type BoxedMacro<C, E> = Box<dyn ErasedMacro<C, Error = E>>;
31
32impl<C, T, E: Into<ReportMessage>> CanErase<C> for T
33where
34 T: Macro<Error = E> + 'static,
35 C: Context,
36 AnyNode: ConvertibleToRef<T::Node>,
37{
38 type Error = UnrecoverableError;
39
40 fn erase(self) -> BoxedMacro<C, Self::Error> {
41 Box::new(self)
42 }
43
44 fn into_any(self: Box<Self>) -> Box<dyn Any> {
45 self
46 }
47}
48
49impl<C, T, E: Into<ReportMessage>> ErasedMacro<C> for T
50where
51 C: Context,
52 T: Macro<Error = E> + 'static,
53 AnyNode: ConvertibleToRef<T::Node>,
54{
55 fn transform(
56 &mut self,
57 node: RefNode,
58 side: VisitSide,
59 token: &mut PermTkn,
60 context: &mut C,
61 ) -> Execution<Self::Error, ChangeSet> {
62 let Some(_) = node.ro(token).try_as_ref() else {
63 return Execution::Skipped;
64 };
65 let exec = <Self as Macro>::transform(
66 self,
67 VisitGuard::new(side, RefMut::new(node), token),
68 context,
69 );
70
71 match exec {
72 Execution::Failed(e) => {
73 let location = context
74 .access_unknown(node.ro(token))
75 .map_or(vec![], |it| vec![it.location()]);
76 match context.report_and_fail::<E, ()>(location, e) {
77 Ok(_) => unreachable!(),
78 Err(report) => Execution::Failed(report),
79 }
80 }
81 Execution::Completed(x) => Execution::Completed(x),
82 Execution::Skipped => Execution::Skipped,
83 }
84 }
85}