From 79c3174f4fe7bb4f5777ddb1dadd6fadd5da96b3 Mon Sep 17 00:00:00 2001 From: xeruf <27jf@pm.me> Date: Fri, 26 Jul 2024 11:07:47 +0300 Subject: [PATCH] feat: enable state filtering --- README.md | 4 +++- src/main.rs | 44 ++++++++++++++++++++++++++++++++------------ src/task.rs | 31 +++++++++++++++++++++---------- 3 files changed, 56 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 2bb81e1..63f8aa2 100644 --- a/README.md +++ b/README.md @@ -36,10 +36,12 @@ Dots can be repeated to move to parent tasks - `:[IND][COL]` - add / remove property column COL to IND or end - `>[TEXT]` - Complete active task and move to parent, with optional state description - `<[TEXT]` - Close active task and move to parent, with optional state description -- `-TEXT` - add text note (comment / description) - `#TAG` - filter by tag +- `?TAG` - filter by state (type or description) +- `-TEXT` - add text note (comment / description) State descriptions can be used for example for Kanban columns. +An active tag or state filter will also create new tasks with those corresponding attributes. ### Available Columns diff --git a/src/main.rs b/src/main.rs index a63771b..60652a0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -139,23 +139,24 @@ async fn main() { println!(); loop { - while let Ok(notification) = notifications.try_recv() { - if let RelayPoolNotification::Event { - subscription_id, - event, - .. - } = notification - { - print_event(&event); - tasks.add(*event); - } - } tasks.print_current_tasks(); print!(" {}) ", tasks.taskpath(tasks.get_position())); stdout().flush().unwrap(); match stdin().lines().next() { Some(Ok(input)) => { + while let Ok(notification) = notifications.try_recv() { + if let RelayPoolNotification::Event { + subscription_id, + event, + .. + } = notification + { + print_event(&event); + tasks.add(*event); + } + } + let mut iter = input.chars(); let op = iter.next(); match op { @@ -188,6 +189,25 @@ async fn main() { } }, + Some('?') => { + let arg = &input[1..]; + tasks.move_to(tasks.get_position()); + tasks.set_filter( + tasks + .current_tasks() + .into_iter() + .filter(|t| { + if arg.is_empty() { + t.pure_state() == State::Open + } else { + t.state().is_some_and(|s| s.get_label() == arg) + } + }) + .map(|t| t.event.id) + .collect(), + ); + } + Some('-') => tasks.add_note(&input[1..]), Some('>') | Some('<') => { @@ -200,7 +220,7 @@ async fn main() { }); tasks.move_up() } - + Some('#') => { tasks.add_tag(input[1..].to_string()); } diff --git a/src/task.rs b/src/task.rs index 580d659..502b688 100644 --- a/src/task.rs +++ b/src/task.rs @@ -42,14 +42,7 @@ impl Task { fn states(&self) -> impl Iterator + '_ { self.props.iter().filter_map(|event| { - match event.kind.as_u32() { - 1630 => Some(State::Open), - 1631 => Some(State::Done), - 1632 => Some(State::Closed), - 1633 => Some(State::Active), - _ => None, - } - .map(|s| TaskState { + event.kind.try_into().ok().map(|s| TaskState { name: if event.content.is_empty() { None } else { @@ -61,7 +54,7 @@ impl Task { }) } - fn state(&self) -> Option { + pub(crate) fn state(&self) -> Option { self.states().max_by_key(|t| t.time) } @@ -130,11 +123,16 @@ impl Task { } } -struct TaskState { +pub(crate) struct TaskState { name: Option, state: State, time: Timestamp, } +impl TaskState { + pub(crate) fn get_label(&self) -> String { + self.name.clone().unwrap_or_else(|| self.state.to_string()) + } +} impl fmt::Display for TaskState { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( @@ -155,6 +153,19 @@ pub(crate) enum State { Active, Done, } +impl TryFrom for State { + type Error = (); + + fn try_from(value: Kind) -> Result { + match value.as_u32() { + 1630 => Ok(State::Open), + 1631 => Ok(State::Done), + 1632 => Ok(State::Closed), + 1633 => Ok(State::Active), + _ => Err(()), + } + } +} impl State { pub(crate) fn kind(&self) -> Kind { match self {