From 4ea8dffca3dae7fd46d2496e82226b8896000584 Mon Sep 17 00:00:00 2001 From: Konstantin Stepanov Date: Fri, 27 Mar 2015 20:23:13 +0300 Subject: [PATCH] working parser! --- src/main.rs | 382 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 381 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index f79c691..02f7e5a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,2 +1,382 @@ -fn main() { +#![feature(core)] +#![allow(dead_code)] // for debug + +use std::ops::Add; +use std::num::FromPrimitive; +use std::iter::{Iterator, IteratorExt}; +use std::str::FromStr; +use std::ascii::AsciiExt; +use std::fmt::{self, Display, Formatter}; + +#[derive(Debug)] +struct CrontabRecord { + mins: Minutes, + hrs: Hours, + days: Days, + mons: Months, + dows: DaysOfWeek, + user: Option, + cmd: String +} + +#[derive(Debug)] +struct CrontabRecordParseError; + +macro_rules! parse_cron_rec_field { + ($iter:expr) => { + try!($iter.next().ok_or(IntervalParseError).and_then(FromStr::from_str).map_err(|_| CrontabRecordParseError)) + } +} + +impl FromStr for CrontabRecord { + type Err = CrontabRecordParseError; + + fn from_str(s: &str) -> Result { + let seps = [' ', '\t']; + let mut splits = s.split(&seps[..]); + Ok(CrontabRecord { + mins: parse_cron_rec_field!(splits), + hrs: parse_cron_rec_field!(splits), + days: parse_cron_rec_field!(splits), + mons: parse_cron_rec_field!(splits), + dows: parse_cron_rec_field!(splits), + user: None, + cmd: splits.collect::>().connect(" ") + }) + } +} + +type Minutes = Vec>; +type Hours = Vec>; +type Days = Vec>; +type Months = Vec>; +type DaysOfWeek = Vec>; + +#[derive(Debug)] +enum Interval { + Value(T), + Range(T, T, u8), + Full(u8) +} + +#[derive(Debug)] +struct IntervalParseError; + +impl Display for Interval { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match *self { + Interval::Value(val) => val.fmt(f), + Interval::Range(from, to, step) => from.fmt(f) + .and_then(|_| f.write_str("-")) + .and_then(|_| to.fmt(f)) + .and_then(|_| if step > 1 { + f.write_str("/").and_then(|_| step.fmt(f)) + } else { + Ok(()) + }), + Interval::Full(step) => f.write_str("*") + .and_then(|_| if step > 1 { + f.write_str("/").and_then(|_| step.fmt(f)) + } else { + Ok(()) + }) + } + } +} + +impl FromStr for Interval { + type Err = IntervalParseError; + fn from_str(s: &str) -> Result, IntervalParseError> { + let (range, step): (&str, u8) = if let Some(slash) = s.find('/') { + (&s[..slash], try!(s[slash+1..].parse().map_err(|_| IntervalParseError))) // FIXME + } else { + (s, 1) + }; + + if step == 0 { + return Err(IntervalParseError) + } + + if range == "*" { + return Ok(Interval::Full(step)) + } + + let (from, to): (T, T) = if let Some(hyphen) = range.find('-') { + (try!(range[..hyphen].parse().map_err(|_| IntervalParseError)), + try!(range[hyphen+1..].parse().map_err(|_| IntervalParseError))) + } else { + return range.parse().map_err(|_| IntervalParseError).map(Interval::Value); + }; + + if from > to { + return Err(IntervalParseError) + } + + Ok(Interval::Range(from, to, step)) + } +} + +impl Interval where T: Limited { + fn new(val: T) -> Interval { + Interval::Value(val) + } + + fn full_step(step: u8) -> Interval { + Interval::Full(step) + } + + fn full() -> Interval { + Interval::Full(1) + } + + fn from_range(from: T, to: T) -> Interval { + Interval::Range(from, to, 1) + } + + fn from_range_step(from: T, to: T, step: u8) -> Interval { + Interval::Range(from, to, step) + } + + fn iter(&self) -> IntervalIter { + match *self { + Interval::Value(v) => IntervalIter { cur: v, max: v, step: 1, stop: false }, + Interval::Range(f, t, s) => IntervalIter { cur: f, max: t, step: s, stop: false }, + Interval::Full(s) => IntervalIter { cur: T::min_value(), max: T::max_value(), step: s, stop: false } + } + } +} + +#[derive(Debug)] +struct IntervalIter { + cur: T, + max: T, + step: u8, + stop: bool +} + +impl Iterator for IntervalIter { + type Item = T; + fn next(&mut self) -> Option { + if self.stop { + return None; + } + + if self.cur <= self.max { + let result = self.cur; + self.cur = self.cur + self.step; + self.stop = result >= self.max; + Some(result) + } else { + self.stop = true; + None + } + } +} + +trait Limited: Add + PartialOrd + Copy { + fn min_value() -> Self; + fn max_value() -> Self; +} + +macro_rules! limited { + ($name:ident, min=$min:expr, max=$max:expr) => { + impl Limited for $name { + fn min_value() -> $name { $name($min) } + fn max_value() -> $name { $name($max) } + } + + impl Add for $name { + type Output = $name; + fn add(self, rhs: u8) -> $name { + //$name(((self.0 - $min) + rhs) % ($max - $min) + $min) + let val = self.0 + rhs; + $name(if val < $min { $min } else if val > $max { $max } else { val }) + } + } + + impl Display for $name { + #[inline] + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + self.0.fmt(f) + } + } + + impl FromStr for $name { + type Err = ::Err; + #[inline] + fn from_str(s: &str) -> Result<$name, ::Err> { + s.parse().map($name) + } + } + } +} + +#[derive(Debug, Copy, PartialEq, Eq, PartialOrd, Ord)] +struct Minute(u8); +limited!(Minute, min=0, max=59); + +#[derive(Debug, Copy, PartialEq, Eq, PartialOrd, Ord)] +struct Hour(u8); +limited!(Hour, min=0, max=23); + +#[derive(Debug, Copy, PartialEq, Eq, PartialOrd, Ord)] +struct Day(u8); +limited!(Day, min=1, max=31); + +#[derive(Debug, Copy, FromPrimitive, PartialEq, Eq, PartialOrd, Ord)] +#[repr(u8)] +enum Month { + January = 1, + February = 2, + March = 3, + April = 4, + May = 5, + June = 6, + July = 7, + August = 8, + September = 9, + October = 10, + November = 11, + December = 12 +} + +#[derive(Debug)] +struct MonthParseError; + +impl FromStr for Month { + type Err = MonthParseError; + fn from_str(s: &str) -> Result { + s.parse::() + .map_err(|_| MonthParseError) + .and_then(|v| Month::from_u8(v).ok_or(MonthParseError)) + .or_else(|_| match &*s[..3].to_ascii_lowercase() { + "jan" => Ok(Month::January), + "feb" => Ok(Month::February), + "mar" => Ok(Month::March), + "apr" => Ok(Month::April), + "may" => Ok(Month::May), + "jun" => Ok(Month::June), + "jul" => Ok(Month::July), + "aug" => Ok(Month::August), + "sep" => Ok(Month::September), + "oct" => Ok(Month::October), + "nov" => Ok(Month::November), + "dec" => Ok(Month::December), + _ => Err(MonthParseError) + }) + } +} + +impl Display for Month { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.write_str(match *self { + Month::January => "Jan", + Month::February => "Feb", + Month::March => "Mar", + Month::April => "Apr", + Month::May => "May", + Month::June => "Jun", + Month::July => "Jul", + Month::August => "Aug", + Month::September => "Sep", + Month::October => "Oct", + Month::November => "Nov", + Month::December => "Dec" + }) + } +} + +impl Limited for Month { + fn min_value() -> Month { Month::January } + fn max_value() -> Month { Month::December } +} + +impl Add for Month { + type Output = Month; + fn add(self, rhs: u8) -> Month { + let val = self as u8 + rhs; + if val < 1 { Month::January } + else if val > 12 { Month::December } + else { FromPrimitive::from_u8(val).unwrap() } + } +} + +#[derive(Debug, Copy, FromPrimitive, PartialEq, Eq, PartialOrd, Ord)] +#[repr(u8)] +enum DayOfWeek { + Sunday = 0, + Monday = 1, + Tuesday = 2, + Wednesday = 3, + Thursday = 4, + Friday = 5, + Saturday = 6, +} +impl Limited for DayOfWeek { + fn min_value() -> DayOfWeek { DayOfWeek::Sunday } + fn max_value() -> DayOfWeek { DayOfWeek::Saturday } +} + +impl Display for DayOfWeek { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.write_str(match *self { + DayOfWeek::Sunday => "Sun", + DayOfWeek::Monday => "Mon", + DayOfWeek::Tuesday => "Tue", + DayOfWeek::Wednesday => "Wed", + DayOfWeek::Thursday => "Thu", + DayOfWeek::Friday => "Fri", + DayOfWeek::Saturday => "Sat", + }) + } +} + +#[derive(Debug)] +struct DayOfWeekParseError; + +impl FromStr for DayOfWeek { + type Err = DayOfWeekParseError; + fn from_str(s: &str) -> Result { + s.parse::() + .map_err(|_| DayOfWeekParseError) + .and_then(|v| DayOfWeek::from_u8(v % 7).ok_or(DayOfWeekParseError)) + .or_else(|_| match &*s[..3].to_ascii_lowercase() { + "sun" => Ok(DayOfWeek::Sunday), + "mon" => Ok(DayOfWeek::Monday), + "tue" => Ok(DayOfWeek::Tuesday), + "wed" => Ok(DayOfWeek::Wednesday), + "thu" => Ok(DayOfWeek::Thursday), + "fri" => Ok(DayOfWeek::Friday), + "sat" => Ok(DayOfWeek::Saturday), + _ => Err(DayOfWeekParseError) + }) + } +} + +impl Add for DayOfWeek { + type Output = DayOfWeek; + fn add(self, rhs: u8) -> DayOfWeek { + let val = self as u8 + rhs; + if val > 6 { DayOfWeek::Saturday } + else { FromPrimitive::from_u8(val).unwrap() } + } +} + +impl FromStr for Vec> { + type Err = IntervalParseError; + fn from_str(s: &str) -> Result>, IntervalParseError> { + s.split(',').map(|v| v.parse::>()).collect() + } +} + +fn main() { + let s : Vec> = "mon,sun-sat/2,*".parse().unwrap(); + println!("{}", s[1]); + for v in s.iter().flat_map(Interval::iter) { + println!("{:?}", v); + } + + println!("{:?}", "* b c\td\t e ".splitn(6, &[' ', '\t'][..]).collect::>()); + + println!("{:?}", "* * * * * root command with args".parse::()); }