Skip to main content

rib/compiler/
type_with_unit.rs

1// Copyright 2024-2025 Golem Cloud
2//
3// Licensed under the Golem Source License v1.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://license.golem.cloud/LICENSE
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 crate::wit_type::{bool, field, record, str, tuple};
16use crate::wit_type::{
17    AnalysedResourceId, AnalysedResourceMode, NameOptionTypePair, NameTypePair, TypeBool, TypeChr,
18    TypeEnum, TypeF32, TypeF64, TypeFlags, TypeHandle, TypeList, TypeOption, TypeRecord,
19    TypeResult, TypeS16, TypeS32, TypeS64, TypeS8, TypeStr, TypeTuple, TypeU16, TypeU32, TypeU64,
20    TypeU8, TypeVariant, WitType,
21};
22use crate::{GetTypeHint, InferredType, InstanceType, TypeInternal};
23use serde::{Deserialize, Serialize};
24
25// An absence of wit type is really `Unit`, however, we avoid
26// Option<WitType> in favor of `WitTypeWithUnit` for clarity.
27// and conversions such as what to print if its `unit` becomes more precise
28#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
29pub enum WitTypeWithUnit {
30    Unit,
31    Type(WitType),
32}
33
34impl WitTypeWithUnit {
35    pub fn unit() -> Self {
36        WitTypeWithUnit::Unit
37    }
38
39    pub fn analysed_type(typ: WitType) -> Self {
40        WitTypeWithUnit::Type(typ)
41    }
42}
43
44impl TryFrom<WitTypeWithUnit> for WitType {
45    type Error = String;
46
47    fn try_from(value: WitTypeWithUnit) -> Result<Self, Self::Error> {
48        match value {
49            WitTypeWithUnit::Unit => Ok(tuple(vec![])),
50            WitTypeWithUnit::Type(typ) => Ok(typ),
51        }
52    }
53}
54
55impl TryFrom<&InferredType> for WitType {
56    type Error = String;
57
58    fn try_from(value: &InferredType) -> Result<Self, Self::Error> {
59        let with_unit = WitTypeWithUnit::try_from(value)?;
60        WitType::try_from(with_unit)
61    }
62}
63
64impl TryFrom<&InferredType> for WitTypeWithUnit {
65    type Error = String;
66
67    fn try_from(inferred_type: &InferredType) -> Result<Self, Self::Error> {
68        match inferred_type.internal_type() {
69            TypeInternal::Instance { instance_type } => match instance_type.as_ref() {
70                InstanceType::Resource {
71                    analysed_resource_id,
72                    analysed_resource_mode,
73                    ..
74                } => {
75                    let analysed_resource_id = AnalysedResourceId(*analysed_resource_id);
76
77                    let analysed_resource_mode = if *analysed_resource_mode == 0 {
78                        AnalysedResourceMode::Owned
79                    } else {
80                        AnalysedResourceMode::Borrowed
81                    };
82
83                    Ok(WitTypeWithUnit::analysed_type(WitType::Handle(
84                        TypeHandle {
85                            resource_id: analysed_resource_id,
86                            mode: analysed_resource_mode,
87                            name: None,
88                            owner: None,
89                        },
90                    )))
91                }
92
93                _ => Ok(WitTypeWithUnit::analysed_type(str())),
94            },
95            TypeInternal::Range { from, to } => {
96                let from: WitType = WitType::try_from(from)?;
97                let to: Option<WitType> = to.as_ref().map(WitType::try_from).transpose()?;
98                let analysed_type = match (from, to) {
99                    (from_type, Some(to_type)) => record(vec![
100                        field("from", from_type),
101                        field("to", to_type),
102                        field("inclusive", bool()),
103                    ]),
104
105                    (from_type, None) => {
106                        record(vec![field("from", from_type), field("inclusive", bool())])
107                    }
108                };
109                Ok(WitTypeWithUnit::analysed_type(analysed_type))
110            }
111            TypeInternal::Bool => Ok(WitTypeWithUnit::analysed_type(WitType::Bool(TypeBool))),
112            TypeInternal::S8 => Ok(WitTypeWithUnit::analysed_type(WitType::S8(TypeS8))),
113            TypeInternal::U8 => Ok(WitTypeWithUnit::analysed_type(WitType::U8(TypeU8))),
114            TypeInternal::S16 => Ok(WitTypeWithUnit::analysed_type(WitType::S16(TypeS16))),
115            TypeInternal::U16 => Ok(WitTypeWithUnit::analysed_type(WitType::U16(TypeU16))),
116            TypeInternal::S32 => Ok(WitTypeWithUnit::analysed_type(WitType::S32(TypeS32))),
117            TypeInternal::U32 => Ok(WitTypeWithUnit::analysed_type(WitType::U32(TypeU32))),
118            TypeInternal::S64 => Ok(WitTypeWithUnit::analysed_type(WitType::S64(TypeS64))),
119            TypeInternal::U64 => Ok(WitTypeWithUnit::analysed_type(WitType::U64(TypeU64))),
120            TypeInternal::F32 => Ok(WitTypeWithUnit::analysed_type(WitType::F32(TypeF32))),
121            TypeInternal::F64 => Ok(WitTypeWithUnit::analysed_type(WitType::F64(TypeF64))),
122            TypeInternal::Chr => Ok(WitTypeWithUnit::analysed_type(WitType::Chr(TypeChr))),
123            TypeInternal::Str => Ok(WitTypeWithUnit::analysed_type(WitType::Str(TypeStr))),
124            TypeInternal::List(inferred_type) => {
125                Ok(WitTypeWithUnit::analysed_type(WitType::List(TypeList {
126                    inner: Box::new(inferred_type.try_into()?),
127                    name: None,
128                    owner: None,
129                })))
130            }
131            TypeInternal::Tuple(tuple) => {
132                Ok(WitTypeWithUnit::analysed_type(WitType::Tuple(TypeTuple {
133                    items: tuple
134                        .iter()
135                        .map(|t| t.try_into())
136                        .collect::<Result<Vec<WitType>, String>>()?,
137                    name: None,
138                    owner: None,
139                })))
140            }
141            TypeInternal::Record(record) => Ok(WitTypeWithUnit::analysed_type(WitType::Record(
142                TypeRecord {
143                    fields: record
144                        .iter()
145                        .map(|(name, typ)| {
146                            Ok(NameTypePair {
147                                name: name.to_string(),
148                                typ: typ.try_into()?,
149                            })
150                        })
151                        .collect::<Result<Vec<NameTypePair>, String>>()?,
152                    name: None,
153                    owner: None,
154                },
155            ))),
156            TypeInternal::Flags(flags) => {
157                Ok(WitTypeWithUnit::analysed_type(WitType::Flags(TypeFlags {
158                    names: flags.clone(),
159                    name: None,
160                    owner: None,
161                })))
162            }
163            TypeInternal::Enum(enums) => {
164                Ok(WitTypeWithUnit::analysed_type(WitType::Enum(TypeEnum {
165                    cases: enums.clone(),
166                    name: None,
167                    owner: None,
168                })))
169            }
170            TypeInternal::Option(option) => Ok(WitTypeWithUnit::analysed_type(WitType::Option(
171                TypeOption {
172                    inner: Box::new(option.try_into()?),
173                    name: None,
174                    owner: None,
175                },
176            ))),
177            TypeInternal::Result { ok, error } => Ok(WitTypeWithUnit::analysed_type(
178                // In the case of result, there are instances users give just 1 value with zero function calls, we need to be flexible here
179                WitType::Result(TypeResult {
180                    ok: ok.as_ref().and_then(|t| t.try_into().ok().map(Box::new)),
181                    err: error.as_ref().and_then(|t| t.try_into().ok().map(Box::new)),
182                    name: None,
183                    owner: None,
184                }),
185            )),
186            TypeInternal::Variant(variant) => Ok(WitTypeWithUnit::analysed_type(WitType::Variant(
187                TypeVariant {
188                    cases: variant
189                        .iter()
190                        .map(|(name, typ)| {
191                            Ok(NameOptionTypePair {
192                                name: name.clone(),
193                                typ: typ.as_ref().map(|t| t.try_into()).transpose()?,
194                            })
195                        })
196                        .collect::<Result<Vec<NameOptionTypePair>, String>>()?,
197                    name: None,
198                    owner: None,
199                },
200            ))),
201            TypeInternal::Resource {
202                resource_id,
203                resource_mode,
204                name: _,
205                owner: _,
206            } => Ok(WitTypeWithUnit::analysed_type(WitType::Handle(
207                TypeHandle {
208                    resource_id: AnalysedResourceId(*resource_id),
209                    mode: if resource_mode == &0 {
210                        AnalysedResourceMode::Owned
211                    } else {
212                        AnalysedResourceMode::Borrowed
213                    },
214                    name: None,
215                    owner: None,
216                },
217            ))),
218
219            TypeInternal::AllOf(types) => Err(format!(
220                "ambiguous types {}",
221                types
222                    .iter()
223                    .map(|x| x.get_type_hint().to_string())
224                    .collect::<Vec<_>>()
225                    .join(", ")
226            )),
227            TypeInternal::Unknown => Err("failed to infer type".to_string()),
228            // We don't expect to have a sequence type in the inferred type.as
229            // This implies Rib will not support multiple types from worker-function results
230            TypeInternal::Sequence(vec) => {
231                if vec.is_empty() {
232                    Ok(WitTypeWithUnit::unit())
233                } else if vec.len() == 1 {
234                    let first = &vec[0];
235                    Ok(first.try_into()?)
236                } else {
237                    Err("function with multiple return types not supported".to_string())
238                }
239            }
240        }
241    }
242}