golem_scalajs_wit_bindgen/codegen/
variant.rs1use std::fmt::Display;
16
17use color_eyre::Result;
18use convert_case::{Case, Casing};
19use wit_parser::{Case as WitCase, Variant as WitVariant};
20
21use super::Render;
22use crate::types::{ConcreteName, Type, TypeMap, TypeName};
23
24struct CaseName(String);
26
27impl Display for CaseName {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 write!(f, "{}", self.0)
30 }
31}
32
33impl From<String> for CaseName {
34 fn from(name: String) -> Self {
35 Self(name.to_case(Case::Camel))
36 }
37}
38
39struct VariantCase {
41 name: CaseName,
43
44 ty: Option<Type>,
46}
47
48impl VariantCase {
49 pub fn from_wit(case: WitCase, type_map: &TypeMap) -> Result<Self> {
51 Ok(Self {
52 name: CaseName::from(case.name),
53 ty: match case.ty {
54 Some(ty) => Some(Type::from_wit(ty, type_map)?),
55 None => None,
56 },
57 })
58 }
59}
60
61pub struct Variant {
63 name: TypeName,
65
66 cases: Vec<VariantCase>,
68}
69
70impl Variant {
71 pub fn from_wit(name: &str, variant: &WitVariant, type_map: &TypeMap) -> Result<Self> {
73 let cases: Result<Vec<VariantCase>> = variant
74 .cases
75 .iter()
76 .map(|case| VariantCase::from_wit(case.clone(), type_map))
77 .collect();
78
79 Ok(Self {
80 name: TypeName::Concrete(ConcreteName::from(name.to_owned())),
81 cases: cases?,
82 })
83 }
84}
85
86impl Render for Variant {
87 fn render(self) -> Result<String> {
88 let name = self.name;
89
90 let constructors = self
91 .cases
92 .iter()
93 .map(
94 |VariantCase {
95 name: case_name,
96 ty,
97 }| {
98 let (def_or_val, param_list, val, type_tag) = if let Some(ty) = ty {
99 (
100 "def",
101 format!("(value: {ty})"),
102 "override val `val`: js.UndefOr[Type] = value",
103 format!("type Type = {ty}"),
104 )
105 } else {
106 ("val", String::new(), "", "type Type = Nothing".to_owned())
107 };
108
109 format!(
110 "
111 {def_or_val} {case_name}{param_list} = new {name} {{
112 {type_tag}
113
114 val tag: String = \"{case_name}\"
115 {val}
116 }}
117 "
118 )
119 },
120 )
121 .collect::<Vec<_>>()
122 .join("\n");
123
124 Ok(format!(
125 "
126 sealed trait {name} extends js.Object {{ self =>
127 type Type
128
129 val tag: String
130 val `val`: js.UndefOr[Type]
131 }}
132
133 object {name} {{
134 {constructors}
135 }}
136 "
137 ))
138 }
139}