Simplify entries and entry dates

This commit is contained in:
Joscha 2021-12-05 17:06:15 +01:00
parent 63f5c6fba9
commit 85d2c4bf89
4 changed files with 125 additions and 92 deletions

View file

@ -5,6 +5,7 @@ use chrono::NaiveDate;
use crate::files::commands::{BirthdaySpec, Command, Done, Note, Span, Spec, Statement, Task}; use crate::files::commands::{BirthdaySpec, Command, Done, Note, Span, Spec, Statement, Task};
use crate::files::{Source, SourcedCommand}; use crate::files::{Source, SourcedCommand};
use super::date::Dates;
use super::{DateRange, Entry, EntryKind, Error, Result}; use super::{DateRange, Entry, EntryKind, Error, Result};
mod birthday; mod birthday;
@ -51,22 +52,26 @@ impl<'a> CommandState<'a> {
// Helper functions // Helper functions
fn title(&self) -> String { fn kind(&self) -> EntryKind {
self.command.command.title().to_string() match self.command.command {
Command::Task(_) => EntryKind::Task,
Command::Note(_) => EntryKind::Note,
}
} }
fn desc(&self) -> Vec<String> { fn last_done(&self) -> Option<NaiveDate> {
self.command.command.desc().to_vec() match self.command.command {
} Command::Task(task) => task.done.iter().map(|done| done.done_at).max(),
Command::Note(_) => None,
fn source(&self) -> Source { }
self.command.source
} }
/// Add an entry, respecting [`Self::from`] and [`Self::until`]. Does not /// Add an entry, respecting [`Self::from`] and [`Self::until`]. Does not
/// overwrite existing entries if a root date is specified. /// overwrite existing entries if a root date is specified.
fn add(&mut self, entry: Entry) { fn add(&mut self, kind: EntryKind, dates: Option<Dates>) {
if let Some(root) = entry.root { let entry = Entry::new(self.command.source, kind, dates);
if let Some(dates) = dates {
let root = dates.root();
if let Some(from) = self.from { if let Some(from) = self.from {
if root < from { if root < from {
return; return;
@ -85,9 +90,10 @@ impl<'a> CommandState<'a> {
/// Add an entry, ignoring [`Self::from`] and [`Self::until`]. Always /// Add an entry, ignoring [`Self::from`] and [`Self::until`]. Always
/// overwrites existing entries if a root date is specified. /// overwrites existing entries if a root date is specified.
fn add_forced(&mut self, entry: Entry) { fn add_forced(&mut self, kind: EntryKind, dates: Option<Dates>) {
if let Some(root) = entry.root { let entry = Entry::new(self.command.source, kind, dates);
self.dated.insert(root, entry); if let Some(dates) = dates {
self.dated.insert(dates.root(), entry);
} else { } else {
self.undated.push(entry); self.undated.push(entry);
} }
@ -95,20 +101,37 @@ impl<'a> CommandState<'a> {
// Actual evaluation // Actual evaluation
fn has_date_stmt(statements: &[Statement]) -> bool {
statements
.iter()
.any(|s| matches!(s, Statement::Date(_) | Statement::BDate(_)))
}
fn eval_task(&mut self, task: &Task) -> Result<()> { fn eval_task(&mut self, task: &Task) -> Result<()> {
for statement in &task.statements { if Self::has_date_stmt(&task.statements) {
self.eval_statement(statement)?; for statement in &task.statements {
self.eval_statement(statement)?;
}
} else {
self.add(self.kind(), None);
} }
for done in &task.done { for done in &task.done {
self.eval_done(done); self.eval_done(done);
} }
Ok(()) Ok(())
} }
fn eval_note(&mut self, note: &Note) -> Result<()> { fn eval_note(&mut self, note: &Note) -> Result<()> {
for statement in &note.statements { if Self::has_date_stmt(&note.statements) {
self.eval_statement(statement)?; for statement in &note.statements {
self.eval_statement(statement)?;
}
} else {
self.add(self.kind(), None);
} }
Ok(()) Ok(())
} }
@ -150,13 +173,9 @@ impl<'a> CommandState<'a> {
} }
fn eval_done(&mut self, done: &Done) { fn eval_done(&mut self, done: &Done) {
self.add_forced(Entry { self.add_forced(
kind: EntryKind::TaskDone(done.done_at), EntryKind::TaskDone(done.done_at),
title: self.title(), done.date.map(|date| date.into()),
desc: self.desc(), );
source: self.source(),
dates: done.date.map(|date| date.into()),
root: done.date.map(|date| date.root()),
});
} }
} }

View file

@ -4,7 +4,7 @@ use crate::eval::date::Dates;
use crate::files::commands::BirthdaySpec; use crate::files::commands::BirthdaySpec;
use super::super::command::CommandState; use super::super::command::CommandState;
use super::super::{Entry, EntryKind}; use super::super::EntryKind;
impl<'a> CommandState<'a> { impl<'a> CommandState<'a> {
pub fn eval_birthday_spec(&mut self, spec: &BirthdaySpec) { pub fn eval_birthday_spec(&mut self, spec: &BirthdaySpec) {
@ -12,39 +12,26 @@ impl<'a> CommandState<'a> {
// but I don't think that kind of optimization will be necessary any // but I don't think that kind of optimization will be necessary any
// time soon. // time soon.
for year in self.range.years() { for year in self.range.years() {
let mut title = self.title(); let age = if spec.year_known {
if spec.year_known {
let age = year - spec.date.year(); let age = year - spec.date.year();
if age < 0 { if age < 0 {
continue; continue;
} }
title.push_str(&format!(" ({})", age)); Some(age)
} } else {
None
};
let kind = EntryKind::Birthday(age);
if let Some(date) = spec.date.with_year(year) { if let Some(date) = spec.date.with_year(year) {
self.add(Entry { self.add(EntryKind::Birthday(age), Some(Dates::new(date, date)));
kind: EntryKind::Birthday,
title,
desc: self.desc(),
source: self.source(),
root: Some(date),
dates: Some(Dates::new(date, date)),
});
} else { } else {
assert_eq!(spec.date.month(), 2); assert_eq!(spec.date.month(), 2);
assert_eq!(spec.date.day(), 29); assert_eq!(spec.date.day(), 29);
let first = NaiveDate::from_ymd(year, 2, 28); let first = NaiveDate::from_ymd(year, 2, 28);
let second = NaiveDate::from_ymd(year, 3, 1); let second = NaiveDate::from_ymd(year, 3, 1);
self.add(Entry { self.add(kind, Some(Dates::new(first, second)));
kind: EntryKind::Birthday,
title,
desc: self.desc(),
source: self.source(),
root: Some(first), // This doesn't matter too much
dates: Some(Dates::new(first, second)),
});
} }
} }
} }

View file

@ -3,76 +3,93 @@ use chrono::NaiveDate;
use crate::files::commands::{DoneDate, Time}; use crate::files::commands::{DoneDate, Time};
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Times { struct Times {
start: Time, root: Time,
end: Time, other: Time,
}
impl Times {
pub fn start(&self) -> Time {
self.start
}
pub fn end(&self) -> Time {
self.end
}
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Dates { pub struct Dates {
start: NaiveDate, root: NaiveDate,
end: NaiveDate, other: NaiveDate,
times: Option<Times>, times: Option<Times>,
} }
impl Dates { impl Dates {
pub fn new(start: NaiveDate, end: NaiveDate) -> Self { pub fn new(root: NaiveDate, other: NaiveDate) -> Self {
assert!(start <= end);
Self { Self {
start, root,
end, other,
times: None, times: None,
} }
} }
pub fn new_with_time( pub fn new_with_time(
start: NaiveDate, root: NaiveDate,
start_time: Time, root_time: Time,
end: NaiveDate, other: NaiveDate,
end_time: Time, other_time: Time,
) -> Self { ) -> Self {
assert!(start <= end);
if start == end {
assert!(start_time <= end_time);
}
Self { Self {
start, root,
end, other,
times: Some(Times { times: Some(Times {
start: start_time, root: root_time,
end: end_time, other: other_time,
}), }),
} }
} }
pub fn root(&self) -> NaiveDate {
self.root
}
pub fn other(&self) -> NaiveDate {
self.other
}
pub fn root_time(&self) -> Option<Time> {
self.times.map(|times| times.root)
}
pub fn other_time(&self) -> Option<Time> {
self.times.map(|times| times.other)
}
fn start_end(&self) -> (NaiveDate, NaiveDate) {
if self.root <= self.other {
(self.root, self.other)
} else {
(self.other, self.root)
}
}
pub fn start(&self) -> NaiveDate { pub fn start(&self) -> NaiveDate {
self.start self.start_end().0
} }
pub fn end(&self) -> NaiveDate { pub fn end(&self) -> NaiveDate {
self.start self.start_end().1
} }
pub fn times(&self) -> Option<Times> { pub fn start_end_time(&self) -> Option<(Time, Time)> {
self.times if let Some(times) = self.times {
if self.root < self.other || (self.root == self.other && times.root <= times.other) {
Some((times.root, times.other))
} else {
Some((times.other, times.root))
}
} else {
None
}
} }
pub fn start_time(&self) -> Option<Time> { pub fn start_time(&self) -> Option<Time> {
self.times.as_ref().map(Times::start) self.start_end_time().map(|times| times.0)
} }
pub fn end_time(&self) -> Option<Time> { pub fn end_time(&self) -> Option<Time> {
self.times.as_ref().map(Times::end) self.start_end_time().map(|times| times.1)
} }
} }

View file

@ -10,19 +10,29 @@ pub enum EntryKind {
Task, Task,
TaskDone(NaiveDate), TaskDone(NaiveDate),
Note, Note,
Birthday, // TODO Add age gere instead of in title Birthday(Option<i32>),
} }
/// A single instance of a command. /// A single instance of a command.
#[derive(Debug)] #[derive(Debug, Clone, Copy)]
pub struct Entry { pub struct Entry {
pub kind: EntryKind,
pub title: String,
pub desc: Vec<String>,
pub source: Source, pub source: Source,
pub kind: EntryKind,
pub dates: Option<Dates>, pub dates: Option<Dates>,
pub root: Option<NaiveDate>, }
impl Entry {
pub fn new(source: Source, kind: EntryKind, dates: Option<Dates>) -> Self {
Self {
source,
kind,
dates,
}
}
pub fn root(&self) -> Option<NaiveDate> {
self.dates.map(|dates| dates.root())
}
} }
/// Mode that determines how entries are filtered when they are added to /// Mode that determines how entries are filtered when they are added to
@ -57,7 +67,7 @@ impl Entries {
} }
fn is_rooted(&self, entry: &Entry) -> bool { fn is_rooted(&self, entry: &Entry) -> bool {
match entry.root { match entry.root() {
Some(date) => self.range.contains(date), Some(date) => self.range.contains(date),
None => false, None => false,
} }