tremor-value 0.13.0-rc.11

Tremor Script Interpreter
// Copyright 2020-2021, The Tremor Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::Value;
use beef::Cow;
use halfbrown::RawEntryMut;
use std::fmt;
use std::hash::{BuildHasher, Hash, Hasher};
use value_trait::{Mutable, ValueAccess, ValueType};

/// Well known key that can be looked up in a `Value` faster.
/// It achives this by memorizing the hash.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct KnownKey<'key> {
    key: Cow<'key, str>,
    hash: u64,
}
impl<'key> std::fmt::Display for KnownKey<'key> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.key.fmt(f)
    }
}

/// Error for known keys
#[derive(Debug, PartialEq, Clone, Eq)]
pub enum Error {
    /// The target passed wasn't an object
    NotAnObject(ValueType),
}

// #[cfg_attr(coverage, no_coverage)]
impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::NotAnObject(t) => write!(f, "Expected object but got {t:?}"),
        }
    }
}
impl std::error::Error for Error {}

impl<'key, S> From<S> for KnownKey<'key>
where
    Cow<'key, str>: From<S>,
{
    fn from(key: S) -> Self {
        let key = Cow::from(key);
        let hash_builder = halfbrown::DefaultHashBuilder::default();
        let mut hasher = hash_builder.build_hasher();
        key.hash(&mut hasher);
        Self {
            hash: hasher.finish(),
            key,
        }
    }
}

impl<'key> KnownKey<'key> {
    /// The known key
    #[inline]
    #[must_use]
    pub fn key(&self) -> &str {
        &self.key
    }

    /// Looks up this key in a `Value`, returns None if the
    /// key wasn't present or `target` isn't an object
    ///
    /// ```rust
    /// use tremor_value::prelude::*;
    /// let object = literal!({
    ///   "answer": 42,
    ///   "key": 7
    /// });
    /// let known_key = KnownKey::from("answer");
    /// assert_eq!(known_key.lookup(&object).unwrap(), &42);
    /// ```
    #[inline]
    #[must_use]
    pub fn lookup<'target, 'value>(
        &self,
        target: &'target Value<'value>,
    ) -> Option<&'target Value<'value>>
    where
        'value: 'target,
    {
        target.as_object().and_then(|m| self.map_lookup(m))
    }
    /// Looks up this key in a `HashMap<<Cow<'value, str>, Value<'value>>` the inner representation of an object `Value`, returns None if the
    /// key wasn't present.
    ///
    /// ```rust
    /// use tremor_value::prelude::*;
    /// let object = literal!({
    ///   "answer": 42,
    ///   "key": 7
    /// });
    /// let known_key = KnownKey::from("answer");
    /// if let Some(inner) = object.as_object() {
    ///   assert_eq!(known_key.map_lookup(inner).unwrap(), &42);
    /// }
    /// ```

    #[inline]
    #[must_use]
    pub fn map_lookup<'target, 'value>(
        &self,
        map: &'target halfbrown::HashMap<Cow<'value, str>, Value<'value>>,
    ) -> Option<&'target Value<'value>>
    where
        'value: 'target,
    {
        map.raw_entry()
            .from_key_hashed_nocheck(self.hash, self.key())
            .map(|kv| kv.1)
    }

    /// Looks up this key in a `Value`, returns None if the
    /// key wasn't present or `target` isn't an object
    ///
    /// ```rust
    /// use tremor_value::prelude::*;
    /// let mut object = literal!({
    ///   "answer": 23,
    ///   "key": 7
    /// });
    /// let known_key = KnownKey::from("answer");
    ///
    /// assert_eq!(object["answer"], 23);
    ///
    /// if let Some(answer) = known_key.lookup_mut(&mut object) {
    ///   *answer = Value::from(42);
    /// }
    ///
    /// assert_eq!(object["answer"], 42);
    /// ```
    #[inline]
    pub fn lookup_mut<'target, 'value>(
        &self,
        target: &'target mut Value<'value>,
    ) -> Option<&'target mut Value<'value>>
    where
        'key: 'value,
        'value: 'target,
    {
        target.as_object_mut().and_then(|m| self.map_lookup_mut(m))
    }

