kqlparser 0.0.4

Simple Kusto Query Language (KQL) Parser
Documentation
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::{u32, alpha1, digit1, multispace0, multispace1, one_of};
use nom::combinator::{map, map_res, opt, value};
use nom::sequence::{pair, preceded, terminated, tuple};
use nom::IResult;

use crate::ast::DateTime;

struct ParsedDate {
    year: u32,
    month: u32,
    day: u32
}

struct ParsedTime {
    hour: u32,
    minute: u32,
    second: u32
}

pub fn iso8601_datetime(input: &str) -> IResult<&str, DateTime> {
    let (input, (date, _, time, timezone)) = tuple((
        iso8601_date,
        alt((multispace1, tag("T"))),
        iso8601_time,
        opt(preceded(multispace0, iso8601_timezone)),
    ))(input)?;

    Ok((
        input,
        DateTime {
            year: date.year,
            month: date.month,
            day: date.day,
            hour: time.hour,
            minute: time.minute,
            second: time.second,
            timezone,
        },
    ))
}

fn iso8601_date(input: &str) -> IResult<&str, ParsedDate> {
    let (input, (year, _, month, _, day)) = tuple((
        u32,
        tag("-"),
        u32,
        tag("-"),
        u32,
    ))(input)?;

    Ok((
        input,
        ParsedDate {
            year,
            month,
            day,
        },
    ))
}

fn iso8601_time(input: &str) -> IResult<&str, ParsedTime> {
    map(tuple((
        u32,
        preceded(tag(":"), u32),
        opt(preceded(tag(":"), u32)),
    )), |(hour, minute, second)| ParsedTime {
        hour,
        minute,
        second: second.unwrap_or(0),
    })(input)
}

fn iso8601_timezone(input: &str) -> IResult<&str, String> {
    alt((
        map(pair(one_of("+-"), digit1), |(sign, value)| -> String {
            format!("{}{}", sign, value)
        }),
        map_res(pair(opt(one_of("+-")), digit1), |(sign, value): (Option<char>, &str)| -> Result<String, nom::error::Error<&str>> {
            Ok(format!("{}{}", sign.unwrap_or('+'), value))
        }),
    ))(input)
}

fn rfc822_date(input: &str) -> IResult<&str, ParsedDate> {
    map(tuple((
        u32,
        multispace1,
        month,
        multispace1,
        u32
    )), |(day, _, month, _, year)| ParsedDate {
        year,
        month,
        day,
    })(input)
}

pub fn rfc822_datetime(input: &str) -> IResult<&str, DateTime> {
    map(tuple((
        opt(terminated(alpha1, tag(","))), // Optional day name
        multispace0,
        rfc822_date,
        multispace0,
        time,
        multispace0,
        rfc822_timezone,
    )), |(_, _, date, _, time, _, timezone)| DateTime {
        year: date.year,
        month: date.month,
        day: date.day,
        hour: time.hour,
        minute: time.minute,
        second: time.second,
        timezone: Some(timezone),
    })(input)
}

fn rfc822_timezone(input: &str) -> IResult<&str, String> {
    alt((
        map(pair(one_of("+-"), digit1), |(sign, value)| {
            format!("{}{}", sign, value)
        }),
        map(pair(opt(one_of("+-")), digit1), |(sign, value)| {
            format!("{}{}", sign.unwrap_or('+'), value)
        })
    ))(input)
}

pub fn rfc850_datetime(input: &str) -> IResult<&str, DateTime> {
    let (input, (_, _, date, _, time, _, timezone)) = tuple((
        opt(terminated(alpha1, tag(","))), // Optional day name
        multispace0,
        rfc850_date,
        multispace0,
        time,
        multispace0,
        rfc850_timezone,
    ))(input)?;

    Ok((
        input,
        DateTime {
            year: date.year,
            month: date.month,
            day: date.day,
            hour: time.hour,
            minute: time.minute,
            second: 0,
            timezone: Some(timezone),
        },
    ))
}

fn rfc850_date(input: &str) -> IResult<&str, ParsedDate> {
    map(tuple((
        u32,
        tag("-"),
        month,
        tag("-"),
        u32
    )), |(day, _, month, _, year)| ParsedDate {
        year,
        month,
        day,
    })(input)
}

fn rfc850_timezone(input: &str) -> IResult<&str, String> {
    alt((
        map(pair(one_of("+-"), digit1), |(sign, value)| {
            format!("{}{}", sign, value)
        }),
        map(pair(opt(one_of("+-")), digit1), |(sign, value)| {
            format!("{}{}", sign.unwrap_or('+'), value)
        }),
    ))(input)
}

fn time(input: &str) -> IResult<&str, ParsedTime> {
    map(tuple((
        u32,
        preceded(tag(":"), u32),
        opt(preceded(tag(":"), u32)),
    )), |(hour, minute, second)| ParsedTime {
        hour,
        minute,
        second: second.unwrap_or(0),
    })(input)
}

fn month(input: &str) -> IResult<&str, u32> {
    alt((
        alt((
            value(1, tag("Jan")),
            value(1, tag("January")),
            value(2, tag("Feb")),
            value(2, tag("February")),
            value(3, tag("Mar")),
            value(3, tag("March")),
            value(4, tag("Apr")),
            value(4, tag("April")),
            value(5, tag("May")),
            value(6, tag("Jun")),
            value(6, tag("June"))
        )),
        alt((
            value(7, tag("Jul")),
            value(7, tag("July")),
            value(8, tag("Aug")),
            value(8, tag("August")),
            value(9, tag("Sep")),
            value(9, tag("September")),
            value(10, tag("Oct")),
            value(10, tag("October")),
            value(11, tag("Nov")),
            value(11, tag("November")),
            value(12, tag("Dec")),
            value(12, tag("December"))
        ))
    ))(input)
}