1use typst::foundations::{Dict, Func, Module, Scope, Type};
2use typst::syntax::FileId;
3
4use super::BoundChecker;
5use crate::{syntax::Decl, ty::prelude::*};
6
7#[derive(Debug, Clone, Copy)]
9pub enum Iface<'a> {
10    Array(&'a Interned<Ty>),
12    Tuple(&'a Interned<Vec<Ty>>),
14    Dict(&'a Interned<RecordTy>),
16    Content {
18        val: &'a typst::foundations::Element,
20        at: &'a Ty,
22    },
23    TypeType {
25        val: &'a typst::foundations::Type,
27        at: &'a Ty,
29    },
30    Type {
32        val: &'a typst::foundations::Type,
34        at: &'a Ty,
36    },
37    Func {
39        val: &'a typst::foundations::Func,
41        at: &'a Ty,
43    },
44    Value {
46        val: &'a Dict,
48        at: &'a Ty,
50    },
51    Module {
53        val: FileId,
55        at: &'a Ty,
57    },
58    ModuleVal {
60        val: &'a Module,
62        at: &'a Ty,
64    },
65}
66
67impl Iface<'_> {
68    pub fn to_type(self) -> Ty {
70        match self {
71            Iface::Array(ty) => Ty::Array(ty.clone()),
72            Iface::Tuple(tys) => Ty::Tuple(tys.clone()),
73            Iface::Dict(dict) => Ty::Dict(dict.clone()),
74            Iface::Content { at, .. }
75            | Iface::TypeType { at, .. }
76            | Iface::Type { at, .. }
77            | Iface::Func { at, .. }
78            | Iface::Value { at, .. }
79            | Iface::Module { at, .. }
80            | Iface::ModuleVal { at, .. } => at.clone(),
81        }
82    }
83
84    pub fn select(self, ctx: &mut impl TyCtxMut, key: &StrRef) -> Option<Ty> {
86        crate::log_debug_ct!("iface shape: {self:?}");
87
88        match self {
89            Iface::Array(..) | Iface::Tuple(..) => {
90                select_scope(Some(Type::of::<typst::foundations::Array>().scope()), key)
91            }
92            Iface::Dict(dict) => dict.field_by_name(key).cloned(),
93            Iface::Content { val, .. } => select_scope(Some(val.scope()), key),
94            Iface::TypeType { val, .. } | Iface::Type { val, .. } => {
96                select_scope(Some(val.scope()), key)
97            }
98            Iface::Func { val, .. } => select_scope(val.scope(), key),
99            Iface::Value { val, at: _ } => ctx.type_of_dict(val).field_by_name(key).cloned(),
100            Iface::Module { val, at: _ } => ctx.check_module_item(val, key),
101            Iface::ModuleVal { val, at: _ } => ctx.type_of_module(val).field_by_name(key).cloned(),
102        }
103    }
104}
105
106fn select_scope(scope: Option<&Scope>, key: &str) -> Option<Ty> {
108    let scope = scope?;
109    let sub = scope.get(key)?;
110    let sub_span = sub.span();
111    Some(Ty::Value(InsTy::new_at(sub.read().clone(), sub_span)))
112}
113
114pub trait IfaceChecker: TyCtx {
116    fn check(&mut self, iface: Iface, ctx: &mut IfaceCheckContext, pol: bool) -> Option<()>;
118}
119
120impl Ty {
121    pub fn iface_surface(
123        &self,
124        pol: bool,
125        checker: &mut impl IfaceChecker,
127    ) {
128        let context = IfaceCheckContext { args: Vec::new() };
129        let mut worker = IfaceCheckDriver {
130            ctx: context,
131            checker,
132        };
133
134        worker.ty(self, pol);
135    }
136}
137
138pub struct IfaceCheckContext {
140    pub args: Vec<Interned<SigTy>>,
142}
143
144#[derive(BindTyCtx)]
146#[bind(checker)]
147pub struct IfaceCheckDriver<'a> {
148    ctx: IfaceCheckContext,
149    checker: &'a mut dyn IfaceChecker,
150}
151
152impl BoundChecker for IfaceCheckDriver<'_> {
153    fn collect(&mut self, ty: &Ty, pol: bool) {
154        self.ty(ty, pol);
155    }
156}
157
158impl IfaceCheckDriver<'_> {
159    fn array_as_iface(&self) -> bool {
161        true
162    }
163
164    fn dict_as_iface(&self) -> bool {
166        true
167    }
168
169    fn value_as_iface(&self) -> bool {
171        true
172    }
173
174    fn ty(&mut self, at: &Ty, pol: bool) {
176        crate::log_debug_ct!("check iface ty: {at:?}");
177
178        match at {
179            Ty::Builtin(BuiltinTy::Stroke) if self.dict_as_iface() => {
180                self.checker
181                    .check(Iface::Dict(&FLOW_STROKE_DICT), &mut self.ctx, pol);
182            }
183            Ty::Builtin(BuiltinTy::Margin) if self.dict_as_iface() => {
184                self.checker
185                    .check(Iface::Dict(&FLOW_MARGIN_DICT), &mut self.ctx, pol);
186            }
187            Ty::Builtin(BuiltinTy::Inset) if self.dict_as_iface() => {
188                self.checker
189                    .check(Iface::Dict(&FLOW_INSET_DICT), &mut self.ctx, pol);
190            }
191            Ty::Builtin(BuiltinTy::Outset) if self.dict_as_iface() => {
192                self.checker
193                    .check(Iface::Dict(&FLOW_OUTSET_DICT), &mut self.ctx, pol);
194            }
195            Ty::Builtin(BuiltinTy::Radius) if self.dict_as_iface() => {
196                self.checker
197                    .check(Iface::Dict(&FLOW_RADIUS_DICT), &mut self.ctx, pol);
198            }
199            Ty::Builtin(BuiltinTy::TextFont) if self.dict_as_iface() => {
200                self.checker
201                    .check(Iface::Dict(&FLOW_TEXT_FONT_DICT), &mut self.ctx, pol);
202            }
203            Ty::Value(ins_ty) => {
204                if self.value_as_iface() {
206                    match &ins_ty.val {
207                        Value::Module(val) => {
208                            self.checker
209                                .check(Iface::ModuleVal { val, at }, &mut self.ctx, pol);
210                        }
211                        Value::Dict(dict) => {
212                            self.checker
213                                .check(Iface::Value { val: dict, at }, &mut self.ctx, pol);
214                        }
215                        Value::Type(ty) => {
216                            self.checker
217                                .check(Iface::TypeType { val: ty, at }, &mut self.ctx, pol);
218                        }
219                        Value::Func(func) => {
220                            self.checker
221                                .check(Iface::Func { val: func, at }, &mut self.ctx, pol);
222                        }
223                        Value::None
224                        | Value::Auto
225                        | Value::Bool(_)
226                        | Value::Int(_)
227                        | Value::Float(_)
228                        | Value::Length(..)
229                        | Value::Angle(..)
230                        | Value::Ratio(..)
231                        | Value::Relative(..)
232                        | Value::Fraction(..)
233                        | Value::Color(..)
234                        | Value::Gradient(..)
235                        | Value::Tiling(..)
236                        | Value::Symbol(..)
237                        | Value::Version(..)
238                        | Value::Str(..)
239                        | Value::Bytes(..)
240                        | Value::Label(..)
241                        | Value::Datetime(..)
242                        | Value::Decimal(..)
243                        | Value::Duration(..)
244                        | Value::Content(..)
245                        | Value::Styles(..)
246                        | Value::Array(..)
247                        | Value::Args(..)
248                        | Value::Dyn(..) => {
249                            self.checker.check(
250                                Iface::Type {
251                                    val: &ins_ty.val.ty(),
252                                    at,
253                                },
254                                &mut self.ctx,
255                                pol,
256                            );
257                        }
258                    }
259                }
260            }
261            Ty::Builtin(BuiltinTy::Content(Some(elem))) if self.value_as_iface() => {
263                self.checker
264                    .check(Iface::Content { val: elem, at }, &mut self.ctx, pol);
265            }
266            Ty::Builtin(BuiltinTy::Content(..)) if self.value_as_iface() => {
267                let ty = Type::of::<typst::foundations::Content>();
268                self.checker
269                    .check(Iface::Type { val: &ty, at }, &mut self.ctx, pol);
270            }
271            Ty::Builtin(BuiltinTy::Type(ty)) if self.value_as_iface() => {
272                self.checker
274                    .check(Iface::Type { val: ty, at }, &mut self.ctx, pol);
275            }
276            Ty::Builtin(BuiltinTy::Element(elem)) if self.value_as_iface() => {
277                self.checker.check(
278                    Iface::Func {
279                        val: &Func::from(*elem),
280                        at,
281                    },
282                    &mut self.ctx,
283                    pol,
284                );
285            }
286            Ty::Builtin(BuiltinTy::Module(module)) => {
287                if let Decl::Module(m) = module.as_ref() {
288                    self.checker
289                        .check(Iface::Module { val: m.fid, at }, &mut self.ctx, pol);
290                }
291            }
292            Ty::Dict(sig) if self.dict_as_iface() => {
302                self.checker.check(Iface::Dict(sig), &mut self.ctx, pol);
304            }
305            Ty::Tuple(sig) if self.array_as_iface() => {
306                self.checker.check(Iface::Tuple(sig), &mut self.ctx, pol);
308            }
309            Ty::Array(sig) if self.array_as_iface() => {
310                self.checker.check(Iface::Array(sig), &mut self.ctx, pol);
312            }
313            Ty::Dict(..) => {
314                self.checker.check(
315                    Iface::Type {
316                        val: &Type::of::<typst::foundations::Dict>(),
317                        at,
318                    },
319                    &mut self.ctx,
320                    pol,
321                );
322            }
323            Ty::Tuple(..) | Ty::Array(..) => {
324                self.checker.check(
325                    Iface::Type {
326                        val: &Type::of::<typst::foundations::Array>(),
327                        at,
328                    },
329                    &mut self.ctx,
330                    pol,
331                );
332            }
333            Ty::Var(..) => at.bounds(pol, self),
334            _ if at.has_bounds() => at.bounds(pol, self),
335            _ => {}
336        }
337        }
343}