golem_scalajs_wit_bindgen/codegen/
variant.rs

1// Copyright 2024 Golem Cloud
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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
24/// Represents the name of a variant case in Scala
25struct 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
39/// Represents a variant case in Scala
40struct VariantCase {
41    /// The case name
42    name: CaseName,
43
44    /// The internal case type
45    ty: Option<Type>,
46}
47
48impl VariantCase {
49    /// Constructs a `VariantCase` from WIT
50    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
61/// Represents a variant in Scala
62pub struct Variant {
63    /// The variant name
64    name: TypeName,
65
66    /// The variant cases
67    cases: Vec<VariantCase>,
68}
69
70impl Variant {
71    /// Constructs a `Variant` from WIT
72    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}