Simplify entries and entry dates
This commit is contained in:
parent
63f5c6fba9
commit
85d2c4bf89
4 changed files with 125 additions and 92 deletions
|
|
@ -5,6 +5,7 @@ use chrono::NaiveDate;
|
|||
use crate::files::commands::{BirthdaySpec, Command, Done, Note, Span, Spec, Statement, Task};
|
||||
use crate::files::{Source, SourcedCommand};
|
||||
|
||||
use super::date::Dates;
|
||||
use super::{DateRange, Entry, EntryKind, Error, Result};
|
||||
|
||||
mod birthday;
|
||||
|
|
@ -51,22 +52,26 @@ impl<'a> CommandState<'a> {
|
|||
|
||||
// Helper functions
|
||||
|
||||
fn title(&self) -> String {
|
||||
self.command.command.title().to_string()
|
||||
fn kind(&self) -> EntryKind {
|
||||
match self.command.command {
|
||||
Command::Task(_) => EntryKind::Task,
|
||||
Command::Note(_) => EntryKind::Note,
|
||||
}
|
||||
}
|
||||
|
||||
fn desc(&self) -> Vec<String> {
|
||||
self.command.command.desc().to_vec()
|
||||
}
|
||||
|
||||
fn source(&self) -> Source {
|
||||
self.command.source
|
||||
fn last_done(&self) -> Option<NaiveDate> {
|
||||
match self.command.command {
|
||||
Command::Task(task) => task.done.iter().map(|done| done.done_at).max(),
|
||||
Command::Note(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Add an entry, respecting [`Self::from`] and [`Self::until`]. Does not
|
||||
/// overwrite existing entries if a root date is specified.
|
||||
fn add(&mut self, entry: Entry) {
|
||||
if let Some(root) = entry.root {
|
||||
fn add(&mut self, kind: EntryKind, dates: Option<Dates>) {
|
||||
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 root < from {
|
||||
return;
|
||||
|
|
@ -85,9 +90,10 @@ impl<'a> CommandState<'a> {
|
|||
|
||||
/// Add an entry, ignoring [`Self::from`] and [`Self::until`]. Always
|
||||
/// overwrites existing entries if a root date is specified.
|
||||
fn add_forced(&mut self, entry: Entry) {
|
||||
if let Some(root) = entry.root {
|
||||
self.dated.insert(root, entry);
|
||||
fn add_forced(&mut self, kind: EntryKind, dates: Option<Dates>) {
|
||||
let entry = Entry::new(self.command.source, kind, dates);
|
||||
if let Some(dates) = dates {
|
||||
self.dated.insert(dates.root(), entry);
|
||||
} else {
|
||||
self.undated.push(entry);
|
||||
}
|
||||
|
|
@ -95,20 +101,37 @@ impl<'a> CommandState<'a> {
|
|||
|
||||
// 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<()> {
|
||||
for statement in &task.statements {
|
||||
self.eval_statement(statement)?;
|
||||
if Self::has_date_stmt(&task.statements) {
|
||||
for statement in &task.statements {
|
||||
self.eval_statement(statement)?;
|
||||
}
|
||||
} else {
|
||||
self.add(self.kind(), None);
|
||||
}
|
||||
|
||||
for done in &task.done {
|
||||
self.eval_done(done);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn eval_note(&mut self, note: &Note) -> Result<()> {
|
||||
for statement in ¬e.statements {
|
||||
self.eval_statement(statement)?;
|
||||
if Self::has_date_stmt(¬e.statements) {
|
||||
for statement in ¬e.statements {
|
||||
self.eval_statement(statement)?;
|
||||
}
|
||||
} else {
|
||||
self.add(self.kind(), None);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -150,13 +173,9 @@ impl<'a> CommandState<'a> {
|
|||
}
|
||||
|
||||
fn eval_done(&mut self, done: &Done) {
|
||||
self.add_forced(Entry {
|
||||
kind: EntryKind::TaskDone(done.done_at),
|
||||
title: self.title(),
|
||||
desc: self.desc(),
|
||||
source: self.source(),
|
||||
dates: done.date.map(|date| date.into()),
|
||||
root: done.date.map(|date| date.root()),
|
||||
});
|
||||
self.add_forced(
|
||||
EntryKind::TaskDone(done.done_at),
|
||||
done.date.map(|date| date.into()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use crate::eval::date::Dates;
|
|||
use crate::files::commands::BirthdaySpec;
|
||||
|
||||
use super::super::command::CommandState;
|
||||
use super::super::{Entry, EntryKind};
|
||||
use super::super::EntryKind;
|
||||
|
||||
impl<'a> CommandState<'a> {
|
||||
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
|
||||
// time soon.
|
||||
for year in self.range.years() {
|
||||
let mut title = self.title();
|
||||
|
||||
if spec.year_known {
|
||||
let age = if spec.year_known {
|
||||
let age = year - spec.date.year();
|
||||
if age < 0 {
|
||||
continue;
|
||||
}
|
||||
title.push_str(&format!(" ({})", age));
|
||||
}
|
||||
Some(age)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let kind = EntryKind::Birthday(age);
|
||||
|
||||
if let Some(date) = spec.date.with_year(year) {
|
||||
self.add(Entry {
|
||||
kind: EntryKind::Birthday,
|
||||
title,
|
||||
desc: self.desc(),
|
||||
source: self.source(),
|
||||
root: Some(date),
|
||||
dates: Some(Dates::new(date, date)),
|
||||
});
|
||||
self.add(EntryKind::Birthday(age), Some(Dates::new(date, date)));
|
||||
} else {
|
||||
assert_eq!(spec.date.month(), 2);
|
||||
assert_eq!(spec.date.day(), 29);
|
||||
|
||||
let first = NaiveDate::from_ymd(year, 2, 28);
|
||||
let second = NaiveDate::from_ymd(year, 3, 1);
|
||||
self.add(Entry {
|
||||
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)),
|
||||
});
|
||||
self.add(kind, Some(Dates::new(first, second)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,76 +3,93 @@ use chrono::NaiveDate;
|
|||
use crate::files::commands::{DoneDate, Time};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Times {
|
||||
start: Time,
|
||||
end: Time,
|
||||
}
|
||||
|
||||
impl Times {
|
||||
pub fn start(&self) -> Time {
|
||||
self.start
|
||||
}
|
||||
|
||||
pub fn end(&self) -> Time {
|
||||
self.end
|
||||
}
|
||||
struct Times {
|
||||
root: Time,
|
||||
other: Time,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Dates {
|
||||
start: NaiveDate,
|
||||
end: NaiveDate,
|
||||
root: NaiveDate,
|
||||
other: NaiveDate,
|
||||
times: Option<Times>,
|
||||
}
|
||||
|
||||
impl Dates {
|
||||
pub fn new(start: NaiveDate, end: NaiveDate) -> Self {
|
||||
assert!(start <= end);
|
||||
pub fn new(root: NaiveDate, other: NaiveDate) -> Self {
|
||||
Self {
|
||||
start,
|
||||
end,
|
||||
root,
|
||||
other,
|
||||
times: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_time(
|
||||
start: NaiveDate,
|
||||
start_time: Time,
|
||||
end: NaiveDate,
|
||||
end_time: Time,
|
||||
root: NaiveDate,
|
||||
root_time: Time,
|
||||
other: NaiveDate,
|
||||
other_time: Time,
|
||||
) -> Self {
|
||||
assert!(start <= end);
|
||||
if start == end {
|
||||
assert!(start_time <= end_time);
|
||||
}
|
||||
Self {
|
||||
start,
|
||||
end,
|
||||
root,
|
||||
other,
|
||||
times: Some(Times {
|
||||
start: start_time,
|
||||
end: end_time,
|
||||
root: root_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 {
|
||||
self.start
|
||||
self.start_end().0
|
||||
}
|
||||
|
||||
pub fn end(&self) -> NaiveDate {
|
||||
self.start
|
||||
self.start_end().1
|
||||
}
|
||||
|
||||
pub fn times(&self) -> Option<Times> {
|
||||
self.times
|
||||
pub fn start_end_time(&self) -> Option<(Time, Time)> {
|
||||
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> {
|
||||
self.times.as_ref().map(Times::start)
|
||||
self.start_end_time().map(|times| times.0)
|
||||
}
|
||||
|
||||
pub fn end_time(&self) -> Option<Time> {
|
||||
self.times.as_ref().map(Times::end)
|
||||
self.start_end_time().map(|times| times.1)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,19 +10,29 @@ pub enum EntryKind {
|
|||
Task,
|
||||
TaskDone(NaiveDate),
|
||||
Note,
|
||||
Birthday, // TODO Add age gere instead of in title
|
||||
Birthday(Option<i32>),
|
||||
}
|
||||
|
||||
/// A single instance of a command.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Entry {
|
||||
pub kind: EntryKind,
|
||||
pub title: String,
|
||||
pub desc: Vec<String>,
|
||||
|
||||
pub source: Source,
|
||||
pub kind: EntryKind,
|
||||
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
|
||||
|
|
@ -57,7 +67,7 @@ impl Entries {
|
|||
}
|
||||
|
||||
fn is_rooted(&self, entry: &Entry) -> bool {
|
||||
match entry.root {
|
||||
match entry.root() {
|
||||
Some(date) => self.range.contains(date),
|
||||
None => false,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue