dapt 0.1.9

serializing and unseralizing unstructured data
Documentation
use std::fmt::Display;

use crate::{
    query::{
        parser::{Parser, FN_CLOSE, FN_OPEN, FN_SEP, STRING_WRAP},
        QueryResult,
    },
    Any,
};

use super::Expression;

pub const FN_LOWER: &str = "LOWER";
pub const FN_UPPER: &str = "UPPER";
pub const FN_LENGTH: &str = "LENGTH";
pub const FN_TRIM: &str = "TRIM";
pub const FN_TRIM_LEFT: &str = "TRIM_LEFT";
pub const FN_TRIM_RIGHT: &str = "TRIM_RIGHT";
pub const FN_CONCAT: &str = "CONCAT";
pub const FN_SPLIT: &str = "SPLIT";

macro_rules! simple_string_fn {
    ($name:ident, $fn:expr) => {
        #[derive(Clone)]
        pub struct $name {
            value: Box<dyn Expression>,
        }

        impl $name {
            pub fn from_parser<'a>(p: &mut Parser<'a>) -> QueryResult<$name> {
                p.consume_token($fn)?;
                p.consume_token(FN_OPEN)?;
                let value = p.parse_expression()?;
                p.consume_token(FN_CLOSE)?;

                Ok($name { value })
            }
        }

        impl Display for $name {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                write!(f, "{}({})", $fn, self.value)
            }
        }
    };
}

simple_string_fn!(StringLower, FN_LOWER);
impl Expression for StringLower {
    fn evaluate<'a, 'b: 'a>(&'a self, d: &'b crate::Dapt) -> Option<Any<'a>> {
        let value = self.value.evaluate(d)?;

        match value {
            Any::String(s) => Some(Any::String(s.to_lowercase())),
            Any::Str(s) => Some(Any::String(s.to_lowercase())),
            _ => None,
        }
    }
}

simple_string_fn!(StringUpper, FN_UPPER);
impl Expression for StringUpper {
    fn evaluate<'a, 'b: 'a>(&'a self, d: &'b crate::Dapt) -> Option<Any<'a>> {
        let value = self.value.evaluate(d)?;

        match value {
            Any::String(s) => Some(Any::String(s.to_uppercase())),
            Any::Str(s) => Some(Any::String(s.to_uppercase())),
            _ => None,
        }
    }
}

simple_string_fn!(StringLength, FN_LENGTH);
impl Expression for StringLength {
    fn evaluate<'a, 'b: 'a>(&'a self, d: &'b crate::Dapt) -> Option<Any<'a>> {
        let value = self.value.evaluate(d)?;

        match value {
            Any::String(s) => Some(Any::USize(s.len())),
            Any::Str(s) => Some(Any::USize(s.len())),
            _ => None,
        }
    }
}

simple_string_fn!(StringTrim, FN_TRIM);
impl Expression for StringTrim {
    fn evaluate<'a, 'b: 'a>(&'a self, d: &'b crate::Dapt) -> Option<Any<'a>> {
        let value = self.value.evaluate(d)?;

        match value {
            Any::String(s) => Some(Any::String(s.trim().to_string())),
            Any::Str(s) => Some(Any::Str(s.trim())),
            _ => None,
        }
    }
}

simple_string_fn!(StringTrimLeft, FN_TRIM_LEFT);
impl Expression for StringTrimLeft {
    fn evaluate<'a, 'b: 'a>(&'a self, d: &'b crate::Dapt) -> Option<Any<'a>> {
        let value = self.value.evaluate(d)?;

        match value {
            Any::String(s) => Some(Any::String(s.trim_start().to_string())),
            Any::Str(s) => Some(Any::Str(s.trim_start())),
            _ => None,
        }
    }
}

simple_string_fn!(StringTrimRight, FN_TRIM_RIGHT);
impl Expression for StringTrimRight {
    fn evaluate<'a, 'b: 'a>(&'a self, d: &'b crate::Dapt) -> Option<Any<'a>> {
        let value = self.value.evaluate(d)?;

        match value {
            Any::String(s) => Some(Any::String(s.trim_end().to_string())),
            Any::Str(s) => Some(Any::Str(s.trim_end())),
            _ => None,
        }
    }
}

#[derive(Clone)]
pub struct StringConcat {
    exprs: Vec<Box<dyn Expression>>,
}

impl StringConcat {
    pub fn from_parser<'a>(p: &mut Parser<'a>) -> QueryResult<StringConcat> {
        p.consume_token(FN_CONCAT)?;
        p.consume_token(FN_OPEN)?;

        let mut exprs = Vec::new();

        exprs.push(p.parse_expression()?);
        while p.continue_if(FN_SEP) {
            exprs.push(p.parse_expression()?);
        }

        p.consume_token(FN_CLOSE)?;

        Ok(StringConcat { exprs })
    }
}

impl Display for StringConcat {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}(", FN_CONCAT)?;
        let mut first = true;
        for e in self.exprs.iter() {
            if !first {
                write!(f, ", ")?;
            } else {
                first = false;
            }

            write!(f, "{}", e)?;
        }
        write!(f, ")")
    }
}

impl Expression for StringConcat {
    fn evaluate<'a, 'b: 'a>(&'a self, d: &'b crate::Dapt) -> Option<Any<'a>> {
        let mut result = String::new();
        for e in self.exprs.iter() {
            let value = e.evaluate(d)?;

            match value {
                Any::String(s) => result.push_str(&s),
                Any::Str(s) => result.push_str(s),
                _ => return None,
            }
        }

        Some(Any::String(result))
    }
}

#[derive(Clone)]
pub struct StringSplit {
    value: Box<dyn Expression>,
    delimiter: String,
}

impl StringSplit {
    pub fn from_parser<'a>(p: &mut Parser<'a>) -> QueryResult<StringSplit> {
        p.consume_token(FN_SPLIT)?;
        p.consume_token(FN_OPEN)?;

        let value = p.parse_expression()?;
        p.consume_token(FN_SEP)?;
        let delimiter = p.parse_string(STRING_WRAP)?;

        p.consume_token(FN_CLOSE)?;

        Ok(StringSplit { value, delimiter })
    }
}

impl Display for StringSplit {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}({}, '{}')", FN_SPLIT, self.value, self.delimiter)
    }
}

impl Expression for StringSplit {
    fn evaluate<'a, 'b: 'a>(&'a self, d: &'b crate::Dapt) -> Option<Any<'a>> {
        let value = self.value.evaluate(d)?;

        match value {
            Any::String(s) => {
                let arr = s
                    .split(&self.delimiter)
                    .map(|s| Any::String(s.to_string()))
                    .collect();
                Some(Any::Array(arr))
            }
            Any::Str(s) => {
                let arr = s.split(&self.delimiter).map(|s| Any::Str(s)).collect();
                Some(Any::Array(arr))
            }
            _ => None,
        }
    }
}