Skip to main content

hypertask_engine/
engine.rs

1use crate::error::*;
2use crate::id::Id;
3use crate::prop::Prop;
4use crate::rpn::StackMachine;
5use crate::tag::Tag;
6use crate::task::{FinalisedTask, Task};
7use chrono::prelude::*;
8
9#[derive(Debug, Deserialize, Serialize)]
10pub enum Mutation {
11    SetProp(Prop),
12    SetTag(Tag),
13}
14
15#[derive(Debug, Deserialize, Serialize)]
16pub enum Query {
17    Id(Id),
18    Tag(Tag),
19}
20
21#[derive(Debug, Deserialize, Serialize)]
22pub enum Command {
23    Create(Vec<Mutation>),
24    Read(Vec<Query>),
25    Update(Vec<Query>, Vec<Mutation>),
26    Delete(Vec<Query>),
27}
28
29pub trait HyperTaskEngineContext<TaskIterator: Iterator<Item = HyperTaskResult<Task>>> {
30    fn finalize_mutations(&self) -> HyperTaskResult<()>;
31    fn generate_id(&mut self) -> String;
32    fn get_now(&self) -> DateTime<Utc>;
33    fn get_stack_machine(&self) -> HyperTaskResult<StackMachine>;
34    fn get_task_iterator(&self) -> HyperTaskResult<TaskIterator>;
35    fn put_task(&mut self, task: &Task) -> HyperTaskResult<()>;
36}
37
38struct TaskEngine<
39    InputIterator: Iterator<Item = HyperTaskResult<Task>>,
40    Context: HyperTaskEngineContext<InputIterator>,
41> {
42    command: Command,
43    input_iterator: InputIterator,
44    context: Context,
45    now: DateTime<Utc>,
46    done: bool,
47}
48
49impl<
50        InputIterator: Iterator<Item = HyperTaskResult<Task>>,
51        Context: HyperTaskEngineContext<InputIterator>,
52    > TaskEngine<InputIterator, Context>
53{
54    pub fn new(command: Command, context: Context) -> HyperTaskResult<Self> {
55        let now = context.get_now();
56        let input_iterator = context.get_task_iterator()?;
57
58        Ok(Self {
59            command,
60            input_iterator,
61            context,
62            now,
63            done: false,
64        })
65    }
66
67    fn yield_next_task(&mut self) -> Option<HyperTaskResult<Task>> {
68        if self.done {
69            return None;
70        }
71
72        match &self.command {
73            Command::Read(queries) => match self.input_iterator.next()? {
74                Err(e) => Some(Err(e)),
75                Ok(next_task) => {
76                    if queries.is_empty() || next_task.satisfies_queries(&queries) {
77                        Some(Ok(next_task))
78                    } else {
79                        self.yield_next_task()
80                    }
81                }
82            },
83
84            Command::Create(mutations) => {
85                let mut new_task = Task::generate(&mut self.context);
86                new_task.apply_mutations(mutations, &self.now);
87
88                self.done = true;
89
90                Some(self.context.put_task(&new_task).map(|_| new_task))
91            }
92
93            Command::Update(queries, mutations) => match self.input_iterator.next()? {
94                Err(e) => Some(Err(e)),
95                Ok(mut next_task) => {
96                    if next_task.satisfies_queries(&queries) {
97                        next_task.apply_mutations(mutations, &self.now);
98
99                        Some(self.context.put_task(&next_task).map(|_| next_task))
100                    } else {
101                        self.yield_next_task()
102                    }
103                }
104            },
105
106            Command::Delete(_) => {
107                panic!("fuck you");
108            }
109        }
110    }
111}
112
113impl<
114        InputIterator: Iterator<Item = HyperTaskResult<Task>>,
115        Context: HyperTaskEngineContext<InputIterator>,
116    > Iterator for TaskEngine<InputIterator, Context>
117{
118    type Item = HyperTaskResult<Task>;
119
120    fn next(&mut self) -> Option<Self::Item> {
121        let next_task = self.yield_next_task();
122
123        if next_task.is_some() {
124            return next_task;
125        }
126
127        if let Command::Read(_) = self.command {
128            return None;
129        }
130
131        match self.context.finalize_mutations() {
132            Ok(_) => None,
133            Err(e) => Some(Err(e)),
134        }
135    }
136}
137
138pub fn run<InputIterator, Context>(
139    command: Command,
140    context: Context,
141) -> HyperTaskResult<Vec<FinalisedTask>>
142where
143    InputIterator: Iterator<Item = HyperTaskResult<Task>>,
144    Context: HyperTaskEngineContext<InputIterator>,
145{
146    let mut stack_machine = context.get_stack_machine()?;
147
148    let mut task_collection = TaskEngine::new(command, context)?
149        .map(|task_result| task_result.and_then(|task| task.finalise(&mut stack_machine)))
150        .filter(|finalised_task| {
151            if let Ok(ft) = finalised_task {
152                (ft.get_score() - 0.0).abs() > std::f64::EPSILON
153            } else {
154                true
155            }
156        })
157        .collect::<HyperTaskResult<Vec<FinalisedTask>>>()?;
158
159    task_collection.sort_unstable();
160
161    Ok(task_collection)
162}