From 0d22b72a2221ae5545fdca4bfd855cf2dff8e4b9 Mon Sep 17 00:00:00 2001 From: Landon Smith Date: Thu, 18 Jan 2024 13:59:41 -0700 Subject: [PATCH 1/3] Support indexing maps with any valid key type --- interpreter/src/objects.rs | 48 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/interpreter/src/objects.rs b/interpreter/src/objects.rs index 8005cf4..9353e8e 100644 --- a/interpreter/src/objects.rs +++ b/interpreter/src/objects.rs @@ -9,7 +9,7 @@ use core::ops; use serde::Serialize; use std::cmp::Ordering; use std::collections::HashMap; -use std::convert::{Infallible, TryInto}; +use std::convert::{Infallible, TryFrom, TryInto}; use std::fmt::{Display, Formatter}; use std::rc::Rc; use std::sync::Arc; @@ -482,6 +482,38 @@ impl<'a> Value { .cloned() .unwrap_or(Value::Null) .into(), + (Value::Map(map), Value::Bool(property)) => map + .map + .get(&property.into()) + .cloned() + .unwrap_or(Value::Null) + .into(), + (Value::Map(map), Value::Int(property)) => map + .map + .get(&property.into()) + .cloned() + .or_else(|| { + // Check for matching unsinged int indexes too. + let Ok(index) = u64::try_from(property) else { + return None; + }; + map.map.get(&index.into()).cloned() + }) + .unwrap_or(Value::Null) + .into(), + (Value::Map(map), Value::UInt(property)) => map + .map + .get(&property.into()) + .cloned() + .or_else(|| { + // Check for matching singed int indexes too. + let Ok(index) = i64::try_from(property) else { + return None; + }; + map.map.get(&index.into()).cloned() + }) + .unwrap_or(Value::Null) + .into(), _ => unimplemented!(), } } @@ -696,7 +728,7 @@ impl ops::Rem for Value { #[cfg(test)] mod tests { - use crate::{Context, Program}; + use crate::{objects::Key, Context, Program}; use std::collections::HashMap; #[test] @@ -710,4 +742,16 @@ mod tests { let value = program.execute(&context).unwrap(); assert_eq!(value, "application/json".into()); } + + #[test] + fn test_numeric_map_access() { + let mut context = Context::default(); + let mut headers = HashMap::new(); + headers.insert(Key::Uint(0), "zero".to_string()); + context.add_variable_from_value("headers", headers); + + let program = Program::compile("headers[0]").unwrap(); + let value = program.execute(&context).unwrap(); + assert_eq!(value, "zero".into()); + } } From 4d1ee432efe6e8cb6f6532aa869539b0d29ada3f Mon Sep 17 00:00:00 2001 From: Landon Smith Date: Mon, 19 Feb 2024 15:43:51 -0700 Subject: [PATCH 2/3] Use more appropriate variable name for numeric map access test --- interpreter/src/objects.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/interpreter/src/objects.rs b/interpreter/src/objects.rs index 9353e8e..f607100 100644 --- a/interpreter/src/objects.rs +++ b/interpreter/src/objects.rs @@ -746,12 +746,12 @@ mod tests { #[test] fn test_numeric_map_access() { let mut context = Context::default(); - let mut headers = HashMap::new(); - headers.insert(Key::Uint(0), "zero".to_string()); - context.add_variable_from_value("headers", headers); + let mut numbers = HashMap::new(); + numbers.insert(Key::Uint(1), "one".to_string()); + context.add_variable_from_value("numbers", numbers); - let program = Program::compile("headers[0]").unwrap(); + let program = Program::compile("numbers[1]").unwrap(); let value = program.execute(&context).unwrap(); - assert_eq!(value, "zero".into()); + assert_eq!(value, "one".into()); } } From f30820fd586b458804968c592e1be72f76422631 Mon Sep 17 00:00:00 2001 From: Landon Smith Date: Sat, 24 Feb 2024 15:22:33 -0700 Subject: [PATCH 3/3] Factor out duplicated code into Map::get implementation --- interpreter/src/objects.rs | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/interpreter/src/objects.rs b/interpreter/src/objects.rs index f607100..120b4b8 100644 --- a/interpreter/src/objects.rs +++ b/interpreter/src/objects.rs @@ -25,6 +25,22 @@ impl PartialOrd for Map { } } +impl Map { + /// Returns a reference to the value corresponding to the key. Implicitly converts between int + /// and uint keys. + pub fn get(&self, key: &Key) -> Option<&Value> { + self.map.get(key).or_else(|| { + // Also check keys that are cross type comparable. + let converted = match key { + Key::Int(k) => Key::Uint(u64::try_from(*k).ok()?), + Key::Uint(k) => Key::Int(i64::try_from(*k).ok()?), + _ => return None, + }; + self.map.get(&converted) + }) + } +} + #[derive(Debug, Eq, PartialEq, Hash, Ord, Clone, PartialOrd)] pub enum Key { Int(i64), @@ -477,41 +493,23 @@ impl<'a> Value { .into() } (Value::Map(map), Value::String(property)) => map - .map .get(&property.into()) .cloned() .unwrap_or(Value::Null) .into(), (Value::Map(map), Value::Bool(property)) => map - .map .get(&property.into()) .cloned() .unwrap_or(Value::Null) .into(), (Value::Map(map), Value::Int(property)) => map - .map .get(&property.into()) .cloned() - .or_else(|| { - // Check for matching unsinged int indexes too. - let Ok(index) = u64::try_from(property) else { - return None; - }; - map.map.get(&index.into()).cloned() - }) .unwrap_or(Value::Null) .into(), (Value::Map(map), Value::UInt(property)) => map - .map .get(&property.into()) .cloned() - .or_else(|| { - // Check for matching singed int indexes too. - let Ok(index) = i64::try_from(property) else { - return None; - }; - map.map.get(&index.into()).cloned() - }) .unwrap_or(Value::Null) .into(), _ => unimplemented!(),