///|
/// Create an empty event context.
pub fn EventContext::new() -> EventContext {
{ entries: [] }
}
///|
/// Create a fixed-date event definition.
pub fn EventSpec::fixed(date : Date) -> EventSpec {
Fixed(date)
}
///|
/// Create an event offset by calendar days from another event.
pub fn EventSpec::after(name : String, offset : Int) -> EventSpec {
After(name, offset)
}
///|
/// Add a definition without mutating the original context.
pub fn EventContext::insert(
self : EventContext,
name : String,
spec : EventSpec,
) -> Result[EventContext, EventError] {
if name.length() == 0 {
return Err(EventDuplicate(name))
}
for entry in self.entries {
if entry.name == name {
return Err(EventDuplicate(name))
}
}
match spec {
After(_, offset) if offset < 0 => Err(EventNegativeOffset(name))
_ => {
let entries = self.entries.copy()
entries.push({ name, spec })
Ok({ entries, })
}
}
}
///|
/// Resolve a named event and all of its dependencies.
pub fn EventContext::resolve(
self : EventContext,
name : String,
) -> Result[Date, EventError] {
resolve_event(self, name, [])
}
///|
fn find_event(context : EventContext, name : String) -> EventSpec? {
for entry in context.entries {
if entry.name == name {
return Some(entry.spec)
}
}
None
}
///|
fn contains_name(names : Array[String], name : String) -> Bool {
for item in names {
if item == name {
return true
}
}
false
}
///|
fn resolve_event(
context : EventContext,
name : String,
stack : Array[String],
) -> Result[Date, EventError] {
if contains_name(stack, name) {
return Err(EventCycle(name))
}
let spec = match find_event(context, name) {
Some(value) => value
None => return Err(EventMissing(name))
}
let next_stack = stack.copy()
next_stack.push(name)
match spec {
Fixed(date) => Ok(date)
After(parent, offset) =>
match resolve_event(context, parent, next_stack) {
Ok(date) =>
match date.try_add_days(offset) {
Ok(result) => Ok(result)
Err(error) => Err(EventDateFailure(error))
}
Err(error) => Err(error)
}
}
}
///|
/// Create a deadline that starts at a named event.
pub fn EventDeadline::new(
event~ : String,
rule~ : DateRule,
start_rule~ : StartDateRule,
calendar~ : Calendar,
extension~ : ExtensionPolicy,
) -> EventDeadline {
{ event, rule, start_rule, calendar, extension }
}
///|
/// Resolve the event and run the standard deadline engine.
pub fn EventDeadline::calculate(
self : EventDeadline,
context : EventContext,
) -> Result[DeadlineResult, EventDeadlineError] {
let start = match context.resolve(self.event) {
Ok(date) => date
Err(error) => return Err(Event(error))
}
let deadline = Deadline::new(
start_date=start,
rule=self.rule,
start_rule=self.start_rule,
calendar=self.calendar,
extension=self.extension,
)
match deadline.calculate() {
Ok(result) => Ok(result)
Err(error) => Err(Deadline(error))
}
}