wat_service/types_analyzer/
renderer.rs

1use super::{
2    signature::Signature,
3    types::{FieldType, Fields, HeapType, OperandType, RefType, StorageType, ValType},
4};
5use crate::{helpers::RenderWithDb, idx::InternIdent};
6use std::fmt::{self, Display, Write};
7use wat_syntax::SyntaxKind;
8
9impl<'db> Signature<'db> {
10    pub(crate) fn render(&self, db: &'db dyn salsa::Database) -> RenderWithDb<'db, (&Self, bool)> {
11        RenderWithDb {
12            value: (self, false),
13            db,
14        }
15    }
16    pub(crate) fn render_compact(
17        &self,
18        db: &'db dyn salsa::Database,
19    ) -> RenderWithDb<'db, (&Self, bool)> {
20        RenderWithDb {
21            value: (self, true),
22            db,
23        }
24    }
25}
26impl Display for RenderWithDb<'_, (&Signature<'_>, bool)> {
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        if self.value.1 {
29            write!(f, "[")?;
30            let mut params = self.value.0.params.iter();
31            if let Some((ty, _)) = params.next() {
32                write!(f, "{}", ty.render(self.db))?;
33                params.try_for_each(|(ty, _)| write!(f, ", {}", ty.render(self.db)))?;
34            }
35            write!(f, "] -> [")?;
36            let mut results = self.value.0.results.iter();
37            if let Some(ty) = results.next() {
38                write!(f, "{}", ty.render(self.db))?;
39                results.try_for_each(|ty| write!(f, ", {}", ty.render(self.db)))?;
40            }
41            write!(f, "]")
42        } else {
43            let mut has_params = false;
44            let mut params = self.value.0.params.iter();
45            if let Some((ty, name)) = params.next() {
46                has_params = true;
47                if let Some(name) = name {
48                    write!(f, "(param {} {})", name.ident(self.db), ty.render(self.db))?;
49                } else {
50                    write!(f, "(param {})", ty.render(self.db))?;
51                }
52                params.try_for_each(|(ty, name)| {
53                    if let Some(name) = name {
54                        write!(f, " (param {} {})", name.ident(self.db), ty.render(self.db))
55                    } else {
56                        write!(f, " (param {})", ty.render(self.db))
57                    }
58                })?;
59            }
60            let mut results = self.value.0.results.iter();
61            if let Some(ty) = results.next() {
62                if has_params {
63                    write!(f, " ")?;
64                }
65                write!(f, "(result {})", ty.render(self.db))?;
66                results.try_for_each(|ty| write!(f, " (result {})", ty.render(self.db)))
67            } else {
68                Ok(())
69            }
70        }
71    }
72}
73
74#[salsa::tracked]
75pub(crate) fn render_func_header<'db>(
76    db: &'db dyn salsa::Database,
77    name: Option<InternIdent<'db>>,
78    signature: Signature<'db>,
79) -> String {
80    let mut content = "(func".to_string();
81    if let Some(name) = name {
82        content.push(' ');
83        content.push_str(name.ident(db));
84    }
85    if !signature.params.is_empty() || !signature.results.is_empty() {
86        content.push(' ');
87        let _ = write!(content, "{}", signature.render(db));
88    }
89    content.push(')');
90    content
91}
92
93#[salsa::tracked]
94pub(crate) fn render_block_header<'db>(
95    db: &'db dyn salsa::Database,
96    kind: SyntaxKind,
97    name: Option<InternIdent<'db>>,
98    signature: Signature<'db>,
99) -> String {
100    let mut content = format!(
101        "({}",
102        match kind {
103            SyntaxKind::BLOCK_IF => "if",
104            SyntaxKind::BLOCK_LOOP => "loop",
105            SyntaxKind::MODULE_FIELD_FUNC => "func",
106            _ => "block",
107        }
108    );
109    if let Some(name) = name {
110        content.push(' ');
111        content.push_str(name.ident(db));
112    }
113    if !signature.params.is_empty() || !signature.results.is_empty() {
114        content.push(' ');
115        let _ = write!(content, "{}", signature.render(db));
116    }
117    content.push(')');
118    content
119}
120
121impl<'db> RefType<'db> {
122    pub(crate) fn render(&self, db: &'db dyn salsa::Database) -> RenderWithDb<'db, &Self> {
123        RenderWithDb { value: self, db }
124    }
125}
126impl Display for RenderWithDb<'_, &RefType<'_>> {
127    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128        if matches!(self.value.heap_ty, HeapType::DefFunc(..)) {
129            write!(f, "(func ")?;
130        } else {
131            write!(f, "(ref ")?;
132        }
133        if self.value.nullable {
134            write!(f, "null ")?;
135        }
136        match self.value.heap_ty {
137            HeapType::Type(idx) | HeapType::DefFunc(idx) => {
138                if let Some(name) = idx.name {
139                    write!(f, "{}", name.ident(self.db))?;
140                } else if let Some(num) = idx.num {
141                    write!(f, "{num}")?;
142                }
143            }
144            HeapType::Any => write!(f, "any")?,
145            HeapType::Eq => write!(f, "eq")?,
146            HeapType::I31 => write!(f, "i31")?,
147            HeapType::Struct => write!(f, "struct")?,
148            HeapType::Array => write!(f, "array")?,
149            HeapType::None => write!(f, "none")?,
150            HeapType::Func => write!(f, "func")?,
151            HeapType::NoFunc => write!(f, "nofunc")?,
152            HeapType::Extern => write!(f, "extern")?,
153            HeapType::NoExtern => write!(f, "noextern")?,
154            HeapType::Rec(..) => unreachable!("rec type is only for internal use"),
155        }
156        write!(f, ")")
157    }
158}
159
160impl<'db> ValType<'db> {
161    pub(crate) fn render(&self, db: &'db dyn salsa::Database) -> RenderWithDb<'db, &Self> {
162        RenderWithDb { value: self, db }
163    }
164}
165impl Display for RenderWithDb<'_, &ValType<'_>> {
166    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167        match &self.value {
168            ValType::I32 => write!(f, "i32"),
169            ValType::I64 => write!(f, "i64"),
170            ValType::F32 => write!(f, "f32"),
171            ValType::F64 => write!(f, "f64"),
172            ValType::V128 => write!(f, "v128"),
173            ValType::Ref(ty) => ty.render(self.db).fmt(f),
174        }
175    }
176}
177
178impl<'db> OperandType<'db> {
179    pub(crate) fn render(&self, db: &'db dyn salsa::Database) -> RenderWithDb<'db, &Self> {
180        RenderWithDb { value: self, db }
181    }
182}
183impl Display for RenderWithDb<'_, &OperandType<'_>> {
184    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185        match self.value {
186            OperandType::Val(ty) => write!(f, "{}", ty.render(self.db)),
187            OperandType::Any => write!(f, "any"),
188        }
189    }
190}
191
192impl<'db> FieldType<'db> {
193    pub(crate) fn render(&self, db: &'db dyn salsa::Database) -> RenderWithDb<'db, &Self> {
194        RenderWithDb { value: self, db }
195    }
196}
197impl Display for RenderWithDb<'_, &FieldType<'_>> {
198    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199        if self.value.mutable {
200            write!(f, "(mut ")?;
201        }
202        match &self.value.storage {
203            StorageType::Val(ty) => write!(f, "{}", ty.render(self.db))?,
204            StorageType::PackedI8 => write!(f, "i8")?,
205            StorageType::PackedI16 => write!(f, "i16")?,
206        }
207        if self.value.mutable {
208            write!(f, ")")?;
209        }
210        Ok(())
211    }
212}
213
214impl<'db> Fields<'db> {
215    pub(crate) fn render(&self, db: &'db dyn salsa::Database) -> RenderWithDb<'db, &Self> {
216        RenderWithDb { value: self, db }
217    }
218}
219impl Display for RenderWithDb<'_, &Fields<'_>> {
220    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221        self.value.0.iter().try_fold(true, |first, field| {
222            if !first {
223                write!(f, " ")?;
224            }
225            write!(f, "(field ")?;
226            if let Some(name) = field.1.name {
227                write!(f, "{} ", name.ident(self.db))?;
228            }
229            write!(f, "{}", field.0.render(self.db))?;
230            write!(f, ")")?;
231            Ok(false)
232        })?;
233        Ok(())
234    }
235}