Skip to content
Snippets Groups Projects
value.rs 3.01 KiB
Newer Older
Louis's avatar
Louis committed
use crate::parser::ast::LiteralNode;
use crate::runtime::numbers::Number;
use std::fmt::{Display, Formatter};

#[derive(Clone, Debug)]
#[cfg_attr(
	feature = "serde",
	derive(serde::Serialize, serde::Deserialize),
	serde(rename_all = "snake_case")
)]
#[cfg_attr(
	all(feature = "serde", feature = "verbose-serde"),
	serde(tag = "type", content = "value")
)]
#[cfg_attr(
	all(feature = "serde", not(feature = "verbose-serde")),
	serde(untagged)
)]
pub enum ForgeValue {
	Number(Number),
	Boolean(bool),
	String(String),
	List(Vec<ForgeValue>),
	Null,
}

impl ForgeValue {
	/// Perform type coercion to force this value into a bool
	///
	/// ## True
	/// - Non-zero number
	/// - Literal value "true"
	/// - Non-empty list
	/// - Non-empty string
	///
	/// ## False
	/// - Zero
	/// - Literal value "false"
	/// - Empty list
	/// - Empty string
	/// - Null
	pub fn as_bool(&self) -> bool {
		match self {
			ForgeValue::Number(val) => val != &Number::integer(0),
			ForgeValue::Boolean(val) => *val,
			ForgeValue::List(val) => !val.is_empty(),
			ForgeValue::String(val) => !val.is_empty(),
			ForgeValue::Null => false,
		}
	}

	/// Perform type coercion to force this value into a number
	///
	/// Number => Number
	/// true => 1
	/// false => 0
	/// Non-Empty List => 1
	/// Empty List => 0
	/// Non-Empty String => 1
	/// Empty String => 0
	/// null => 0
	pub fn as_number(&self) -> Number {
		match self {
			ForgeValue::Number(val) => *val,
			ForgeValue::Boolean(val) => {
				if *val {
					Number::Integer(1)
				} else {
					Number::Integer(0)
				}
			}
			ForgeValue::List(val) => {
				if val.is_empty() {
					Number::Integer(0)
				} else {
					Number::Integer(1)
				}
			}
			ForgeValue::String(val) => {
				if val.is_empty() {
					Number::Integer(0)
				} else {
					Number::Integer(1)
				}
			}
			ForgeValue::Null => Number::Integer(0),
		}
	}

	/// Perform type coercion to force this value into a string
	///
	/// Does not quote strings, just returns their value
	/// Arrays print as a comma seperated list, surrounded by "\[" "]"
	pub fn as_string(&self) -> String {
		self.to_string()
	}
}

impl Display for ForgeValue {
	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
		match self {
			Self::Number(n) => write!(f, "{}", n),
			Self::String(st) => st.fmt(f),
			Self::Null => write!(f, "null"),
			Self::Boolean(val) => {
				if *val {
					write!(f, "true")
				} else {
					write!(f, "false")
				}
			}
			Self::List(val) => {
				write!(
					f,
					"[{}]",
					val.iter()
						.map(|val| format!("{}", val))
						.fold(String::new(), |mut acc, va| {
							if acc.is_empty() {
								va
							} else {
								acc.push_str(", ");
								acc.push_str(va.as_str());
								acc
							}
						})
				)
			}
		}
	}
}

impl From<LiteralNode> for ForgeValue {
	fn from(value: LiteralNode) -> Self {
		match value {
			LiteralNode::Boolean(val) => ForgeValue::Boolean(val),
			LiteralNode::String(val) => ForgeValue::String(val),
			LiteralNode::Number(val) => ForgeValue::Number(val),
			LiteralNode::Null => ForgeValue::Null,
		}
	}
}