geam_core/plan/execution/
explain.rs1use 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}