    /// Looks up this key in a `HashMap<Cow<'value, str>, Value<'value>>`, the inner representation of an object value.
    /// returns None if the key wasn't present.
    ///
    /// ```rust
    /// use tremor_value::prelude::*;
    /// let mut object = literal!({
    ///   "answer": 23,
    ///   "key": 7
    /// });
    ///
    /// assert_eq!(object["answer"], 23);
    ///
    /// let known_key = KnownKey::from("answer");
    /// if let Some(inner) = object.as_object_mut() {
    ///   if let Some(answer) = known_key.map_lookup_mut(inner) {
    ///     *answer = Value::from(42);
    ///   }
    /// }
    /// assert_eq!(object["answer"], 42);
    ///
    /// ```
    #[inline]
    pub fn map_lookup_mut<'target, 'value>(
        &self,
        map: &'target mut halfbrown::HashMap<Cow<'value, str>, Value<'value>>,
    ) -> Option<&'target mut Value<'value>>
    where
        'key: 'value,
        'value: 'target,
    {
        match map
            .raw_entry_mut()
            .from_key_hashed_nocheck(self.hash, &self.key)
        {
            RawEntryMut::Occupied(e) => Some(e.into_mut()),
            RawEntryMut::Vacant(_e) => None,
        }
    }

    /// Looks up this key in a `Value`, inserts `with` when the key
    ///  when wasn't present returns None if the `target` isn't an object
    ///
    /// # Errors
    /// - if the value isn't an object
    ///
    /// ```rust
    /// use tremor_value::prelude::*;
    /// let mut object = literal!({
    ///   "answer": 23,
    ///   "key": 7
    /// });
    /// let known_key = KnownKey::from("answer");
    ///
    /// assert_eq!(object["answer"], 23);
    ///
    /// if let Ok(answer) = known_key.lookup_or_insert_mut(&mut object, || 17.into()) {
    ///   assert_eq!(*answer, 23);
    ///   *answer = Value::from(42);
    /// }
    ///
    /// assert_eq!(object["answer"], 42);
    ///
    /// let known_key2 = KnownKey::from("also the answer");
    /// if let Ok(answer) = known_key2.lookup_or_insert_mut(&mut object, || 8.into()) {
    ///   assert_eq!(*answer, 8);
    ///   *answer = Value::from(42);
    /// }
    ///
    /// assert_eq!(object["also the answer"], 42);
    /// ```
    #[inline]
    pub fn lookup_or_insert_mut<'target, 'value, F>(
        &self,
        target: &'target mut Value<'value>,
        with: F,
    ) -> Result<&'target mut Value<'value>, Error>
    where
        'key: 'value,
        'value: 'target,
        F: FnOnce() -> Value<'value>,
    {
        // we make use of internals here, but this is the fastest way, only requiring one match
        match target {
            Value::Object(m) => Ok(self.map_lookup_or_insert_mut(m, with)),
            other => Err(Error::NotAnObject(other.value_type())),
        }
    }

