golem_scalajs_wit_bindgen/codegen/
interface.rs1use std::fmt::Display;
16
17use color_eyre::{eyre::eyre, Result};
18use convert_case::{Case, Casing};
19use id_arena::Id;
20use wit_parser::{Interface as WitInterface, TypeDefKind, TypeOwner, UnresolvedPackage};
21
22use super::{Function, Record, Render, Variant};
23use crate::types::TypeMap;
24
25#[derive(Clone)]
27struct InterfaceName(String);
28
29impl Display for InterfaceName {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 write!(f, "{}", self.0)
32 }
33}
34
35impl From<&String> for InterfaceName {
36 fn from(name: &String) -> Self {
37 Self(name.to_case(Case::UpperCamel))
38 }
39}
40
41pub struct Interface {
43 name: InterfaceName,
45
46 records: Vec<Record>,
48
49 variants: Vec<Variant>,
51
52 functions: Vec<Function>,
54}
55
56impl Interface {
57 pub fn from_wit(unresolved_package: &UnresolvedPackage) -> Result<Self> {
59 let (interface_id, interface) = Self::get_interface("api", unresolved_package)?;
60 let type_map = TypeMap::from(unresolved_package);
61 let types = &unresolved_package.types;
62
63 let records: Result<Vec<Record>> = types
64 .iter()
65 .filter(|(_, ty)| match ty.owner {
66 TypeOwner::Interface(id) => id == interface_id,
67 _ => false,
68 })
69 .filter_map(|(_, ty)| match &ty.kind {
70 TypeDefKind::Record(record) => ty
71 .name
72 .as_ref()
73 .map(|name| Record::from_wit(name, record, &type_map)),
74 _ => None,
75 })
76 .collect();
77
78 let variants: Result<Vec<Variant>> = types
79 .iter()
80 .filter(|(_, ty)| match ty.owner {
81 TypeOwner::Interface(id) => id == interface_id,
82 _ => false,
83 })
84 .filter_map(|(_, ty)| match &ty.kind {
85 TypeDefKind::Variant(variant) => ty
86 .name
87 .as_ref()
88 .map(|name| Variant::from_wit(name, variant, &type_map)),
89 _ => None,
90 })
91 .collect();
92
93 let functions: Result<Vec<Function>> = interface
94 .functions
95 .iter()
96 .map(|(_, function)| Function::from_wit(function.clone(), &type_map))
97 .collect();
98
99 Ok(Self {
100 name: InterfaceName::from(interface.name.as_ref().ok_or(eyre!(
101 "Interface with ID {interface_id:?} does not have a name"
102 ))?),
103 records: records?,
104 variants: variants?,
105 functions: functions?,
106 })
107 }
108
109 fn get_interface<'a>(
110 name: &'static str,
111 unresolved_package: &'a UnresolvedPackage,
112 ) -> Result<(Id<WitInterface>, &'a WitInterface)> {
113 unresolved_package
114 .interfaces
115 .iter()
116 .find(|(_, interface)| interface.name.clone().unwrap_or_default() == name)
117 .ok_or(eyre!("Interface {name} not found"))
118 }
119
120 pub fn render(self, package: &str) -> Result<String> {
122 fn render(elements: Vec<impl Render>) -> Result<String> {
123 let elements: Result<Vec<String>> = elements.into_iter().map(Render::render).collect();
124 Ok(elements?.join("\n"))
125 }
126
127 let records = render(self.records)?;
128 let variants = render(self.variants)?;
129 let functions = render(self.functions)?;
130 let name = self.name;
131
132 Ok(format!(
133 "
134 // Generated by golem-scalajs-wit-bindgen
135 package {package}
136
137 import scala.scalajs.js
138 import scala.scalajs.js.JSConverters._
139
140 {records}
141
142 {variants}
143
144 trait {name} {{
145 type WitResult[+Ok, +Err] = Ok
146 object WitResult {{
147 def ok[Ok](value: Ok): WitResult[Ok, Nothing] = value
148
149 def err[Err](value: Err): WitResult[Nothing, Err] = throw js.JavaScriptException(value)
150
151 val unit: WitResult[Unit, Nothing] = ()
152 }}
153
154 type WitOption[+A] = js.UndefOr[A]
155 object WitOption {{
156 def some[A](value: A): WitOption[A] = value
157
158 val none: WitOption[Nothing] = js.undefined
159
160 def fromOption[A](option: Option[A]) =
161 option match {{
162 case Some(value) => value.asInstanceOf[js.UndefOr[A]]
163 case None => js.undefined
164 }}
165 }}
166
167 type WitList[A] = js.Array[A]
168 object WitList {{
169 def fromList[A](list: List[A]): WitList[A] = list.toJSArray
170 }}
171
172 {functions}
173 }}
174 "
175 ))
176 }
177}