use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq)]
pub struct MortarString(pub String);
impl std::fmt::Display for MortarString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct MortarNumber(pub f64);
impl std::fmt::Display for MortarNumber {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MortarBoolean(pub bool);
impl std::fmt::Display for MortarBoolean {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MortarVoid;
impl std::fmt::Display for MortarVoid {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "")
}
}
#[derive(Debug, Clone)]
pub enum MortarValue {
String(MortarString),
Number(MortarNumber),
Boolean(MortarBoolean),
Void,
}
impl MortarString {
pub fn as_str(&self) -> &str {
&self.0
}
}
impl MortarNumber {
pub fn as_f64(&self) -> f64 {
self.0
}
pub fn as_i32(&self) -> i32 {
self.0 as i32
}
pub fn as_usize(&self) -> usize {
self.0 as usize
}
}
impl MortarBoolean {
pub fn as_bool(&self) -> bool {
self.0
}
}
impl MortarValue {
pub fn as_string(&self) -> Option<&MortarString> {
match self {
MortarValue::String(s) => Some(s),
_ => None,
}
}
pub fn as_number(&self) -> Option<MortarNumber> {
match self {
MortarValue::Number(n) => Some(*n),
_ => None,
}
}
pub fn as_bool(&self) -> Option<MortarBoolean> {
match self {
MortarValue::Boolean(b) => Some(*b),
_ => None,
}
}
pub fn to_display_string(&self) -> String {
match self {
MortarValue::String(s) => s.0.clone(),
MortarValue::Number(n) => n.0.to_string(),
MortarValue::Boolean(b) => b.0.to_string(),
MortarValue::Void => String::new(),
}
}
pub fn is_truthy(&self) -> bool {
match self {
MortarValue::Boolean(b) => b.0,
MortarValue::Number(n) => n.0 != 0.0,
MortarValue::String(s) => !s.0.is_empty(),
MortarValue::Void => false,
}
}
pub fn parse(s: &str) -> Self {
if let Ok(n) = s.parse::<f64>() {
return MortarValue::Number(MortarNumber(n));
}
match s {
"true" => return MortarValue::Boolean(MortarBoolean(true)),
"false" => return MortarValue::Boolean(MortarBoolean(false)),
_ => {}
}
let trimmed = s.trim();
if trimmed.len() >= 2
&& ((trimmed.starts_with('"') && trimmed.ends_with('"'))
|| (trimmed.starts_with('\'') && trimmed.ends_with('\'')))
{
MortarValue::String(MortarString(trimmed[1..trimmed.len() - 1].to_string()))
} else {
MortarValue::String(MortarString(s.to_string()))
}
}
}
impl From<String> for MortarString {
fn from(s: String) -> Self {
MortarString(s)
}
}
impl From<&str> for MortarString {
fn from(s: &str) -> Self {
MortarString(s.to_string())
}
}
impl From<f64> for MortarNumber {
fn from(n: f64) -> Self {
MortarNumber(n)
}
}
impl From<i32> for MortarNumber {
fn from(n: i32) -> Self {
MortarNumber(n as f64)
}
}
impl From<usize> for MortarNumber {
fn from(n: usize) -> Self {
MortarNumber(n as f64)
}
}
impl From<bool> for MortarBoolean {
fn from(b: bool) -> Self {
MortarBoolean(b)
}
}
impl From<MortarString> for MortarValue {
fn from(s: MortarString) -> Self {
MortarValue::String(s)
}
}
impl From<String> for MortarValue {
fn from(s: String) -> Self {
MortarValue::String(MortarString(s))
}
}
impl From<&str> for MortarValue {
fn from(s: &str) -> Self {
MortarValue::String(MortarString(s.to_string()))
}
}
impl From<MortarNumber> for MortarValue {
fn from(n: MortarNumber) -> Self {
MortarValue::Number(n)
}
}
impl From<f64> for MortarValue {
fn from(n: f64) -> Self {
MortarValue::Number(MortarNumber(n))
}
}
impl From<i32> for MortarValue {
fn from(n: i32) -> Self {
MortarValue::Number(MortarNumber(n as f64))
}
}
impl From<usize> for MortarValue {
fn from(n: usize) -> Self {
MortarValue::Number(MortarNumber(n as f64))
}
}
impl From<MortarBoolean> for MortarValue {
fn from(b: MortarBoolean) -> Self {
MortarValue::Boolean(b)
}
}
impl From<bool> for MortarValue {
fn from(b: bool) -> Self {
MortarValue::Boolean(MortarBoolean(b))
}
}
impl From<MortarVoid> for MortarValue {
fn from(_: MortarVoid) -> Self {
MortarValue::Void
}
}
impl From<()> for MortarValue {
fn from(_: ()) -> Self {
MortarValue::Void
}
}
pub type MortarFunction = Box<dyn Fn(&[MortarValue]) -> MortarValue + Send + Sync>;
#[derive(Default)]
pub struct MortarFunctionRegistry {
functions: HashMap<String, MortarFunction>,
}
impl MortarFunctionRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register<F>(&mut self, name: impl Into<String>, func: F)
where
F: Fn(&[MortarValue]) -> MortarValue + Send + Sync + 'static,
{
self.functions.insert(name.into(), Box::new(func));
}
pub fn call(&self, name: &str, args: &[MortarValue]) -> Option<MortarValue> {
self.functions.get(name).map(|f| f(args))
}
}
impl TryFrom<MortarValue> for MortarString {
type Error = ();
fn try_from(value: MortarValue) -> Result<Self, Self::Error> {
match value {
MortarValue::String(s) => Ok(s),
MortarValue::Number(n) => Ok(MortarString(n.0.to_string())),
MortarValue::Boolean(b) => Ok(MortarString(b.0.to_string())),
MortarValue::Void => Ok(MortarString(String::new())),
}
}
}
impl TryFrom<MortarValue> for MortarNumber {
type Error = ();
fn try_from(value: MortarValue) -> Result<Self, Self::Error> {
match value {
MortarValue::Number(n) => Ok(n),
MortarValue::String(s) => s.0.parse().map(MortarNumber).map_err(|_| ()),
_ => Err(()),
}
}
}
impl TryFrom<MortarValue> for MortarBoolean {
type Error = ();
fn try_from(value: MortarValue) -> Result<Self, Self::Error> {
match value {
MortarValue::Boolean(b) => Ok(b),
_ => Err(()),
}
}
}
impl TryFrom<MortarValue> for String {
type Error = ();
fn try_from(value: MortarValue) -> Result<Self, Self::Error> {
match value {
MortarValue::String(s) => Ok(s.0),
MortarValue::Number(n) => Ok(n.0.to_string()),
MortarValue::Boolean(b) => Ok(b.0.to_string()),
MortarValue::Void => Ok(String::new()),
}
}
}
impl TryFrom<MortarValue> for f64 {
type Error = ();
fn try_from(value: MortarValue) -> Result<Self, Self::Error> {
match value {
MortarValue::Number(n) => Ok(n.0),
MortarValue::String(s) => s.0.parse().map_err(|_| ()),
_ => Err(()),
}
}
}
impl TryFrom<MortarValue> for i32 {
type Error = ();
fn try_from(value: MortarValue) -> Result<Self, Self::Error> {
match value {
MortarValue::Number(n) => Ok(n.0 as i32),
MortarValue::String(s) => s.0.parse().map_err(|_| ()),
_ => Err(()),
}
}
}
impl TryFrom<MortarValue> for usize {
type Error = ();
fn try_from(value: MortarValue) -> Result<Self, Self::Error> {
match value {
MortarValue::Number(n) => Ok(n.0 as usize),
MortarValue::String(s) => s.0.parse().map_err(|_| ()),
_ => Err(()),
}
}
}
impl TryFrom<MortarValue> for bool {
type Error = ();
fn try_from(value: MortarValue) -> Result<Self, Self::Error> {
match value {
MortarValue::Boolean(b) => Ok(b.0),
_ => Err(()),
}
}
}