    /// Looks up this key in a `HashMap<Cow<'value, str>, Value<'value>>`, the inner representation of an object `Value`.
    /// Inserts `with` when the key when wasn't present.
    ///
    /// ```rust
    /// use tremor_value::prelude::*;
    /// let mut object = literal!({
    ///   "answer": 23,
    ///   "key": 7
    /// });
    /// let known_key = KnownKey::from("answer");
    ///
    /// assert_eq!(object["answer"], 23);
    ///
    /// if let Some(inner) = object.as_object_mut() {
    ///   let answer = known_key.map_lookup_or_insert_mut(inner, || 17.into());
    ///   assert_eq!(*answer, 23);
    ///   *answer = Value::from(42);
    /// }
    ///
    /// assert_eq!(object["answer"], 42);
    ///
    /// let known_key2 = KnownKey::from("also the answer");
    /// if let Some(inner) = object.as_object_mut() {
    ///   let answer = known_key2.map_lookup_or_insert_mut(inner, || 8.into());
    ///   assert_eq!(*answer, 8);
    ///   *answer = Value::from(42);
    /// }
    ///
    /// assert_eq!(object["also the answer"], 42);
    /// ```
    #[inline]
    pub fn map_lookup_or_insert_mut<'target, 'value, F>(
        &self,
        map: &'target mut halfbrown::HashMap<Cow<'value, str>, Value<'value>>,
        with: F,
    ) -> &'target mut Value<'value>
    where
        'key: 'value,
        'value: 'target,
        F: FnOnce() -> Value<'value>,
    {
        let key: &str = &self.key;
        map.raw_entry_mut()
            .from_key_hashed_nocheck(self.hash, key)
            .or_insert_with(|| (self.key.clone(), with()))
            .1
    }

    /// Inserts a value key into  `Value`, returns None if the
    /// key wasn't present otherwise Some(`old value`).
    ///
    /// # Errors
    /// - if `target` isn't an object
    ///
    /// ```rust
    /// use tremor_value::prelude::*;
    /// let mut object = literal!({
    ///   "answer": 23,
    ///   "key": 7
    /// });
    /// let known_key = KnownKey::from("answer");
    ///
    /// assert_eq!(object["answer"], 23);
    ///
    /// assert!(known_key.insert(&mut object, Value::from(42)).is_ok());
    ///
    /// assert_eq!(object["answer"], 42);
    ///
    /// let known_key2 = KnownKey::from("also the answer");
    ///
    /// assert!(known_key2.insert(&mut object, Value::from(42)).is_ok());
    ///
    /// assert_eq!(object["also the answer"], 42);
    /// ```
    #[inline]
    pub fn insert<'target, 'value>(
        &self,
        target: &'target mut Value<'value>,
        value: Value<'value>,
    ) -> Result<Option<Value<'value>>, Error>
    where
        'key: 'value,
        'value: 'target,
    {
        target
            .as_object_mut()
            .map(|m| self.map_insert(m, value))
            .ok_or_else(|| Error::NotAnObject(target.value_type()))
    }

    /// Inserts a value key into `map`, returns None if the
    /// key wasn't present otherwise Some(`old value`).
    ///
    /// ```rust
    /// use tremor_value::prelude::*;
    ///
    /// let mut object = literal!({
    ///   "answer": 23,
    ///   "key": 7
    /// });
    /// let known_key = KnownKey::from("answer");
    ///
    /// assert_eq!(object["answer"], 23);
    ///
    /// if let Some(inner) = object.as_object_mut() {
    ///   assert!(known_key.map_insert(inner, Value::from(42)).is_some());
    /// }
    ///
    /// assert_eq!(object["answer"], 42);
    ///
    /// let known_key2 = KnownKey::from("also the answer");
    ///
    /// if let Some(inner) = object.as_object_mut() {
    ///   assert!(known_key2.map_insert(inner, Value::from(42)).is_none());
    /// }
    ///
    /// assert_eq!(object["also the answer"], 42);
    /// ```
    #[inline]
    pub fn map_insert<'target, 'value>(
        &self,
        map: &'target mut halfbrown::HashMap<Cow<'value, str>, Value<'value>>,
        value: Value<'value>,
    ) -> Option<Value<'value>>
    where
        'key: 'value,
        'value: 'target,
    {
        match map
            .raw_entry_mut()
            .from_key_hashed_nocheck(self.hash, self.key())
        {
            RawEntryMut::Occupied(mut e) => Some(e.insert(value)),
            RawEntryMut::Vacant(e) => {
                e.insert_hashed_nocheck(self.hash, self.key.clone(), value);
                None
            }
        }
    }
}

