gluesql-core 0.20.0

GlueSQL - Open source SQL database engine fully written in Rust with pure functional execution layer, easily swappable storage and web assembly support!
Documentation
use {
    super::ExprNode,
    crate::{
        ast::Expr,
        parse_sql::parse_comma_separated_exprs,
        plan::ExprPlan,
        result::Result,
        translate::{NO_PARAMS, translate_expr},
    },
    std::borrow::Cow,
};

#[derive(Clone, Debug)]
pub enum ExprList<'a> {
    Text(Cow<'a, str>),
    Exprs(Cow<'a, [ExprNode<'a>]>),
}

impl<'a> From<&'a str> for ExprList<'a> {
    fn from(exprs: &'a str) -> Self {
        ExprList::Text(Cow::Borrowed(exprs))
    }
}

impl From<String> for ExprList<'_> {
    fn from(exprs: String) -> Self {
        ExprList::Text(Cow::from(exprs))
    }
}

impl<'a, T: Into<ExprNode<'a>>> From<Vec<T>> for ExprList<'a> {
    fn from(exprs: Vec<T>) -> Self {
        ExprList::Exprs(Cow::Owned(exprs.into_iter().map(Into::into).collect()))
    }
}

impl<'a, T: Into<ExprNode<'a>> + Copy> From<&'a Vec<T>> for ExprList<'a> {
    fn from(exprs: &'a Vec<T>) -> Self {
        exprs.as_slice().into()
    }
}

impl<'a, T: Into<ExprNode<'a>> + Copy> From<&'a [T]> for ExprList<'a> {
    fn from(exprs: &'a [T]) -> Self {
        ExprList::Exprs(Cow::Owned(exprs.iter().map(|&v| v.into()).collect()))
    }
}

impl<'a> From<&'a [ExprNode<'a>]> for ExprList<'a> {
    fn from(exprs: &'a [ExprNode<'a>]) -> Self {
        ExprList::Exprs(Cow::Borrowed(exprs))
    }
}

impl ExprList<'_> {
    pub(super) fn build_exprs_plan(self) -> Result<Vec<ExprPlan>> {
        match self {
            ExprList::Text(exprs) => parse_comma_separated_exprs(exprs)?
                .iter()
                .map(|expr| translate_expr(expr, NO_PARAMS).map(Into::into))
                .collect::<Result<Vec<_>>>(),
            ExprList::Exprs(exprs) => {
                let exprs = exprs.into_owned();

                exprs
                    .into_iter()
                    .map(ExprNode::build_expr_plan)
                    .collect::<Result<Vec<_>>>()
            }
        }
    }

    pub(super) fn build_exprs(self) -> Result<Vec<Expr>> {
        match self {
            ExprList::Text(exprs) => parse_comma_separated_exprs(exprs)?
                .iter()
                .map(|expr| translate_expr(expr, NO_PARAMS))
                .collect::<Result<Vec<_>>>(),
            ExprList::Exprs(exprs) => exprs
                .into_owned()
                .into_iter()
                .map(ExprNode::build_expr)
                .collect::<Result<Vec<_>>>(),
        }
    }
}

#[cfg(test)]
mod tests {
    use {
        super::ExprList,
        crate::{
            parse_sql::parse_comma_separated_exprs,
            plan::ExprPlan,
            query_builder::col,
            result::Result,
            translate::{NO_PARAMS, translate_expr},
        },
        pretty_assertions::assert_eq,
    };

    fn test(actual: ExprList, expected: &str) {
        let parsed = parse_comma_separated_exprs(expected).expect(expected);
        let expected = parsed
            .iter()
            .map(|expr| translate_expr(expr, NO_PARAMS).map(ExprPlan::from))
            .collect::<Result<Vec<ExprPlan>>>();

        assert_eq!(actual.build_exprs_plan(), expected);
    }

    #[test]
    fn into_expr_list() {
        let actual: ExprList = "1, a * 2, b".into();
        let expected = "1, a * 2, b";
        test(actual, expected);

        let actual: ExprList = String::from("'hello' || 'world', col1").into();
        let expected = "'hello' || 'world', col1";
        test(actual, expected);

        // Vec<Into<ExprNode>>
        let actual: ExprList = vec!["id", "name"].into();
        let expected = "id, name";
        test(actual, expected);

        // Vec<ExprNode>
        let actual: ExprList = vec![col("id"), col("name")].into();
        let expected = "id, name";
        test(actual, expected);

        // &Vec<ExprNode>
        let actual = vec!["id", "name"];
        let actual: ExprList = (&actual).into();
        let expected = "id, name";
        test(actual, expected);

        // &[Into<ExprNode>]
        let actual = vec!["rate / 10", "col1"];
        let actual: ExprList = actual.as_slice().into();
        let expected = "rate / 10, col1";
        test(actual, expected);

        // &[ExprNode]
        let actual = vec![col("id"), col("name")];
        let actual: ExprList = actual.as_slice().into();
        let expected = "id, name";
        test(actual, expected);
    }
}