summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lib.rs16
-rw-r--r--src/options.rs17
-rw-r--r--src/parse_options.rs12
-rw-r--r--src/rrulestr.rs158
4 files changed, 106 insertions, 97 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 252c7e3..5a12e32 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -11,7 +11,7 @@
//! use rrule::build_rrule;
//!
//! // Parse a RRule string, return a RRule type
-//! let mut rrule = build_rrule("DTSTART:20120201T093000Z\nRRULE:FREQ=WEEKLY;INTERVAL=5;UNTIL=20130130T230000Z;BYDAY=MO,FR");
+//! let mut rrule = build_rrule("DTSTART:20120201T093000Z\nRRULE:FREQ=WEEKLY;INTERVAL=5;UNTIL=20130130T230000Z;BYDAY=MO,FR").unwrap();
//! assert_eq!(rrule.all().len(), 21);
//! ```
//!
@@ -24,7 +24,7 @@
//! use rrule::build_rruleset;
//!
//! // Parse a RRuleSet string, return a RRuleSet type
-//! let mut rrule = build_rruleset("DTSTART:20120201T023000Z\nRRULE:FREQ=MONTHLY;COUNT=5\nRDATE:20120701T023000Z,20120702T023000Z\nEXRULE:FREQ=MONTHLY;COUNT=2\nEXDATE:20120601T023000Z");
+//! let mut rrule = build_rruleset("DTSTART:20120201T023000Z\nRRULE:FREQ=MONTHLY;COUNT=5\nRDATE:20120701T023000Z,20120702T023000Z\nEXRULE:FREQ=MONTHLY;COUNT=2\nEXDATE:20120601T023000Z").unwrap();
//! assert_eq!(rrule.all().len(), 6);
//! ```
//!
@@ -46,7 +46,8 @@
//! .dtstart(UTC.ymd(2020, 1, 1).and_hms(9, 0, 0))
//! .count(5)
//! .freq(Frequenzy::Daily)
-//! .build();
+//! .build()
+//! .unwrap();
//!
//! // Construct `RRule` from options
//! let mut rrule = RRule::new(options);
@@ -75,7 +76,8 @@
//! .count(4)
//! .freq(Frequenzy::Weekly)
//! .byweekday(vec![Weekday::Tue, Weekday::Wed])
-//! .build();
+//! .build()
+//! .unwrap();
//!
//! // Construct `RRule` from options
//! let mut rrule = RRule::new(rrule_options);
@@ -87,7 +89,8 @@
//! .count(4)
//! .freq(Frequenzy::Weekly)
//! .byweekday(vec![Weekday::Wed])
-//! .build();
+//! .build()
+//! .unwrap();
//!
//! // Construct `RRule` from options
//! let mut exrule = RRule::new(exrule_options);
@@ -137,7 +140,8 @@
//! .dtstart(Berlin.ymd(2020, 1, 1).and_hms(9, 0, 0))
//! .count(4)
//! .freq(Frequenzy::Daily)
-//! .build();
+//! .build()
+//! .unwrap();
//!
//! let mut rrule = RRule::new(rrule_options);
//!
diff --git a/src/options.rs b/src/options.rs
index 2cb017b..7b5c7e3 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -2,6 +2,8 @@ use chrono::prelude::*;
use chrono_tz::Tz;
use crate::datetime::{DTime, get_weekday_val};
use crate::parse_options::parse_options;
+use std::error::Error;
+use std::fmt::{Display, Formatter};
#[derive(Debug, PartialEq, PartialOrd, Clone)]
pub enum Frequenzy {
@@ -200,7 +202,18 @@ impl Options {
self
}
- pub fn build(self) -> ParsedOptions {
+ pub fn build(self) -> Result<ParsedOptions, RRuleParseError> {
parse_options(&self)
}
-} \ No newline at end of file
+}
+
+#[derive(Debug, Clone)]
+pub struct RRuleParseError(pub String);
+
+impl Display for RRuleParseError {
+ fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
+ write!(f, "Encountered parsing error: {}", self.0)
+ }
+}
+
+impl Error for RRuleParseError{} \ No newline at end of file
diff --git a/src/parse_options.rs b/src/parse_options.rs
index 49b7550..e360762 100644
--- a/src/parse_options.rs
+++ b/src/parse_options.rs
@@ -1,9 +1,9 @@
use chrono::prelude::*;
-use crate::options::{ParsedOptions, Frequenzy, Options};
+use crate::options::{ParsedOptions, Frequenzy, Options, RRuleParseError};
use chrono_tz::{Tz, UTC};
// TODO: Validation
-pub fn parse_options(options: &Options) -> ParsedOptions {
+pub fn parse_options(options: &Options) -> Result<ParsedOptions, RRuleParseError> {
let mut default_partial_options = Options::new();
default_partial_options.interval = Some(1);
default_partial_options.freq = Some(Frequenzy::Yearly);
@@ -23,7 +23,7 @@ pub fn parse_options(options: &Options) -> ParsedOptions {
let freq = partial_options.freq.unwrap();
if partial_options.dtstart.is_none() {
- panic!("Dtstart can not be None");
+ return Err(RRuleParseError(String::from("Dtstart can not be None")));
}
if partial_options.wkst.is_none() {
@@ -33,7 +33,7 @@ pub fn parse_options(options: &Options) -> ParsedOptions {
if let Some(bysetpos) = &partial_options.bysetpos {
for pos in bysetpos {
if *pos == 0 || !(*pos >= -366 && *pos <= 366) {
- panic!("bysetpos must be between 1 and 366, or between -366 and -1");
+ return Err(RRuleParseError(String::from("Bysetpos must be between 1 and 366, or between -366 and -1")));
}
}
}
@@ -104,7 +104,7 @@ pub fn parse_options(options: &Options) -> ParsedOptions {
}
- ParsedOptions {
+ Ok(ParsedOptions {
freq,
interval: partial_options.interval.unwrap(),
count: partial_options.count,
@@ -124,7 +124,7 @@ pub fn parse_options(options: &Options) -> ParsedOptions {
bysecond: partial_options.bysecond.unwrap_or(vec![]),
bynweekday: partial_options.bynweekday.unwrap_or(vec![]),
byeaster: partial_options.byeaster,
- }
+ })
}
fn is_some_and_not_empty<T>(v: &Option<Vec<T>>) -> bool {
diff --git a/src/rrulestr.rs b/src/rrulestr.rs
index 452f6b0..ff71516 100644
--- a/src/rrulestr.rs
+++ b/src/rrulestr.rs
@@ -66,16 +66,14 @@ fn from_str_to_freq(s: &str) -> Option<Frequenzy> {
}
}
-fn parse_rrule(line: &str) -> Options {
+fn parse_rrule(line: &str) -> Result<Options, RRuleParseError> {
let stripped_line = if line.starts_with("RRULE:") {
&line[6..]
} else {
line
};
- println!("Stripped line: {}", stripped_line);
let mut options = parse_dtstart(stripped_line).unwrap_or(Options::new());
- println!("Options from dtstart: {:?}", options);
let attrs = RRULE_RE.replace(line, "");
let attrs = attrs.split(";");
@@ -136,7 +134,7 @@ fn parse_rrule(line: &str) -> Options {
options.bysecond = Some(bysecond);
}
"BYWEEKDAY" | "BYDAY" => {
- options.byweekday = Some(parse_weekday(value));
+ options.byweekday = Some(parse_weekday(value)?);
}
"DTSTART" | "TZID" => {
// for backwards compatibility
@@ -151,27 +149,27 @@ fn parse_rrule(line: &str) -> Options {
"BYEASTER" => {
options.byeaster = Some(value.parse::<isize>().unwrap());
}
- _ => panic!("Invalid property: {}", key),
+ _ => return Err(RRuleParseError(format!("Invalid property: {}", key))),
};
}
- options
+ Ok(options)
}
-fn str_to_weekday(d: &str) -> usize {
+fn str_to_weekday(d: &str) -> Result<usize, RRuleParseError> {
match d.to_uppercase().as_str() {
- "MO" => 0,
- "TU" => 1,
- "WE" => 2,
- "TH" => 3,
- "FR" => 4,
- "SA" => 5,
- "SU" => 6,
- _ => panic!("Invalid weekday: {}", d),
+ "MO" => Ok(0),
+ "TU" => Ok(1),
+ "WE" => Ok(2),
+ "TH" => Ok(3),
+ "FR" => Ok(4),
+ "SA" => Ok(5),
+ "SU" => Ok(6),
+ _ => Err(RRuleParseError(format!("Invalid weekday: {}", d))),
}
}
-fn parse_weekday(val: &str) -> Vec<usize> {
+fn parse_weekday(val: &str) -> Result<Vec<usize>, RRuleParseError> {
val.split(",").map(|day| {
if day.len() == 2 {
// MO, TU, ...
@@ -190,12 +188,11 @@ fn parse_weekday(val: &str) -> Vec<usize> {
}).collect()
}
-fn parse_line(rfc_string: &str) -> Option<Options> {
- println!("Parse line: {}", rfc_string);
+fn parse_line(rfc_string: &str) -> Result<Option<Options>, RRuleParseError> {
let re = Regex::new(r"(?m)^\s+|\s+$").unwrap();
let rfc_string = re.replace(rfc_string, "");
if rfc_string.is_empty() {
- return None;
+ return Ok(None);
}
let re = Regex::new(r"(?m)^([A-Z]+?)[:;]").unwrap();
@@ -206,15 +203,15 @@ fn parse_line(rfc_string: &str) -> Option<Options> {
let rfc_string = rfc_string.to_string();
if header.is_none() {
- return Some(parse_rrule(&rfc_string));
+ return Ok(Some(parse_rrule(&rfc_string)?));
}
let header = header.unwrap();
let key = header.get(1).unwrap().as_str();
match key {
- "EXRULE" | "RRULE" => Some(parse_rrule(&rfc_string)),
- "DTSTART" => Some(parse_dtstart(&rfc_string).unwrap()),
- _ => panic!("Unsupported RFC prop {} in {}", key, &rfc_string)
+ "EXRULE" | "RRULE" => Ok(Some(parse_rrule(&rfc_string)?)),
+ "DTSTART" => Ok(Some(parse_dtstart(&rfc_string).unwrap())),
+ _ => Err(RRuleParseError(format!("Unsupported RFC prop {} in {}", key, &rfc_string)))
}
}
@@ -259,16 +256,21 @@ fn extract_name(line: String) -> LineName {
}
}
-fn parse_string(rfc_string: &str) -> Options {
- let options: Vec<Options> = rfc_string.split("\n").map(|l| parse_line(l)).filter(|x| x.is_some())
- .map(|o| o.unwrap())
- .collect();
+fn parse_string(rfc_string: &str) -> Result<Options, RRuleParseError> {
+ let mut options = vec![];
+ for line in rfc_string.split("\n") {
+ let parsed_line = parse_line(line)?;
+ if let Some(parsed_line) = parsed_line {
+ options.push(parsed_line);
+ }
+
+ }
if options.len() == 1 {
- return options[0].clone();
+ return Ok(options[0].clone());
}
- Options::concat(&options[0], &options[1])
+ Ok(Options::concat(&options[0], &options[1]))
}
#[derive(Debug)]
@@ -281,7 +283,7 @@ struct ParsedInput {
tzid: Option<Tz>,
}
-fn parse_input(s: &str) -> ParsedInput {
+fn parse_input(s: &str) -> Result<ParsedInput, RRuleParseError> {
let mut rrule_vals = vec![];
let mut rdate_vals = vec![];
let mut exrule_vals = vec![];
@@ -301,23 +303,23 @@ fn parse_input(s: &str) -> ParsedInput {
match parsed_line.name.to_uppercase().as_str() {
"RRULE" => {
if !parsed_line.params.is_empty() {
- panic!("Unsupported RRULE value");
+ return Err(RRuleParseError(String::from("Unsupported RRULE value")));
}
if parsed_line.value.is_empty() {
continue;
}
- rrule_vals.push(parse_string(line));
+ rrule_vals.push(parse_string(line)?);
}
"EXRULE" => {
if !parsed_line.params.is_empty() {
- panic!("Unsupported EXRULE value");
+ return Err(RRuleParseError(String::from("Unsupported EXRULE value")));
}
if parsed_line.value.is_empty() {
continue;
}
-
- exrule_vals.push(parse_string(&parsed_line.value));
+ // TODO: why is it parsed_line.value here and line for RRULE ?? Do some testing
+ exrule_vals.push(parse_string(&parsed_line.value)?);
}
"RDATE" => {
let re = Regex::new(r"(?m)RDATE(?:;TZID=([^:=]+))?").unwrap();
@@ -340,28 +342,29 @@ fn parse_input(s: &str) -> ParsedInput {
exdate_vals.append(&mut parse_rdate(&parsed_line.value, parsed_line.params, &tz));
}
"DTSTART" => (),
- _ => panic!("Unsupported property: {}", parsed_line.name)
+ _ => return Err(RRuleParseError(format!("Unsupported property: {}", parsed_line.name)))
}
}
- return ParsedInput {
+ return Ok(ParsedInput {
dtstart,
tzid,
rrule_vals,
rdate_vals,
exrule_vals,
exdate_vals
- }
+ })
}
-fn validate_date_param(params: Vec<&str>){
+fn validate_date_param(params: Vec<&str>) -> Result<(), RRuleParseError>{
let re = Regex::new(r"(?m)(VALUE=DATE(-TIME)?)|(TZID=)").unwrap();
for param in &params {
if re.captures(param).unwrap().len() == 0 {
- panic!("Unsupported RDATE/EXDATE parm: {}", param);
+ return Err(RRuleParseError(format!("Unsupported RDATE/EXDATE parm: {}", param)));
}
}
+ Ok(())
}
// ! works needs to be done here
@@ -377,7 +380,7 @@ fn parse_rdate(rdateval: &str, params: Vec<String>, tz: &Tz) -> Vec<DTime> {
}
-pub fn build_rruleset(s: &str) -> RRuleSet {
+pub fn build_rruleset(s: &str) -> Result<RRuleSet, RRuleParseError> {
let ParsedInput {
mut rrule_vals,
rdate_vals,
@@ -386,66 +389,55 @@ pub fn build_rruleset(s: &str) -> RRuleSet {
dtstart,
tzid,
..
- } = parse_input(s);
+ } = parse_input(s)?;
let mut rset = RRuleSet::new();
+ rset.dtstart = dtstart;
- if !rrule_vals.is_empty() ||
- !rdate_vals.is_empty() ||
- !exrule_vals.is_empty() ||
- !exdate_vals.is_empty() {
+ for rruleval in rrule_vals.iter_mut() {
+ rruleval.tzid = tzid.clone();
+ rruleval.dtstart = dtstart;
+ let parsed_opts = parse_options(&rruleval)?;
+ let rrule = RRule::new(parsed_opts);
+ rset.rrule(rrule);
+ }
- rset.dtstart = dtstart;
- // rset.tzid(tzid || undefined);
-
- for rruleval in rrule_vals.iter_mut() {
- rruleval.tzid = tzid.clone();
- rruleval.dtstart = dtstart;
- let parsed_opts = parse_options(&rruleval);
- let rrule = RRule::new(parsed_opts);
- rset.rrule(rrule);
- }
-
- for rdate in rdate_vals {
- rset.rdate(rdate);
- }
+ for rdate in rdate_vals {
+ rset.rdate(rdate);
+ }
- for exrule in exrule_vals.iter_mut() {
- exrule.tzid = tzid.clone();
- exrule.dtstart = dtstart;
+ for exrule in exrule_vals.iter_mut() {
+ exrule.tzid = tzid.clone();
+ exrule.dtstart = dtstart;
- let parsed_opts = parse_options(&exrule);
- let exrule = RRule::new(parsed_opts);
- rset.rrule(exrule);
- }
-
- for exdate in exdate_vals {
- rset.exdate(exdate);
- }
-
- // if (options.compatible && options.dtstart) rset.rdate(dtstart!);
- return rset;
+ let parsed_opts = parse_options(&exrule)?;
+ let exrule = RRule::new(parsed_opts);
+ rset.rrule(exrule);
+ }
+
+ for exdate in exdate_vals {
+ rset.exdate(exdate);
}
- rset
+ Ok(rset)
}
-pub fn build_rrule(s: &str) -> RRule {
+pub fn build_rrule(s: &str) -> Result<RRule, RRuleParseError> {
let ParsedInput {
mut rrule_vals,
tzid,
dtstart,
..
- } = parse_input(s);
+ } = parse_input(s)?;
rrule_vals[0].tzid = tzid;
rrule_vals[0].dtstart = dtstart;
- let parsed_opts = parse_options(&rrule_vals[0]);
+ let parsed_opts = parse_options(&rrule_vals[0])?;
- RRule::new(parsed_opts)
+ Ok(RRule::new(parsed_opts))
}
@@ -456,14 +448,14 @@ mod test {
#[test]
fn it_works_1() {
- let options = build_rruleset("DTSTART:19970902T090000Z\nRRULE:FREQ=YEARLY;COUNT=3\n");
+ let options = build_rruleset("DTSTART:19970902T090000Z\nRRULE:FREQ=YEARLY;COUNT=3\n").unwrap();
println!("?????????????=================?????????????");
println!("{:?}", options);
}
#[test]
fn it_works_2() {
- let mut options = build_rrule("DTSTART:20120201T093000Z\nRRULE:FREQ=WEEKLY;INTERVAL=5;UNTIL=20130130T230000Z;BYDAY=MO,FR");
+ let mut options = build_rrule("DTSTART:20120201T093000Z\nRRULE:FREQ=WEEKLY;INTERVAL=5;UNTIL=20130130T230000Z;BYDAY=MO,FR").unwrap();
println!("?????????????=================?????????????");
println!("{:?}", options);
println!("?????????????=== ALLL ==============?????????????");
@@ -472,7 +464,7 @@ mod test {
#[test]
fn it_works_3() {
- let mut options = build_rruleset("RRULE:UNTIL=19990404T110000Z;DTSTART;TZID=America/Denver:19990104T110000Z;FREQ=WEEKLY;BYDAY=TU,WE");
+ let mut options = build_rruleset("RRULE:UNTIL=19990404T110000Z;DTSTART;TZID=America/Denver:19990104T110000Z;FREQ=WEEKLY;BYDAY=TU,WE").unwrap();
println!("?????????????=================?????????????");
println!("{:?}", options);
let tzid: Tz = "America/Denver".parse().unwrap();
@@ -484,7 +476,7 @@ mod test {
#[test]
fn it_works_4() {
- let mut set = build_rruleset("DTSTART:20120201T120000Z\nRRULE:FREQ=DAILY;COUNT=5\nEXDATE;TZID=Europe/Berlin:20120202T130000Z,20120203T130000Z");
+ let mut set = build_rruleset("DTSTART:20120201T120000Z\nRRULE:FREQ=DAILY;COUNT=5\nEXDATE;TZID=Europe/Berlin:20120202T130000Z,20120203T130000Z").unwrap();
println!("?????????????=================??======?????????????");
println!("{:?}", set.exdate.iter().map(|d| d.timestamp()).collect::<Vec<i64>>());
let all = set.all();