Skip to main content

geam_core/plan/execution/
explain.rs

1use super::{ExecutionPlan, HostedExecution};
2use crate::host::HostProfile;
3use crate::plan::execution::type_::{ValueShapeId, ValueShapeTable, ValueType};
4use std::fmt;
5
6pub(in crate::plan::execution) trait Explain {
7    fn write_explanation(&self, context: &mut ExplainContext<'_, '_>);
8}
9
10#[derive(Clone, Copy)]
11pub(in crate::plan::execution) struct FunctionLabel {
12    family: &'static str,
13    index: usize,
14}
15
16impl FunctionLabel {
17    pub(in crate::plan::execution) fn new(family: &'static str, index: usize) -> Self {
18        Self { family, index }
19    }
20
21    pub(in crate::plan::execution) fn write(self, output: &mut String) {
22        output.push_str(self.family);
23        output.push('#');
24        output.push_str(&self.index.to_string());
25    }
26}
27
28pub(in crate::plan::execution) struct ExplainContext<'plan, 'output> {
29    value_shapes: &'plan ValueShapeTable,
30    output: &'output mut String,
31}
32
33impl<'plan, 'output> ExplainContext<'plan, 'output> {
34    pub(in crate::plan::execution) fn new(
35        plan: &'plan ExecutionPlan,
36        output: &'output mut String,
37    ) -> Self {
38        Self {
39            value_shapes: &plan.program.common.value_shapes,
40            output,
41        }
42    }
43
44    pub(in crate::plan::execution) fn new_hosted<Profile: HostProfile>(
45        execution: &'plan HostedExecution<Profile>,
46        output: &'output mut String,
47    ) -> Self {
48        Self {
49            value_shapes: &execution.program.common.value_shapes,
50            output,
51        }
52    }
53
54    pub(in crate::plan::execution) fn shape_value_type(&self, id: ValueShapeId) -> ValueType {
55        self.value_shapes.value_type(id).clone()
56    }
57
58    pub(in crate::plan::execution) fn output(&mut self) -> &mut String {
59        self.output
60    }
61
62    pub(in crate::plan::execution) fn push(&mut self, character: char) {
63        self.output.push(character);
64    }
65
66    pub(in crate::plan::execution) fn push_str(&mut self, text: &str) {
67        self.output.push_str(text);
68    }
69
70    pub(in crate::plan::execution) fn write<Value>(&mut self, value: &Value)
71    where
72        Value: Explain + ?Sized,
73    {
74        value.write_explanation(self);
75    }
76
77    pub(in crate::plan::execution) fn write_list<Value>(
78        &mut self,
79        values: &[Value],
80        mut write_value: impl FnMut(&mut Self, &Value),
81    ) {
82        self.output.push('[');
83        for (index, value) in values.iter().enumerate() {
84            if index > 0 {
85                self.output.push_str(", ");
86            }
87            write_value(self, value);
88        }
89        self.output.push(']');
90    }
91}
92
93pub struct ExecutionPlanExplanation<'a> {
94    execution: ExplainedExecution<'a>,
95}
96
97enum ExplainedExecution<'a> {
98    Plain(&'a ExecutionPlan),
99    Hosted(&'a dyn HostedExplanation),
100}
101
102trait HostedExplanation {
103    fn write_to(&self, output: &mut String);
104}
105
106impl<'a> ExecutionPlanExplanation<'a> {
107    pub(super) fn new(plan: &'a ExecutionPlan) -> Self {
108        Self {
109            execution: ExplainedExecution::Plain(plan),
110        }
111    }
112
113    pub(super) fn new_hosted<Profile: HostProfile>(
114        execution: &'a HostedExecution<Profile>,
115    ) -> Self {
116        Self {
117            execution: ExplainedExecution::Hosted(execution),
118        }
119    }
120}
121
122impl fmt::Display for ExecutionPlanExplanation<'_> {
123    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
124        let mut output = String::new();
125        match self.execution {
126            ExplainedExecution::Plain(plan) => {
127                let mut context = ExplainContext::new(plan, &mut output);
128                context.write(plan);
129            }
130            ExplainedExecution::Hosted(execution) => execution.write_to(&mut output),
131        }
132        formatter.write_str(&output)
133    }
134}
135
136impl<Profile: HostProfile> HostedExplanation for HostedExecution<Profile> {
137    fn write_to(&self, output: &mut String) {
138        let mut context = ExplainContext::new_hosted(self, output);
139        context.write(self);
140    }
141}
142
143#[cfg(test)]
144pub(in crate::plan::execution) fn with_execution_plan<Result>(
145    source: &str,
146    inspect: impl FnOnce(&ExecutionPlan) -> Result,
147) -> Result {
148    let typed =
149        crate::compile_typed_module("main", "main.gleam", source).expect("source should compile");
150    let module_plan = crate::plan_module(typed).expect("source should plan");
151    let plan = ExecutionPlan::from_module_plan(module_plan);
152    inspect(&plan)
153}
154
155#[cfg(test)]
156pub(in crate::plan::execution) fn assert_rendered(
157    source: &str,
158    expected: &str,
159    render: impl FnOnce(&ExecutionPlan, &mut String),
160) {
161    with_execution_plan(source, |plan| {
162        let mut actual = String::new();
163        render(plan, &mut actual);
164        assert_eq!(actual, expected, "source:\n{source}");
165    });
166}
167
168#[cfg(test)]
169pub(in crate::plan::execution) fn assert_written(expected: &str, write: impl FnOnce(&mut String)) {
170    let mut actual = String::new();
171    write(&mut actual);
172    assert_eq!(actual, expected);
173}
174
175#[cfg(test)]
176mod tests {
177    use crate::ExecutionPlanExplanation;
178
179    #[test]
180    fn formats_the_public_execution_plan_facade() {
181        let source = "pub fn main() { 1 }";
182        let expected = concat!(
183            "module main\n",
184            "main int#0\n",
185            "\n",
186            "function int#0\n",
187            "  entry b0 params=[] captures=[]\n",
188            "  block b0 params=[]\n",
189            "    %int#0:shape#0(Int) = int.value 1\n",
190            "    return %int#0\n",
191        );
192
193        super::assert_rendered(source, expected, |plan, output| {
194            let explanation: ExecutionPlanExplanation<'_> = plan.explain();
195            output.push_str(&explanation.to_string());
196        });
197    }
198}