rib/
variable_id.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 bincode::{Decode, Encode};
16use serde::{Deserialize, Serialize};
17use std::fmt::Display;
18
19#[derive(
20    Hash, Eq, Debug, Clone, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Encode, Decode,
21)]
22pub enum VariableId {
23    Global(String),
24    Local(String, Option<Id>),
25    MatchIdentifier(MatchIdentifier),
26    ListComprehension(ListComprehensionIdentifier),
27    ListReduce(ListAggregationIdentifier),
28}
29
30impl VariableId {
31    pub fn as_instance_variable(&self) -> VariableId {
32        let variable_string = match self {
33            VariableId::Global(name) => name.clone(),
34            VariableId::Local(name, identifier) => {
35                if let Some(id) = identifier {
36                    format!("{}-{}", name, id.0)
37                } else {
38                    name.clone()
39                }
40            }
41            VariableId::MatchIdentifier(m) => format!("{}-{}", m.name, m.match_arm_index),
42            VariableId::ListComprehension(l) => l.name.clone(),
43            VariableId::ListReduce(r) => r.name.clone(),
44        };
45
46        VariableId::global(format!("__instance_{variable_string}"))
47    }
48    pub fn list_comprehension_identifier(name: impl AsRef<str>) -> VariableId {
49        VariableId::ListComprehension(ListComprehensionIdentifier {
50            name: name.as_ref().to_string(),
51        })
52    }
53
54    pub fn list_reduce_identifier(name: impl AsRef<str>) -> VariableId {
55        VariableId::ListReduce(ListAggregationIdentifier {
56            name: name.as_ref().to_string(),
57        })
58    }
59
60    pub fn match_identifier(name: String, match_arm_index: usize) -> VariableId {
61        VariableId::MatchIdentifier(MatchIdentifier {
62            name,
63            match_arm_index,
64        })
65    }
66
67    pub fn name(&self) -> String {
68        match self {
69            VariableId::Global(name) => name.clone(),
70            VariableId::Local(name, _) => name.clone(),
71            VariableId::MatchIdentifier(m) => m.name.clone(),
72            VariableId::ListComprehension(l) => l.name.clone(),
73            VariableId::ListReduce(r) => r.name.clone(),
74        }
75    }
76
77    pub fn is_global(&self) -> bool {
78        match self {
79            VariableId::Global(_) => true,
80            VariableId::Local(_, _) => false,
81            VariableId::MatchIdentifier(_) => false,
82            VariableId::ListComprehension(_) => false,
83            VariableId::ListReduce(_) => false,
84        }
85    }
86
87    pub fn is_local(&self) -> bool {
88        match self {
89            VariableId::Global(_) => false,
90            VariableId::Local(_, _) => true,
91            VariableId::MatchIdentifier(_) => false,
92            VariableId::ListComprehension(_) => false,
93            VariableId::ListReduce(_) => false,
94        }
95    }
96
97    pub fn is_match_binding(&self) -> bool {
98        match self {
99            VariableId::Global(_) => false,
100            VariableId::Local(_, _) => false,
101            VariableId::MatchIdentifier(_) => true,
102            VariableId::ListComprehension(_) => false,
103            VariableId::ListReduce(_) => false,
104        }
105    }
106
107    // Default variable_id could global, but as soon as type inference
108    // identifies them to be local it gets converted to a local with an id
109    pub fn global(variable_name: String) -> VariableId {
110        VariableId::Global(variable_name)
111    }
112
113    pub fn local(variable_name: &str, id: u32) -> VariableId {
114        VariableId::Local(variable_name.to_string(), Some(Id(id)))
115    }
116
117    // A local variable can be directly formed during parsing itself.
118    // For example: all identifiers in the LHS of a pattern-match-arm
119    // don't have a local definition of the variable, yet they are considered to be local
120    pub fn local_with_no_id(name: &str) -> VariableId {
121        VariableId::Local(name.to_string(), None)
122    }
123
124    pub fn increment_local_variable_id(&mut self) -> VariableId {
125        match self {
126            VariableId::Global(name) => VariableId::Local(name.clone(), Some(Id(0))),
127            VariableId::Local(name, id) => {
128                let new_id = id.clone().map_or(Some(Id(0)), |x| Some(Id(x.0 + 1)));
129                *id = new_id.clone();
130                VariableId::Local(name.to_string(), new_id)
131            }
132            VariableId::MatchIdentifier(m) => VariableId::MatchIdentifier(m.clone()),
133            VariableId::ListComprehension(l) => VariableId::ListComprehension(l.clone()),
134            VariableId::ListReduce(l) => VariableId::ListReduce(l.clone()),
135        }
136    }
137}
138
139#[derive(
140    Hash, Eq, Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, Ord, PartialOrd,
141)]
142pub struct ListComprehensionIdentifier {
143    pub name: String,
144}
145
146#[derive(
147    Hash, Eq, Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, Ord, PartialOrd,
148)]
149pub struct ListAggregationIdentifier {
150    pub name: String,
151}
152
153#[derive(
154    Hash, Eq, Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, Ord, PartialOrd,
155)]
156pub struct MatchIdentifier {
157    pub name: String,
158    pub match_arm_index: usize, // Every match arm across the program is identified by a non-sharing index value. Within a match arm the identifier names cannot be reused
159}
160
161impl MatchIdentifier {
162    pub fn new(name: String, match_arm_index: usize) -> MatchIdentifier {
163        MatchIdentifier {
164            name,
165            match_arm_index,
166        }
167    }
168}
169
170impl Display for VariableId {
171    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172        match self {
173            VariableId::Global(name) => write!(f, "{name}"),
174            VariableId::Local(name, _) => write!(f, "{name}"),
175            VariableId::MatchIdentifier(m) => write!(f, "{}", m.name),
176            VariableId::ListComprehension(l) => write!(f, "{}", l.name),
177            VariableId::ListReduce(r) => write!(f, "{}", r.name),
178        }
179    }
180}
181#[derive(
182    Hash, Eq, Debug, Clone, PartialEq, Serialize, Deserialize, Encode, Decode, Ord, PartialOrd,
183)]
184pub struct Id(pub(crate) u32);
185
186#[cfg(feature = "protobuf")]
187mod protobuf {
188    use crate::{Id, VariableId};
189    use golem_api_grpc::proto::golem::rib::VariableId as ProtoVariableId;
190
191    impl TryFrom<ProtoVariableId> for VariableId {
192        type Error = String;
193
194        fn try_from(value: ProtoVariableId) -> Result<Self, Self::Error> {
195            let variable_id = value.variable_id.ok_or("Missing variable_id".to_string())?;
196
197            match variable_id {
198                golem_api_grpc::proto::golem::rib::variable_id::VariableId::Global(global) => {
199                    Ok(VariableId::Global(global.name))
200                }
201                golem_api_grpc::proto::golem::rib::variable_id::VariableId::Local(local) => Ok(
202                    VariableId::Local(local.name, local.id.map(|x| Id(x as u32))),
203                ),
204                golem_api_grpc::proto::golem::rib::variable_id::VariableId::MatchIdentifier(
205                    match_identifier,
206                ) => Ok(VariableId::MatchIdentifier(crate::MatchIdentifier {
207                    name: match_identifier.name,
208                    match_arm_index: match_identifier.match_arm_index as usize
209                })),
210                golem_api_grpc::proto::golem::rib::variable_id::VariableId::ListComprehensionIdentifier(
211                    list_comprehension,
212                ) => Ok(VariableId::ListComprehension(
213                    crate::ListComprehensionIdentifier {
214                        name: list_comprehension.name,
215                    },
216                )),
217                golem_api_grpc::proto::golem::rib::variable_id::VariableId::ListAggregationIdentifier(
218                    list_aggregation,
219                ) => Ok(VariableId::ListReduce(
220                    crate::ListAggregationIdentifier {
221                        name: list_aggregation.name,
222                    },
223                )),
224            }
225        }
226    }
227
228    impl From<VariableId> for ProtoVariableId {
229        fn from(value: VariableId) -> Self {
230            match value {
231                VariableId::Global(name) => ProtoVariableId {
232                    variable_id: Some(
233                        golem_api_grpc::proto::golem::rib::variable_id::VariableId::Global(
234                            golem_api_grpc::proto::golem::rib::Global { name },
235                        ),
236                    ),
237                },
238                VariableId::MatchIdentifier(m) => ProtoVariableId {
239                    variable_id: Some(
240                        golem_api_grpc::proto::golem::rib::variable_id::VariableId::MatchIdentifier(
241                            golem_api_grpc::proto::golem::rib::MatchIdentifier {
242                                name: m.name,
243                                match_arm_index: m.match_arm_index as u32,
244                            },
245                        ),
246                    ),
247                },
248                VariableId::Local(name, id) => ProtoVariableId {
249                    variable_id: Some(
250                        golem_api_grpc::proto::golem::rib::variable_id::VariableId::Local(
251                            golem_api_grpc::proto::golem::rib::Local {
252                                name,
253                                id: id.map(|x| x.0 as u64),
254                            },
255                        ),
256                    ),
257                },
258                VariableId::ListComprehension(l) => ProtoVariableId {
259                    variable_id: Some(
260                        golem_api_grpc::proto::golem::rib::variable_id::VariableId::ListComprehensionIdentifier(
261                            golem_api_grpc::proto::golem::rib::ListComprehensionIdentifier {
262                                name: l.name,
263                            },
264                        ),
265                    ),
266                },
267                VariableId::ListReduce(r) => ProtoVariableId {
268                    variable_id: Some(
269                        golem_api_grpc::proto::golem::rib::variable_id::VariableId::ListAggregationIdentifier(
270                            golem_api_grpc::proto::golem::rib::ListAggregationIdentifier {
271                                name: r.name,
272                            },
273                        ),
274                    ),
275                },
276            }
277        }
278    }
279}