rexlang_engine/libraries/library.rs
1use futures::FutureExt;
2use rexlang_ast::expr::{Decl, NameRef, TypeDecl, TypeExpr, TypeVariant, intern, sym};
3use rexlang_lexer::span::Span;
4use rexlang_typesystem::{
5 AdtDecl, BuiltinTypeId, Predicate, Scheme, Type, TypeKind, TypeVar, collect_adts_in_types,
6};
7use rexlang_util::GasMeter;
8
9use crate::EvaluatorRef;
10use crate::engine::{AsyncHandler, Export, Handler, NativeFuture, apply, order_adt_family};
11use crate::evaluator::EvaluatorRef as RuntimeEvaluatorRef;
12use crate::{
13 CancellationToken, Engine, EngineError, FromPointer, IntoPointer, Pointer, ROOT_LIBRARY_NAME,
14 RexType, Value,
15};
16
17/// A staged host library that you build up in Rust and later inject into an [`Engine`].
18///
19/// `Library` is the host-side representation of a Rex module. It lets embedders collect:
20///
21/// - Rex declarations such as `pub type ...`
22/// - typed Rust handlers via [`Library::export`] / [`Library::export_async`]
23/// - pointer-level native handlers via [`Library::export_native`] /
24/// [`Library::export_native_async`]
25///
26/// Once the library is assembled, pass it to [`Engine::inject_library`] to make it importable
27/// from Rex code.
28///
29/// This type is intentionally mutable and staged: you can build it incrementally, inspect its
30/// raw and structured declarations plus [`Library::exports`], transform them, and only inject it
31/// once you are satisfied with the final module shape.
32///
33/// # Examples
34///
35/// ```rust,ignore
36/// use rexlang_engine::{Engine, Library};
37///
38/// let mut engine = Engine::with_prelude(()).unwrap();
39/// engine.add_default_resolvers();
40///
41/// let mut math = Library::new("acme.math");
42/// math.export("inc", |_state: &(), x: i32| Ok(x + 1)).unwrap();
43/// math.add_raw_declaration("pub type Sign = Positive | Negative").unwrap();
44///
45/// engine.inject_library(math).unwrap();
46/// ```
47pub struct Library<State: Clone + Send + Sync + 'static> {
48 /// The module name Rex code will import.
49 ///
50 /// This should be the fully-qualified library path you want users to write in `import`
51 /// declarations, such as `acme.math` or `sample`.
52 ///
53 /// [`Engine::inject_library`] validates and reserves this name when the library is injected.
54 ///
55 /// # Examples
56 ///
57 /// ```rust,ignore
58 /// use rexlang_engine::Library;
59 ///
60 /// let library = Library::<()>::new("acme.math");
61 /// assert_eq!(library.name, "acme.math");
62 /// ```
63 pub name: String,
64
65 /// Raw Rex declarations supplied directly by the embedder.
66 ///
67 /// This is most commonly used for `pub type ...` declarations, but it can hold any raw Rex
68 /// declaration text you want included in the virtual module source.
69 ///
70 /// The usual way to append to this field is [`Library::add_raw_declaration`], which validates that
71 /// the added text is non-empty. The field itself is public so callers can inspect or construct
72 /// a library in multiple passes.
73 ///
74 /// # Examples
75 ///
76 /// ```rust,ignore
77 /// use rexlang_engine::Library;
78 ///
79 /// let mut library = Library::<()>::new("acme.status");
80 /// library
81 /// .add_raw_declaration("pub type Status = Ready | Failed string")
82 /// .unwrap();
83 ///
84 /// assert_eq!(library.raw_declarations.len(), 1);
85 /// ```
86 pub raw_declarations: Vec<String>,
87
88 /// Structured declarations registered from Rust metadata rather than Rex source.
89 ///
90 /// APIs such as [`Library::add_adt_decl`] and [`Library::add_rex_adt`] append here instead
91 /// of synthesizing Rex source text.
92 pub structured_decls: Vec<Decl>,
93
94 pub(crate) staged_adts: Vec<AdtDecl>,
95
96 /// Staged host exports that will become callable Rex values when the library is injected.
97 ///
98 /// Each [`Export`] bundles a public Rex name, a declaration that is inserted into the virtual
99 /// module source, and the runtime injector that registers the implementation with the engine.
100 ///
101 /// Most callers populate this with [`Library::export`], [`Library::export_async`],
102 /// [`Library::export_native`], [`Library::export_native_async`], or [`Library::add_export`].
103 /// The field is public so advanced embedders can construct exports separately and assemble the
104 /// final library programmatically.
105 ///
106 /// # Examples
107 ///
108 /// ```rust,ignore
109 /// use rexlang_engine::{Export, Library};
110 ///
111 /// let mut library = Library::<()>::new("acme.math");
112 /// let export = Export::from_handler("inc", |_state: &(), x: i32| Ok(x + 1)).unwrap();
113 /// library.exports.push(export);
114 ///
115 /// assert_eq!(library.exports.len(), 1);
116 /// ```
117 pub exports: Vec<Export<State>>,
118}
119
120impl<State> Library<State>
121where
122 State: Clone + Send + Sync + 'static,
123{
124 fn tracing_log_scheme() -> Scheme {
125 let a_tv = TypeVar::new(0, Some(sym("a")));
126 let a = Type::var(a_tv.clone());
127 Scheme::new(
128 vec![a_tv],
129 vec![Predicate::new("Show", a.clone())],
130 Type::fun(a, Type::builtin(BuiltinTypeId::String)),
131 )
132 }
133
134 /// Create an empty staged library that targets the engine root namespace.
135 ///
136 /// Injecting a global library installs its declarations and exports directly
137 /// into the engine rather than making them importable as a named module.
138 pub fn global() -> Self {
139 Self::new(ROOT_LIBRARY_NAME)
140 }
141
142 /// Create an empty staged library with the given import name.
143 ///
144 /// The returned library contains no declarations and no exports yet. Add those with the
145 /// helper methods on `Library`, then pass it to [`Engine::inject_library`].
146 ///
147 /// # Examples
148 ///
149 /// ```rust,ignore
150 /// use rexlang_engine::Library;
151 ///
152 /// let library = Library::<()>::new("acme.math");
153 /// assert_eq!(library.name, "acme.math");
154 /// assert!(library.raw_declarations.is_empty());
155 /// assert!(library.exports.is_empty());
156 /// ```
157 pub fn new(name: impl Into<String>) -> Self {
158 Self {
159 name: name.into(),
160 raw_declarations: Vec::new(),
161 structured_decls: Vec::new(),
162 staged_adts: Vec::new(),
163 exports: Vec::new(),
164 }
165 }
166
167 /// Append a raw Rex declaration to this staged library.
168 ///
169 /// Use this when you already have declaration text in Rex syntax, for example `pub type ...`.
170 /// The text is stored exactly as provided and later concatenated into the virtual module source
171 /// that [`Engine::inject_library`] exposes to Rex imports.
172 ///
173 /// This rejects empty or whitespace-only strings.
174 ///
175 /// # Examples
176 ///
177 /// ```rust,ignore
178 /// use rexlang_engine::Library;
179 ///
180 /// let mut library = Library::<()>::new("acme.status");
181 /// library
182 /// .add_raw_declaration("pub type Status = Ready | Failed string")
183 /// .unwrap();
184 /// ```
185 pub fn add_raw_declaration(
186 &mut self,
187 declaration: impl Into<String>,
188 ) -> Result<(), EngineError> {
189 let declaration = declaration.into();
190 if declaration.trim().is_empty() {
191 return Err(EngineError::Internal(
192 "library declaration cannot be empty".into(),
193 ));
194 }
195 self.raw_declarations.push(declaration);
196 Ok(())
197 }
198
199 /// Append a structured Rex declaration to this staged library.
200 pub fn add_decl(&mut self, decl: Decl) {
201 self.structured_decls.push(decl);
202 }
203
204 /// Append multiple structured Rex declarations to this staged library.
205 pub fn add_decls(&mut self, decls: impl IntoIterator<Item = Decl>) {
206 self.structured_decls.extend(decls);
207 }
208
209 /// Convert an [`AdtDecl`] into a structured type declaration and append it to this library.
210 ///
211 /// This is a structured alternative to [`Library::add_raw_declaration`] when you already have
212 /// an ADT declaration in typed form.
213 ///
214 /// # Examples
215 ///
216 /// ```rust,ignore
217 /// use rexlang_engine::{Engine, Library};
218 ///
219 /// let mut engine = Engine::with_prelude(()).unwrap();
220 /// let mut library = Library::new("acme.types");
221 /// let adt = engine.adt_decl_from_type(&rexlang_typesystem::Type::user_con("Thing", 0)).unwrap();
222 ///
223 /// library.add_adt_decl(adt).unwrap();
224 /// ```
225 pub fn add_adt_decl(&mut self, adt: AdtDecl) -> Result<(), EngineError> {
226 self.add_adt_family(vec![adt])
227 }
228
229 /// Append an acyclic family of ADT declarations to this staged library.
230 ///
231 /// Families are ordered before insertion so declarations are staged in
232 /// dependency order, and cycles are rejected.
233 pub fn add_adt_family(&mut self, adts: Vec<AdtDecl>) -> Result<(), EngineError> {
234 for adt in order_adt_family(adts)? {
235 let candidate = type_decl_from_adt(&adt);
236 let already_staged = self.structured_decls.iter().find_map(|decl| match decl {
237 Decl::Type(type_decl) if type_decl.name == adt.name => Some(type_decl),
238 _ => None,
239 });
240 if let Some(existing_decl) = already_staged {
241 if existing_decl != &candidate {
242 return Err(EngineError::Custom(format!(
243 "conflicting staged ADT registration for `{}`: existing declaration differs from new ADT declaration",
244 adt.name,
245 )));
246 }
247 continue;
248 }
249 self.staged_adts.push(adt);
250 self.structured_decls.push(Decl::Type(candidate));
251 }
252 Ok(())
253 }
254
255 /// Discover user ADTs referenced by the supplied types and append their declarations.
256 ///
257 /// This is useful when you have Rust-side type information and want to register the
258 /// corresponding user-defined ADTs for every type it mentions.
259 ///
260 /// The discovery process:
261 ///
262 /// - walks the provided types recursively
263 /// - deduplicates repeated ADTs
264 /// - asks the engine to materialize each discovered ADT declaration
265 /// - appends the resulting structured declarations to this library
266 ///
267 /// If conflicting ADT definitions are found for the same type constructor name, this returns
268 /// an [`EngineError`] that describes the conflict instead of silently picking one.
269 ///
270 /// # Examples
271 ///
272 /// ```rust,ignore
273 /// use rex_engine::{Engine, Library};
274 /// use rexlang_typesystem::{BuiltinTypeId, Type};
275 ///
276 /// let mut engine = Engine::with_prelude(()).unwrap();
277 /// let mut library = Library::new("acme.types");
278 /// let types = vec![
279 /// Type::app(Type::user_con("Foo", 1), Type::builtin(BuiltinTypeId::I32)),
280 /// Type::user_con("Bar", 0),
281 /// ];
282 ///
283 /// library.add_adt_decls_from_types(&mut engine, types).unwrap();
284 /// ```
285 pub fn add_adt_decls_from_types(
286 &mut self,
287 engine: &mut Engine<State>,
288 types: Vec<Type>,
289 ) -> Result<(), EngineError> {
290 let adts = collect_adts_in_types(types).map_err(crate::collect_adts_error_to_engine)?;
291 for typ in adts {
292 let adt = engine.adt_decl_from_type(&typ)?;
293 self.add_adt_decl(adt)?;
294 }
295 Ok(())
296 }
297
298 /// Derive a Rex ADT declaration from a Rust type and append it to this library.
299 ///
300 /// This is the most ergonomic way to expose a Rust enum or struct that implements [`RexType`]
301 /// as a library-local structured Rex type declaration.
302 ///
303 /// Unlike older engine-level registration helpers, this stages the declaration
304 /// inside the library so the caller can choose whether to inject it globally or
305 /// as a named module.
306 ///
307 /// # Examples
308 ///
309 /// ```rust,ignore
310 /// use rexlang_engine::{Engine, Library};
311 ///
312 /// #[derive(rexlang::Rex)]
313 /// struct Label {
314 /// text: String,
315 /// }
316 ///
317 /// let mut engine = Engine::with_prelude(()).unwrap();
318 /// let mut library = Library::new("sample");
319 /// library.add_rex_adt::<Label>().unwrap();
320 /// ```
321 pub fn add_rex_adt<T>(&mut self) -> Result<(), EngineError>
322 where
323 T: RexType,
324 {
325 let mut family = Vec::new();
326 T::collect_rex_family(&mut family)?;
327 self.add_adt_family(family)
328 }
329
330 /// Append a preconstructed [`Export`] to this library.
331 ///
332 /// This is useful when exports are assembled elsewhere, such as from plugin metadata or a
333 /// higher-level registration layer.
334 ///
335 /// # Examples
336 ///
337 /// ```rust,ignore
338 /// use rexlang_engine::{Export, Library};
339 ///
340 /// let mut library = Library::<()>::new("acme.math");
341 /// let export = Export::from_handler("inc", |_state: &(), x: i32| Ok(x + 1)).unwrap();
342 /// library.add_export(export);
343 /// ```
344 pub fn add_export(&mut self, export: Export<State>) {
345 self.exports.push(export);
346 }
347
348 /// Stage a typed synchronous Rust handler as a library export.
349 ///
350 /// This is the most convenient API for exporting ordinary Rust functions or closures into a
351 /// library. The handler's argument and return types drive the Rex signature automatically.
352 ///
353 /// The staged export becomes available to Rex code after [`Engine::inject_library`] is called.
354 ///
355 /// # Examples
356 ///
357 /// ```rust,ignore
358 /// use rexlang_engine::Library;
359 ///
360 /// let mut library = Library::<()>::new("acme.math");
361 /// library.export("inc", |_state: &(), x: i32| Ok(x + 1)).unwrap();
362 /// ```
363 pub fn export<Sig, H>(&mut self, name: impl Into<String>, handler: H) -> Result<(), EngineError>
364 where
365 H: Handler<State, Sig>,
366 {
367 self.exports.push(Export::from_handler(name, handler)?);
368 Ok(())
369 }
370
371 /// Stage a typed asynchronous Rust handler as a library export.
372 ///
373 /// Use this when the host implementation is naturally async, for example when it awaits I/O or
374 /// other long-running work.
375 ///
376 /// # Examples
377 ///
378 /// ```rust,ignore
379 /// use rexlang_engine::Library;
380 ///
381 /// let mut library = Library::<()>::new("acme.math");
382 /// library
383 /// .export_async("double_async", |_state: &(), x: i32| async move { Ok(x * 2) })
384 /// .unwrap();
385 /// ```
386 pub fn export_async<Sig, H>(
387 &mut self,
388 name: impl Into<String>,
389 handler: H,
390 ) -> Result<(), EngineError>
391 where
392 H: AsyncHandler<State, Sig>,
393 {
394 self.exports
395 .push(Export::from_async_handler(name, handler)?);
396 Ok(())
397 }
398
399 /// Stage a tracing-backed log export with type `a -> str where Show a`.
400 pub fn export_tracing_log_function(
401 &mut self,
402 name: impl Into<String>,
403 log: fn(&str),
404 ) -> Result<(), EngineError> {
405 let name = name.into();
406 let name_sym = sym(&name);
407 let scheme = Self::tracing_log_scheme();
408 self.exports.push(Export::from_native_async(
409 name,
410 scheme,
411 1,
412 move |engine, call_type, args| {
413 let name_sym = name_sym.clone();
414 async move {
415 if args.len() != 1 {
416 return Err(EngineError::NativeArity {
417 name: name_sym.clone(),
418 expected: 1,
419 got: args.len(),
420 });
421 }
422
423 let (arg_ty, _ret_ty) = match call_type.as_ref() {
424 TypeKind::Fun(arg, ret) => (arg.clone(), ret.clone()),
425 _ => return Err(EngineError::NotCallable(call_type.to_string())),
426 };
427 let show_ty = Type::fun(arg_ty.clone(), Type::builtin(BuiltinTypeId::String));
428 let mut gas = GasMeter::default();
429 let show_ptr = RuntimeEvaluatorRef::new(&engine)
430 .resolve_class_method(&sym("show"), &show_ty, &mut gas)
431 .await?;
432 let rendered_ptr = apply(
433 &engine,
434 show_ptr,
435 args[0],
436 Some(&show_ty),
437 Some(&arg_ty),
438 &mut gas,
439 )
440 .await?;
441 let rendered = String::from_pointer(&engine.heap, &rendered_ptr)?;
442 log(&rendered);
443 engine.heap.alloc_string(rendered)
444 }
445 .boxed()
446 },
447 )?);
448 Ok(())
449 }
450
451 /// Stage the standard `debug`/`info`/`warn`/`error` tracing exports.
452 pub fn export_tracing_log_functions(&mut self) -> Result<(), EngineError> {
453 self.export_tracing_log_function("debug", |s| tracing::debug!("{s}"))?;
454 self.export_tracing_log_function("info", |s| tracing::info!("{s}"))?;
455 self.export_tracing_log_function("warn", |s| tracing::warn!("{s}"))?;
456 self.export_tracing_log_function("error", |s| tracing::error!("{s}"))?;
457 Ok(())
458 }
459
460 /// Stage a pointer-level synchronous native export with an explicit Rex type scheme.
461 ///
462 /// This lower-level API is intended for dynamic or runtime-defined integrations where the
463 /// handler needs access to the engine heap or where the Rex type cannot be inferred from an
464 /// ordinary Rust function signature alone.
465 ///
466 /// `scheme` describes the Rex-visible type, and `arity` must match the number of arguments the
467 /// handler expects.
468 ///
469 /// # Examples
470 ///
471 /// ```rust,ignore
472 /// use rexlang_engine::{EvaluatorRef, Library, Pointer};
473 /// use rexlang_typesystem::{BuiltinTypeId, Scheme, Type};
474 ///
475 /// let mut library = Library::<()>::new("acme.dynamic");
476 /// let scheme = Scheme::new(
477 /// vec![],
478 /// vec![],
479 /// Type::fun(Type::builtin(BuiltinTypeId::I32), Type::builtin(BuiltinTypeId::I32)),
480 /// );
481 ///
482 /// library
483 /// .export_native("id_ptr", scheme, 1, |_engine: EvaluatorRef<'_, ()>, _typ: &Type, args: &[Pointer]| {
484 /// Ok(args[0].clone())
485 /// })
486 /// .unwrap();
487 /// ```
488 pub fn export_native<F>(
489 &mut self,
490 name: impl Into<String>,
491 scheme: rexlang_typesystem::Scheme,
492 arity: usize,
493 handler: F,
494 ) -> Result<(), EngineError>
495 where
496 F: for<'a> Fn(
497 EvaluatorRef<'a, State>,
498 &'a Type,
499 &'a [Pointer],
500 ) -> Result<Pointer, EngineError>
501 + Send
502 + Sync
503 + 'static,
504 {
505 self.exports
506 .push(Export::from_native(name, scheme, arity, handler)?);
507 Ok(())
508 }
509
510 pub fn export_native_with_gas_cost<F>(
511 &mut self,
512 name: impl Into<String>,
513 scheme: rexlang_typesystem::Scheme,
514 arity: usize,
515 gas_cost: u64,
516 handler: F,
517 ) -> Result<(), EngineError>
518 where
519 F: for<'a> Fn(
520 EvaluatorRef<'a, State>,
521 &'a Type,
522 &'a [Pointer],
523 ) -> Result<Pointer, EngineError>
524 + Send
525 + Sync
526 + 'static,
527 {
528 self.exports.push(Export::from_native_with_gas_cost(
529 name, scheme, arity, gas_cost, handler,
530 )?);
531 Ok(())
532 }
533
534 /// Stage a pointer-level asynchronous native export with an explicit Rex type scheme.
535 ///
536 /// This is the async counterpart to [`Library::export_native`]. Use it when the export needs
537 /// both direct engine access and asynchronous execution.
538 ///
539 /// # Examples
540 ///
541 /// ```rust,ignore
542 /// use futures::FutureExt;
543 /// use rexlang_engine::{EvaluatorRef, Library, Pointer};
544 /// use rexlang_typesystem::{BuiltinTypeId, Scheme, Type};
545 ///
546 /// let mut library = Library::<()>::new("acme.dynamic");
547 /// let scheme = Scheme::new(vec![], vec![], Type::builtin(BuiltinTypeId::I32));
548 ///
549 /// library
550 /// .export_native_async(
551 /// "answer_async",
552 /// scheme,
553 /// 0,
554 /// |engine: EvaluatorRef<'_, ()>, _typ: Type, _args: Vec<Pointer>| {
555 /// async move { engine.heap.alloc_i32(42) }.boxed()
556 /// },
557 /// )
558 /// .unwrap();
559 /// ```
560 pub fn export_native_async<F>(
561 &mut self,
562 name: impl Into<String>,
563 scheme: rexlang_typesystem::Scheme,
564 arity: usize,
565 handler: F,
566 ) -> Result<(), EngineError>
567 where
568 F: for<'a> Fn(EvaluatorRef<'a, State>, Type, Vec<Pointer>) -> NativeFuture<'a>
569 + Send
570 + Sync
571 + 'static,
572 {
573 self.exports
574 .push(Export::from_native_async(name, scheme, arity, handler)?);
575 Ok(())
576 }
577
578 pub fn export_native_async_with_gas_cost<F>(
579 &mut self,
580 name: impl Into<String>,
581 scheme: rexlang_typesystem::Scheme,
582 arity: usize,
583 gas_cost: u64,
584 handler: F,
585 ) -> Result<(), EngineError>
586 where
587 F: for<'a> Fn(EvaluatorRef<'a, State>, Type, Vec<Pointer>) -> NativeFuture<'a>
588 + Send
589 + Sync
590 + 'static,
591 {
592 self.exports.push(Export::from_native_async_with_gas_cost(
593 name, scheme, arity, gas_cost, handler,
594 )?);
595 Ok(())
596 }
597
598 pub fn export_native_async_cancellable<F>(
599 &mut self,
600 name: impl Into<String>,
601 scheme: rexlang_typesystem::Scheme,
602 arity: usize,
603 handler: F,
604 ) -> Result<(), EngineError>
605 where
606 F: for<'a> Fn(
607 EvaluatorRef<'a, State>,
608 CancellationToken,
609 Type,
610 &'a [Pointer],
611 ) -> NativeFuture<'a>
612 + Send
613 + Sync
614 + 'static,
615 {
616 self.export_native_async_cancellable_with_gas_cost(name, scheme, arity, 0, handler)
617 }
618
619 pub fn export_native_async_cancellable_with_gas_cost<F>(
620 &mut self,
621 name: impl Into<String>,
622 scheme: rexlang_typesystem::Scheme,
623 arity: usize,
624 gas_cost: u64,
625 handler: F,
626 ) -> Result<(), EngineError>
627 where
628 F: for<'a> Fn(
629 EvaluatorRef<'a, State>,
630 CancellationToken,
631 Type,
632 &'a [Pointer],
633 ) -> NativeFuture<'a>
634 + Send
635 + Sync
636 + 'static,
637 {
638 self.exports
639 .push(Export::from_native_async_cancellable_with_gas_cost(
640 name, scheme, arity, gas_cost, handler,
641 )?);
642 Ok(())
643 }
644
645 pub fn export_value<V>(&mut self, name: impl Into<String>, value: V) -> Result<(), EngineError>
646 where
647 V: IntoPointer + RexType + Clone + Send + Sync + 'static,
648 {
649 self.exports.push(Export::from_value(name, value)?);
650 Ok(())
651 }
652
653 pub fn export_value_typed(
654 &mut self,
655 name: impl Into<String>,
656 typ: Type,
657 value: Value,
658 ) -> Result<(), EngineError> {
659 self.exports
660 .push(Export::from_value_typed(name, typ, value)?);
661 Ok(())
662 }
663}
664
665fn type_decl_from_adt(adt: &AdtDecl) -> TypeDecl {
666 TypeDecl {
667 span: Span::default(),
668 is_pub: true,
669 name: adt.name.clone(),
670 params: adt.params.iter().map(|p| p.name.clone()).collect(),
671 variants: adt
672 .variants
673 .iter()
674 .map(|variant| TypeVariant {
675 name: variant.name.clone(),
676 args: variant.args.iter().map(type_expr_from_type).collect(),
677 })
678 .collect(),
679 }
680}
681
682fn type_expr_from_type(typ: &Type) -> TypeExpr {
683 match typ.as_ref() {
684 TypeKind::Var(tv) => {
685 let name = tv
686 .name
687 .clone()
688 .unwrap_or_else(|| intern(&format!("t{}", tv.id)));
689 TypeExpr::Name(Span::default(), NameRef::Unqualified(name))
690 }
691 TypeKind::Con(con) => {
692 TypeExpr::Name(Span::default(), NameRef::Unqualified(con.name.clone()))
693 }
694 TypeKind::App(fun, arg) => {
695 if let TypeKind::App(head, err) = fun.as_ref()
696 && let TypeKind::Con(con) = head.as_ref()
697 && con.builtin_id == Some(rexlang_typesystem::BuiltinTypeId::Result)
698 && con.arity == 2
699 {
700 let result =
701 TypeExpr::Name(Span::default(), NameRef::Unqualified(con.name.clone()));
702 let ok_expr = type_expr_from_type(arg);
703 let err_expr = type_expr_from_type(err);
704 let app1 = TypeExpr::App(Span::default(), Box::new(result), Box::new(ok_expr));
705 return TypeExpr::App(Span::default(), Box::new(app1), Box::new(err_expr));
706 }
707 TypeExpr::App(
708 Span::default(),
709 Box::new(type_expr_from_type(fun)),
710 Box::new(type_expr_from_type(arg)),
711 )
712 }
713 TypeKind::Fun(arg, ret) => TypeExpr::Fun(
714 Span::default(),
715 Box::new(type_expr_from_type(arg)),
716 Box::new(type_expr_from_type(ret)),
717 ),
718 TypeKind::Tuple(elems) => TypeExpr::Tuple(
719 Span::default(),
720 elems.iter().map(type_expr_from_type).collect(),
721 ),
722 TypeKind::Record(fields) => TypeExpr::Record(
723 Span::default(),
724 fields
725 .iter()
726 .map(|(name, ty)| (name.clone(), type_expr_from_type(ty)))
727 .collect(),
728 ),
729 }
730}