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