linkrs-query 0.1.0

Query engine, parser, and executor for Linkrs
Documentation
use crate::executor::expression::ExpressionError;
use linkrs_core::value::{DateTimeValue, DateValue, NullType};
use linkrs_core::Value;

pub(crate) fn execute_date_trunc(args: &[Value]) -> Result<Value, ExpressionError> {
    let precision = match &args[1] {
        Value::String(s) => s.as_str(),
        Value::Null(_) => return Ok(Value::Null(NullType::Null)),
        _ => {
            return Err(ExpressionError::type_error(
                "date_trunc precision must be a string",
            ))
        }
    };
    match &args[0] {
        Value::Date(d) => match precision {
            "year" => Ok(Value::Date(DateValue {
                year: d.year,
                month: 1,
                day: 1,
            })),
            "month" => Ok(Value::Date(DateValue {
                year: d.year,
                month: d.month,
                day: 1,
            })),
            "day" => Ok(Value::Date(DateValue {
                year: d.year,
                month: d.month,
                day: d.day,
            })),
            _ => Err(ExpressionError::type_error(format!(
                "Invalid date_trunc precision: {}",
                precision
            ))),
        },
        Value::DateTime(dt) => match precision {
            "year" => Ok(Value::DateTime(DateTimeValue {
                year: dt.year,
                month: 1,
                day: 1,
                hour: 0,
                minute: 0,
                sec: 0,
                microsec: 0,
            })),
            "month" => Ok(Value::DateTime(DateTimeValue {
                year: dt.year,
                month: dt.month,
                day: 1,
                hour: 0,
                minute: 0,
                sec: 0,
                microsec: 0,
            })),
            "day" => Ok(Value::DateTime(DateTimeValue {
                year: dt.year,
                month: dt.month,
                day: dt.day,
                hour: 0,
                minute: 0,
                sec: 0,
                microsec: 0,
            })),
            "hour" => Ok(Value::DateTime(DateTimeValue {
                year: dt.year,
                month: dt.month,
                day: dt.day,
                hour: dt.hour,
                minute: 0,
                sec: 0,
                microsec: 0,
            })),
            "minute" => Ok(Value::DateTime(DateTimeValue {
                year: dt.year,
                month: dt.month,
                day: dt.day,
                hour: dt.hour,
                minute: dt.minute,
                sec: 0,
                microsec: 0,
            })),
            "second" => Ok(Value::DateTime(DateTimeValue {
                year: dt.year,
                month: dt.month,
                day: dt.day,
                hour: dt.hour,
                minute: dt.minute,
                sec: dt.sec,
                microsec: 0,
            })),
            _ => Err(ExpressionError::type_error(format!(
                "Invalid date_trunc precision: {}",
                precision
            ))),
        },
        Value::Null(_) => Ok(Value::Null(NullType::Null)),
        _ => Err(ExpressionError::type_error(
            "date_trunc requires a date or datetime as first argument",
        )),
    }
}

pub(crate) fn execute_last_day(args: &[Value]) -> Result<Value, ExpressionError> {
    match &args[0] {
        Value::Date(d) => {
            let last_day = get_last_day(d.year, d.month);
            Ok(Value::Date(DateValue {
                year: d.year,
                month: d.month,
                day: last_day,
            }))
        }
        Value::DateTime(dt) => {
            let last_day = get_last_day(dt.year, dt.month);
            Ok(Value::DateTime(DateTimeValue {
                year: dt.year,
                month: dt.month,
                day: last_day,
                hour: dt.hour,
                minute: dt.minute,
                sec: dt.sec,
                microsec: dt.microsec,
            }))
        }
        Value::Null(_) => Ok(Value::Null(NullType::Null)),
        _ => Err(ExpressionError::type_error(
            "last_day requires a date or datetime argument",
        )),
    }
}

fn get_last_day(year: i32, month: u32) -> u32 {
    match month {
        1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
        4 | 6 | 9 | 11 => 30,
        2 => {
            if (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) {
                29
            } else {
                28
            }
        }
        _ => 30,
    }
}