/ synthesizer / program / src / logic / command / get_or_use.rs
get_or_use.rs
  1  // Copyright (c) 2025 ADnet Contributors
  2  // This file is part of the AlphaVM library.
  3  
  4  // Licensed under the Apache License, Version 2.0 (the "License");
  5  // you may not use this file except in compliance with the License.
  6  // You may obtain a copy of the License at:
  7  
  8  // http://www.apache.org/licenses/LICENSE-2.0
  9  
 10  // Unless required by applicable law or agreed to in writing, software
 11  // distributed under the License is distributed on an "AS IS" BASIS,
 12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  // See the License for the specific language governing permissions and
 14  // limitations under the License.
 15  
 16  use crate::{CallOperator, FinalizeStoreTrait, Opcode, Operand, RegistersTrait, StackTrait};
 17  use console::{
 18      network::prelude::*,
 19      program::{Register, Value},
 20  };
 21  
 22  /// A get command that uses the provided default in case of failure, e.g. `get.or_use accounts[r0] r1 into r2;`.
 23  /// Gets the value stored at `operand` in `mapping` and stores the result in `destination`.
 24  /// If the key is not present, `default` is stored in `destination`.
 25  #[derive(Clone, PartialEq, Eq, Hash)]
 26  pub struct GetOrUse<N: Network> {
 27      /// The mapping.
 28      mapping: CallOperator<N>,
 29      /// The operands.
 30      operands: [Operand<N>; 2],
 31      /// The destination register.
 32      destination: Register<N>,
 33  }
 34  
 35  impl<N: Network> GetOrUse<N> {
 36      /// Returns the opcode.
 37      #[inline]
 38      pub const fn opcode() -> Opcode {
 39          Opcode::Command("get.or_use")
 40      }
 41  
 42      /// Returns the operands in the operation.
 43      #[inline]
 44      pub fn operands(&self) -> &[Operand<N>] {
 45          &self.operands
 46      }
 47  
 48      /// Returns the mapping.
 49      #[inline]
 50      pub const fn mapping(&self) -> &CallOperator<N> {
 51          &self.mapping
 52      }
 53  
 54      /// Returns the operand containing the key.
 55      #[inline]
 56      pub const fn key(&self) -> &Operand<N> {
 57          &self.operands[0]
 58      }
 59  
 60      /// Returns the default value.
 61      #[inline]
 62      pub const fn default(&self) -> &Operand<N> {
 63          &self.operands[1]
 64      }
 65  
 66      /// Returns the destination register.
 67      #[inline]
 68      pub const fn destination(&self) -> &Register<N> {
 69          &self.destination
 70      }
 71  }
 72  
 73  impl<N: Network> GetOrUse<N> {
 74      /// Finalizes the command.
 75      #[inline]
 76      pub fn finalize(
 77          &self,
 78          stack: &impl StackTrait<N>,
 79          store: &impl FinalizeStoreTrait<N>,
 80          registers: &mut impl RegistersTrait<N>,
 81      ) -> Result<()> {
 82          // Determine the program ID and mapping name.
 83          let (program_id, mapping_name) = match self.mapping {
 84              CallOperator::Locator(locator) => (*locator.program_id(), *locator.resource()),
 85              CallOperator::Resource(mapping_name) => (*stack.program_id(), mapping_name),
 86          };
 87  
 88          // Ensure the mapping exists.
 89          if !store.contains_mapping_speculative(&program_id, &mapping_name)? {
 90              bail!("Mapping '{program_id}/{mapping_name}' does not exist");
 91          }
 92  
 93          // Load the operand as a plaintext.
 94          let key = registers.load_plaintext(stack, self.key())?;
 95  
 96          // Retrieve the value from storage as a literal.
 97          let value = match store.get_value_speculative(program_id, mapping_name, &key)? {
 98              Some(Value::Plaintext(plaintext)) => Value::Plaintext(plaintext),
 99              Some(Value::Record(..)) => bail!("Cannot 'get.or_use' a 'record'"),
100              Some(Value::Future(..)) => bail!("Cannot 'get.or_use' a 'future'"),
101              // If a key does not exist, then use the default value.
102              None => Value::Plaintext(registers.load_plaintext(stack, self.default())?),
103          };
104  
105          // Assign the value to the destination register.
106          registers.store(stack, &self.destination, value)?;
107  
108          // Return the finalize operation.
109          Ok(())
110      }
111  }
112  
113  impl<N: Network> Parser for GetOrUse<N> {
114      /// Parses a string into an operation.
115      #[inline]
116      fn parse(string: &str) -> ParserResult<Self> {
117          // Parse the whitespace and comments from the string.
118          let (string, _) = Sanitizer::parse(string)?;
119          // Parse the opcode from the string.
120          let (string, _) = tag(*Self::opcode())(string)?;
121          // Parse the whitespace from the string.
122          let (string, _) = Sanitizer::parse_whitespaces(string)?;
123  
124          // Parse the mapping name from the string.
125          let (string, mapping) = CallOperator::parse(string)?;
126          // Parse the "[" from the string.
127          let (string, _) = tag("[")(string)?;
128          // Parse the whitespace from the string.
129          let (string, _) = Sanitizer::parse_whitespaces(string)?;
130          // Parse the key operand from the string.
131          let (string, key) = Operand::parse(string)?;
132          // Parse the whitespace from the string.
133          let (string, _) = Sanitizer::parse_whitespaces(string)?;
134          // Parse the "]" from the string.
135          let (string, _) = tag("]")(string)?;
136          // Parse the whitespace from the string.
137          let (string, _) = Sanitizer::parse_whitespaces(string)?;
138          // Parse the default value from the string.
139          let (string, default) = Operand::parse(string)?;
140  
141          // Parse the whitespace from the string.
142          let (string, _) = Sanitizer::parse_whitespaces(string)?;
143          // Parse the "into" keyword from the string.
144          let (string, _) = tag("into")(string)?;
145          // Parse the whitespace from the string.
146          let (string, _) = Sanitizer::parse_whitespaces(string)?;
147          // Parse the destination register from the string.
148          let (string, destination) = Register::parse(string)?;
149  
150          // Parse the whitespace from the string.
151          let (string, _) = Sanitizer::parse_whitespaces(string)?;
152          // Parse the ";" from the string.
153          let (string, _) = tag(";")(string)?;
154  
155          Ok((string, Self { mapping, operands: [key, default], destination }))
156      }
157  }
158  
159  impl<N: Network> FromStr for GetOrUse<N> {
160      type Err = Error;
161  
162      /// Parses a string into the command.
163      #[inline]
164      fn from_str(string: &str) -> Result<Self> {
165          match Self::parse(string) {
166              Ok((remainder, object)) => {
167                  // Ensure the remainder is empty.
168                  ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
169                  // Return the object.
170                  Ok(object)
171              }
172              Err(error) => bail!("Failed to parse string. {error}"),
173          }
174      }
175  }
176  
177  impl<N: Network> Debug for GetOrUse<N> {
178      /// Prints the command as a string.
179      fn fmt(&self, f: &mut Formatter) -> fmt::Result {
180          Display::fmt(self, f)
181      }
182  }
183  
184  impl<N: Network> Display for GetOrUse<N> {
185      /// Prints the command to a string.
186      fn fmt(&self, f: &mut Formatter) -> fmt::Result {
187          // Print the command.
188          write!(f, "{} ", Self::opcode())?;
189          // Print the mapping and key operand.
190          write!(f, "{}[{}] {} into ", self.mapping, self.key(), self.default())?;
191          // Print the destination register.
192          write!(f, "{};", self.destination)
193      }
194  }
195  
196  impl<N: Network> FromBytes for GetOrUse<N> {
197      /// Reads the command from a buffer.
198      fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
199          // Read the mapping name.
200          let mapping = CallOperator::read_le(&mut reader)?;
201          // Read the key operand.
202          let key = Operand::read_le(&mut reader)?;
203          // Read the default value.
204          let default = Operand::read_le(&mut reader)?;
205          // Read the destination register.
206          let destination = Register::read_le(&mut reader)?;
207          // Return the command.
208          Ok(Self { mapping, operands: [key, default], destination })
209      }
210  }
211  
212  impl<N: Network> ToBytes for GetOrUse<N> {
213      /// Writes the operation to a buffer.
214      fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
215          // Write the mapping name.
216          self.mapping.write_le(&mut writer)?;
217          // Write the key operand.
218          self.key().write_le(&mut writer)?;
219          // Write the default value.
220          self.default().write_le(&mut writer)?;
221          // Write the destination register.
222          self.destination.write_le(&mut writer)
223      }
224  }
225  
226  #[cfg(test)]
227  mod tests {
228      use super::*;
229      use console::{network::MainnetV0, program::Register};
230  
231      type CurrentNetwork = MainnetV0;
232  
233      #[test]
234      fn test_parse() {
235          let (string, get_or_use) = GetOrUse::<CurrentNetwork>::parse("get.or_use account[r0] r1 into r2;").unwrap();
236          assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
237          assert_eq!(get_or_use.mapping, CallOperator::from_str("account").unwrap());
238          assert_eq!(get_or_use.operands().len(), 2, "The number of operands is incorrect");
239          assert_eq!(get_or_use.key(), &Operand::Register(Register::Locator(0)), "The first operand is incorrect");
240          assert_eq!(get_or_use.default(), &Operand::Register(Register::Locator(1)), "The second operand is incorrect");
241          assert_eq!(get_or_use.destination, Register::Locator(2), "The second operand is incorrect");
242  
243          let (string, get_or_use) =
244              GetOrUse::<CurrentNetwork>::parse("get.or_use token.alpha/balances[r0] r1 into r2;").unwrap();
245          assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
246          assert_eq!(get_or_use.mapping, CallOperator::from_str("token.alpha/balances").unwrap());
247          assert_eq!(get_or_use.operands().len(), 2, "The number of operands is incorrect");
248          assert_eq!(get_or_use.key(), &Operand::Register(Register::Locator(0)), "The first operand is incorrect");
249          assert_eq!(get_or_use.default(), &Operand::Register(Register::Locator(1)), "The second operand is incorrect");
250          assert_eq!(get_or_use.destination, Register::Locator(2), "The second operand is incorrect");
251      }
252  
253      #[test]
254      fn test_from_bytes() {
255          let (string, get_or_use) = GetOrUse::<CurrentNetwork>::parse("get.or_use account[r0] r1 into r2;").unwrap();
256          assert!(string.is_empty());
257          let bytes_le = get_or_use.to_bytes_le().unwrap();
258          let result = GetOrUse::<CurrentNetwork>::from_bytes_le(&bytes_le[..]);
259          assert!(result.is_ok());
260      }
261  }