rib/
variable_id.rs

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