plotnik_lib/query/
infer_dump.rs1use std::fmt::Write;
4
5use crate::ir::{TYPE_NODE, TYPE_STR, TYPE_VOID, TypeId, TypeKind};
6
7use super::infer::TypeInferenceResult;
8
9impl TypeInferenceResult<'_> {
10 pub fn dump(&self) -> String {
11 let mut out = String::new();
12 let printer = TypePrinter::new(self);
13 printer.format(&mut out).expect("String write never fails");
14 out
15 }
16
17 pub fn dump_diagnostics(&self, source: &str) -> String {
18 self.diagnostics.render_filtered(source)
19 }
20
21 pub fn has_errors(&self) -> bool {
22 !self.errors.is_empty()
23 }
24}
25
26struct TypePrinter<'a, 'src> {
27 result: &'a TypeInferenceResult<'src>,
28 width: usize,
29}
30
31impl<'a, 'src> TypePrinter<'a, 'src> {
32 fn new(result: &'a TypeInferenceResult<'src>) -> Self {
33 let total_types = 3 + result.type_defs.len();
34 let width = if total_types == 0 {
35 1
36 } else {
37 ((total_types as f64).log10().floor() as usize) + 1
38 };
39 Self { result, width }
40 }
41
42 fn format(&self, w: &mut String) -> std::fmt::Result {
43 for (name, type_id) in &self.result.entrypoint_types {
45 let type_name = self.get_type_name(*type_id);
46 if type_name.as_deref() == Some(*name) {
47 continue;
48 }
49 writeln!(w, "{} = {}", name, self.format_type(*type_id))?;
50 }
51
52 let has_entrypoints = self
53 .result
54 .entrypoint_types
55 .iter()
56 .any(|(name, id)| self.get_type_name(*id).as_deref() != Some(*name));
57
58 let mut first_typedef = true;
60 for (idx, def) in self.result.type_defs.iter().enumerate() {
61 let type_id = 3 + idx as TypeId;
62
63 if self.is_inlinable(type_id) {
64 continue;
65 }
66
67 if first_typedef && has_entrypoints {
68 writeln!(w)?;
69 }
70 first_typedef = false;
71
72 let header = self.format_type_header(type_id, def.name);
73
74 match def.kind {
75 TypeKind::Record => {
76 if def.members.len() == 1 {
77 let m = &def.members[0];
78 writeln!(
79 w,
80 "{} = {{ {}: {} }}",
81 header,
82 m.name,
83 self.format_type(m.ty)
84 )?;
85 } else {
86 writeln!(w, "{} = {{", header)?;
87 for member in &def.members {
88 writeln!(w, " {}: {}", member.name, self.format_type(member.ty))?;
89 }
90 writeln!(w, "}}")?;
91 }
92 }
93 TypeKind::Enum => {
94 if def.members.len() == 1 {
95 let m = &def.members[0];
96 writeln!(
97 w,
98 "{} = {{ {} => {} }}",
99 header,
100 m.name,
101 self.format_type(m.ty)
102 )?;
103 } else {
104 writeln!(w, "{} = {{", header)?;
105 for member in &def.members {
106 writeln!(w, " {} => {}", member.name, self.format_type(member.ty))?;
107 }
108 writeln!(w, "}}")?;
109 }
110 }
111 TypeKind::Optional => {
112 let inner = def
113 .inner_type
114 .map(|t| self.format_type(t))
115 .unwrap_or_default();
116 writeln!(w, "{} = {}?", header, inner)?;
117 }
118 TypeKind::ArrayStar => {
119 let inner = def
120 .inner_type
121 .map(|t| self.format_type(t))
122 .unwrap_or_default();
123 writeln!(w, "{} = [{}]", header, inner)?;
124 }
125 TypeKind::ArrayPlus => {
126 let inner = def
127 .inner_type
128 .map(|t| self.format_type(t))
129 .unwrap_or_default();
130 writeln!(w, "{} = [{}]⁺", header, inner)?;
131 }
132 }
133 }
134
135 if !self.result.errors.is_empty() {
137 if has_entrypoints || !first_typedef {
138 writeln!(w)?;
139 }
140 writeln!(w, "Errors:")?;
141 for err in &self.result.errors {
142 let types = err
143 .types_found
144 .iter()
145 .map(|t| t.to_string())
146 .collect::<Vec<_>>()
147 .join(", ");
148 writeln!(
149 w,
150 " field `{}` in `{}`: incompatible types [{}]",
151 err.field, err.definition, types
152 )?;
153 }
154 }
155
156 Ok(())
157 }
158
159 fn get_type_name(&self, id: TypeId) -> Option<String> {
160 if id < 3 {
161 return None;
162 }
163 let idx = (id - 3) as usize;
164 self.result
165 .type_defs
166 .get(idx)
167 .and_then(|def| def.name.map(|s| s.to_string()))
168 }
169
170 fn is_inlinable(&self, id: TypeId) -> bool {
173 if id < 3 {
174 return true; }
176 let idx = (id - 3) as usize;
177 let Some(def) = self.result.type_defs.get(idx) else {
178 return false;
179 };
180
181 if def.name.is_some() {
183 return false;
184 }
185
186 match def.kind {
187 TypeKind::Record | TypeKind::Enum => false,
188 TypeKind::Optional | TypeKind::ArrayStar | TypeKind::ArrayPlus => {
189 def.inner_type.map(|t| self.is_inlinable(t)).unwrap_or(true)
190 }
191 }
192 }
193
194 fn format_type_header(&self, type_id: TypeId, name: Option<&str>) -> String {
195 match name {
196 Some(n) => n.to_string(),
197 None => format!("T{:0width$}", type_id, width = self.width),
198 }
199 }
200
201 fn format_type(&self, id: TypeId) -> String {
202 match id {
203 TYPE_VOID => "()".to_string(),
204 TYPE_NODE => "Node".to_string(),
205 TYPE_STR => "str".to_string(),
206 _ => {
207 let idx = (id - 3) as usize;
208 if let Some(def) = self.result.type_defs.get(idx) {
209 if let Some(name) = def.name {
211 return name.to_string();
212 }
213
214 if self.is_inlinable(id) {
216 let inner = def
217 .inner_type
218 .map(|t| self.format_type(t))
219 .unwrap_or_default();
220 return match def.kind {
221 TypeKind::Optional => format!("{}?", inner),
222 TypeKind::ArrayStar => format!("[{}]", inner),
223 TypeKind::ArrayPlus => format!("[{}]⁺", inner),
224 _ => format!("T{:0width$}", id, width = self.width),
225 };
226 }
227 }
228 format!("T{:0width$}", id, width = self.width)
229 }
230 }
231 }
232}