impl<'script> KnownKey<'script> {
    /// turns the key into one with static lifetime
    #[must_use]
    pub fn into_static(self) -> KnownKey<'static> {
        let KnownKey { key, hash } = self;
        KnownKey {
            key: Cow::owned(key.to_string()),
            hash,
        }
    }
}

#[cfg(test)]
mod tests {
    #![allow(
        clippy::unnecessary_operation,
        clippy::non_ascii_literal,
        clippy::unwrap_used
    )]

    use super::*;
    use beef::Cow;
    use value_trait::Builder;

    #[test]
    fn known_key() {
        let mut v = Value::object();
        v.try_insert("key", 1);
        let key1 = KnownKey::from(Cow::from("key"));
        let key2 = KnownKey::from(Cow::from("cake"));

        assert!(key1.lookup(&Value::null()).is_none());
        assert!(key2.lookup(&Value::null()).is_none());
        assert!(key1.lookup(&v).is_some());
        assert!(key2.lookup(&v).is_none());
        assert!(key1.lookup_mut(&mut v).is_some());
        assert!(key2.lookup_mut(&mut v).is_none());
    }

    #[test]
    fn known_key_insert() {
        let mut v = Value::object();
        v.try_insert("key", 1);
        let key1 = KnownKey::from(Cow::from("key"));
        let key2 = KnownKey::from(Cow::from("cake"));

        let mut v1 = Value::null();
        assert!(key1.insert(&mut v1, 2.into()).is_err());
        assert!(key2.insert(&mut v1, 2.into()).is_err());
        assert_eq!(key1.insert(&mut v, 2.into()).unwrap(), Some(1.into()));
        assert_eq!(key2.insert(&mut v, 3.into()).unwrap(), None);
        assert_eq!(v["key"], 2);
        assert_eq!(v["cake"], 3);
    }

    #[test]
    fn lookup_or_insert_mut() {
        let mut v = Value::object();
        v.try_insert("key", 1);
        let key1 = KnownKey::from(Cow::from("key"));
        let key2 = KnownKey::from(Cow::from("cake"));

        let mut v1 = Value::null();
        assert!(key1.lookup_or_insert_mut(&mut v1, || 2.into()).is_err());
        assert!(key2.lookup_or_insert_mut(&mut v1, || 2.into()).is_err());

        {
            let r1 = key1.lookup_or_insert_mut(&mut v, || 2.into()).unwrap();
            assert_eq!(r1.as_u8(), Some(1));
        }
        {
            let r2 = key2.lookup_or_insert_mut(&mut v, || 3.into()).unwrap();
            assert_eq!(r2.as_u8(), Some(3));
        }
    }
    #[test]
    fn known_key_map() {
        let mut v = Value::object_with_capacity(128);
        v.try_insert("key", 1);
        let key1 = KnownKey::from(Cow::from("key"));
        let key2 = KnownKey::from(Cow::from("cake"));

        assert!(key1.lookup(&Value::null()).is_none());
        assert!(key2.lookup(&Value::null()).is_none());
        assert!(key1.lookup(&v).is_some());
        assert!(key2.lookup(&v).is_none());
    }

    #[test]
    fn known_key_insert_map() {
        let mut v = Value::object_with_capacity(128);
        v.try_insert("key", 1);
        let key1 = KnownKey::from(Cow::from("key"));
        let key2 = KnownKey::from(Cow::from("cake"));

        let mut v1 = Value::null();

        assert!(key1.insert(&mut v1, 2.into()).is_err());
        assert!(key2.insert(&mut v1, 2.into()).is_err());
        assert_eq!(key1.insert(&mut v, 2.into()).unwrap(), Some(1.into()));
        assert_eq!(key2.insert(&mut v, 3.into()).unwrap(), None);
        assert_eq!(v["key"], 2);
        assert_eq!(v["cake"], 3);
    }

    #[test]
    fn known_key_get_key() {
        let key1 = KnownKey::from("snot").into_static();
        assert_eq!(key1.key(), "snot");
    }
}