1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
use std::{cell::RefCell, mem, ops::Deref, rc::Rc};

use crate::RantValue;

/// Represents a Rant variable of one of two flavors: **by-value** or **by-reference**.
///
/// ## Cloning
/// The `Clone` implementation for this type does not copy any data in `ByRef*` variants; it only copies the reference.
#[derive(Debug)]
pub enum RantVar {
  /// By-value variable
  ByVal(RantValue),
  /// By-value constant
  ByValConst(RantValue),
  /// By-ref variable
  ByRef(Rc<RefCell<RantValue>>),
  /// By-ref constant
  ByRefConst(Rc<RantValue>)
}

impl Default for RantVar {
  fn default() -> Self {
    Self::ByVal(RantValue::Empty)
  }
}

impl Clone for RantVar {
  /// Creates a copy of the variable, preserving references.
  fn clone(&self) -> Self {
    match self {
      Self::ByVal(val) => Self::ByVal(val.clone()),
      Self::ByRef(val_ref) => Self::ByRef(Rc::clone(val_ref)),
      Self::ByValConst(val) => Self::ByValConst(val.clone()),
      Self::ByRefConst(val_ref) => Self::ByRefConst(Rc::clone(val_ref)),
    }
  }
}

impl RantVar {
  /// Returns `true` if the variable is a constant.
  #[inline]
  pub fn is_const(&self) -> bool {
    matches!(self, Self::ByValConst(_) | Self::ByRefConst(_))
  }

  /// Returns `true` if the variable is by-value.
  #[inline]
  pub fn is_by_val(&self) -> bool {
    matches!(self, Self::ByVal(_) | Self::ByValConst(_))
  }
  
  /// Returns `true` if the variable is by-reference.
  #[inline]
  pub fn is_by_ref(&self) -> bool {
    matches!(self, Self::ByRef(_) | Self::ByRefConst(_))
  }
  
  /// Converts the variable to its by-reference counterpart.
  #[inline]
  pub fn make_by_ref(&mut self) {
    if self.is_by_ref() { return }
    match mem::take(self) {
      Self::ByVal(val) => *self = Self::ByRef(Rc::new(RefCell::new(val))),
      Self::ByValConst(val) => *self = Self::ByRefConst(Rc::new(val)),
      _ => unreachable!()
    }
  }
  
  /// Attempts to write the specified value to the variable.
  /// If the variable is a constant, returns `false`; otherwise, returns `true`.
  #[inline]
  pub fn write(&mut self, value: RantValue) -> bool {
    match self {
      Self::ByVal(val) => *val = value,
      Self::ByRef(val_ref) => {
        val_ref.replace(value);
      },
      Self::ByRefConst(_) | Self::ByValConst(_) => return false,
    }
    true
  }
  
  /// Returns an immutable reference to the contained value.
  #[inline]
  pub fn value_ref(&self) -> impl Deref<Target = RantValue> + '_ {
    match self {
      Self::ByVal(val) | Self::ByValConst(val) => cervine::Cow::Borrowed(val),
      Self::ByRef(val_ref) => cervine::Cow::Owned(val_ref.borrow()),
      Self::ByRefConst(val_ref) => cervine::Cow::Borrowed(val_ref.as_ref()),
    }
  }

  /// Returns a copy of the variable value.
  #[inline]
  pub fn value_cloned(&self) -> RantValue {
    match self {
      RantVar::ByVal(val) | RantVar::ByValConst(val) => val.clone(),
      RantVar::ByRef(val_ref) => val_ref.borrow().clone(),
      RantVar::ByRefConst(val_ref) => val_ref.as_ref().clone(),
    }
  }
}