style: reformat code
This commit is contained in:
parent
db11b54220
commit
0dba23bcc6
|
@ -36,7 +36,7 @@ impl<T: TimeZone> ToTimestamp for DateTime<T> {
|
|||
|
||||
/// Parses the hour from a plain number in the String,
|
||||
/// with max of max_future hours into the future.
|
||||
/// TODO parse HHMM as well
|
||||
// TODO parse HHMM as well
|
||||
pub fn parse_hour(str: &str, max_future: i64) -> Option<DateTime<Local>> {
|
||||
str.parse::<u32>().ok().and_then(|hour| {
|
||||
let now = Local::now();
|
||||
|
|
|
@ -118,8 +118,8 @@ pub(crate) fn extract_tags(input: &str, users: &NostrUsers) -> (String, Vec<Tag>
|
|||
return false;
|
||||
}
|
||||
if let Ok(num) = s[1..].parse::<Prio>() {
|
||||
tags.push(to_prio_tag(num * (if s.len() > 2 { 1 } else { 10 })));
|
||||
return false
|
||||
tags.push(to_prio_tag(num * (if s.len() > 2 { 1 } else { 10 })));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
|
|
|
@ -9,6 +9,7 @@ use std::str::FromStr;
|
|||
use std::time::Duration;
|
||||
|
||||
use crate::event_sender::MostrMessage;
|
||||
use crate::hashtag::Hashtag;
|
||||
use crate::helpers::*;
|
||||
use crate::kinds::{format_tag_basic, match_event_tag, Prio, BASIC_KINDS, PROPERTY_COLUMNS, PROP_KINDS};
|
||||
use crate::task::{State, Task, TaskState, MARKER_PROPERTY};
|
||||
|
@ -28,7 +29,6 @@ use rustyline::DefaultEditor;
|
|||
use tokio::sync::mpsc;
|
||||
use tokio::time::error::Elapsed;
|
||||
use tokio::time::timeout;
|
||||
use crate::hashtag::Hashtag;
|
||||
|
||||
mod helpers;
|
||||
mod task;
|
||||
|
@ -404,7 +404,7 @@ async fn main() -> Result<()> {
|
|||
Some(et) =>
|
||||
Some(et).take_if(|et| et.marker.as_ref().is_some_and(|m| m != MARKER_PROPERTY))
|
||||
.map(|et| format!("{}: {}", et.marker.as_ref().unwrap(), tasks.get_relative_path(et.id))),
|
||||
None =>
|
||||
None =>
|
||||
Some(format_tag_basic(t)),
|
||||
}
|
||||
}).join(", ")
|
||||
|
@ -492,7 +492,8 @@ async fn main() -> Result<()> {
|
|||
tasks.set_key_filter(key)
|
||||
} else {
|
||||
if parse_hour(arg, 1)
|
||||
.or_else(|| parse_date(arg).map(|utc| utc.with_timezone(&Local)))
|
||||
.or_else(|| parse_date(arg)
|
||||
.map(|utc| utc.with_timezone(&Local)))
|
||||
.map(|time| {
|
||||
info!("Filtering for tasks from {}", format_datetime_relative(time));
|
||||
tasks.set_filter_since(time.to_timestamp())
|
||||
|
|
15
src/task.rs
15
src/task.rs
|
@ -7,15 +7,16 @@ use std::iter::once;
|
|||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
|
||||
use crate::hashtag::{is_hashtag, Hashtag};
|
||||
use crate::helpers::{format_timestamp_local, some_non_empty};
|
||||
use crate::kinds::{match_event_tag, Prio, PRIO, PROCEDURE_KIND, PROCEDURE_KIND_ID, TASK_KIND};
|
||||
use crate::tasks::now;
|
||||
|
||||
use colored::{ColoredString, Colorize};
|
||||
use itertools::Either::{Left, Right};
|
||||
use itertools::Itertools;
|
||||
use log::{debug, error, info, trace, warn};
|
||||
use nostr_sdk::{Alphabet, Event, EventId, Kind, PublicKey, SingleLetterTag, Tag, TagKind, Timestamp};
|
||||
use crate::hashtag::{is_hashtag, Hashtag};
|
||||
use crate::helpers::{format_timestamp_local, some_non_empty};
|
||||
use crate::kinds::{match_event_tag, Prio, PRIO, PROCEDURE_KIND, PROCEDURE_KIND_ID, TASK_KIND};
|
||||
use crate::tasks::now;
|
||||
|
||||
pub static MARKER_PARENT: &str = "parent";
|
||||
pub static MARKER_DEPENDS: &str = "depends";
|
||||
|
@ -151,7 +152,7 @@ impl Task {
|
|||
pub fn last_state_update(&self) -> Timestamp {
|
||||
self.state().map(|s| s.time).unwrap_or(self.event.created_at)
|
||||
}
|
||||
|
||||
|
||||
pub fn state_at(&self, time: Timestamp) -> Option<TaskState> {
|
||||
// TODO do not iterate constructed state objects
|
||||
let state = self.states().take_while_inclusive(|ts| ts.time > time);
|
||||
|
@ -201,7 +202,7 @@ impl Task {
|
|||
fn tags(&self) -> impl Iterator<Item=&Tag> {
|
||||
self.props.iter()
|
||||
.flat_map(|e| e.tags.iter()
|
||||
.filter(|t| t.single_letter_tag().is_none_or(|s| s.character != Alphabet::E)))
|
||||
.filter(|t| t.single_letter_tag().is_none_or(|s| s.character != Alphabet::E)))
|
||||
.chain(self.tags.iter().flatten())
|
||||
}
|
||||
|
||||
|
@ -376,7 +377,7 @@ mod tasks_test {
|
|||
.sign_with_keys(&keys).unwrap());
|
||||
assert_eq!(task.pure_state(), State::Open);
|
||||
assert_eq!(task.list_hashtags().count(), 1);
|
||||
|
||||
|
||||
let now = Timestamp::now();
|
||||
task.props.insert(
|
||||
EventBuilder::new(State::Done.into(), "")
|
||||
|
|
146
src/tasks.rs
146
src/tasks.rs
|
@ -9,14 +9,20 @@ use std::time::Duration;
|
|||
|
||||
use crate::event_sender::{EventSender, MostrMessage};
|
||||
use crate::hashtag::Hashtag;
|
||||
use crate::helpers::{format_timestamp_local, format_timestamp_relative, format_timestamp_relative_to, parse_tracking_stamp, some_non_empty, to_string_or_default, CHARACTER_THRESHOLD};
|
||||
use crate::helpers::{
|
||||
format_timestamp_local, format_timestamp_relative, format_timestamp_relative_to,
|
||||
parse_tracking_stamp, some_non_empty, to_string_or_default, CHARACTER_THRESHOLD,
|
||||
};
|
||||
use crate::kinds::*;
|
||||
use crate::task::{State, Task, TaskState, MARKER_DEPENDS, MARKER_PARENT, MARKER_PROPERTY};
|
||||
use crate::tasks::nostr_users::NostrUsers;
|
||||
use colored::Colorize;
|
||||
use itertools::Itertools;
|
||||
use log::{debug, error, info, trace, warn};
|
||||
use nostr_sdk::{Alphabet, Event, EventBuilder, EventId, JsonUtil, Keys, Kind, Metadata, PublicKey, SingleLetterTag, Tag, TagKind, Timestamp, Url};
|
||||
use nostr_sdk::{
|
||||
Alphabet, Event, EventBuilder, EventId, JsonUtil, Keys, Kind, Metadata, PublicKey,
|
||||
SingleLetterTag, Tag, TagKind, Timestamp, Url,
|
||||
};
|
||||
use regex::bytes::Regex;
|
||||
use tokio::sync::mpsc::Sender;
|
||||
|
||||
|
@ -64,7 +70,7 @@ pub(crate) struct TasksRelay {
|
|||
/// The task properties currently visible
|
||||
properties: Vec<String>,
|
||||
/// The task properties sorted by
|
||||
sorting: VecDeque<String>, // TODO track boolean for reversal?
|
||||
sorting: VecDeque<String>, // TODO prefix +/- for asc/desc, no prefix for default
|
||||
|
||||
/// A filtered view of the current tasks.
|
||||
/// Would like this to be Task references
|
||||
|
@ -270,7 +276,9 @@ impl TasksRelay {
|
|||
hist.values().filter_map(move |event| {
|
||||
let new = some_non_empty(&event.tags.iter()
|
||||
.filter_map(|t| t.content())
|
||||
.map(|str| EventId::from_str(str).ok().map_or(str.to_string(), |id| self.get_task_path(Some(id))))
|
||||
.map(|str|
|
||||
EventId::from_str(str).ok()
|
||||
.map_or(str.to_string(), |id| self.get_task_path(Some(id))))
|
||||
.join(" "));
|
||||
if new != last {
|
||||
// TODO omit intervals <2min - but I think I need threeway variable tracking for that
|
||||
|
@ -310,7 +318,7 @@ impl TasksRelay {
|
|||
match self.get_position() {
|
||||
None => self.times_tracked_for(key),
|
||||
Some(id) => {
|
||||
// TODO show current recursive with pubkey
|
||||
// TODO show current recursive if there is a pubkey
|
||||
let ids = [id];
|
||||
let mut history =
|
||||
self.history.iter().flat_map(|(key, set)| {
|
||||
|
@ -335,8 +343,7 @@ impl TasksRelay {
|
|||
))
|
||||
});
|
||||
vec
|
||||
})
|
||||
.collect_vec();
|
||||
}).collect_vec();
|
||||
// TODO sorting depends on timestamp format - needed to interleave different people
|
||||
history.sort_unstable();
|
||||
(
|
||||
|
@ -404,13 +411,14 @@ impl TasksRelay {
|
|||
|
||||
pub(crate) fn pubkey_str(&self) -> Option<String> {
|
||||
match self.pubkey {
|
||||
None => { Some("ALL".to_string()) }
|
||||
Some(key) =>
|
||||
None => Some("ALL".to_string()),
|
||||
Some(key) => {
|
||||
if key != self.sender.pubkey() {
|
||||
Some(self.users.get_username(&key))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -425,8 +433,8 @@ impl TasksRelay {
|
|||
prompt.push_str(&format!(" -#{}", tag));
|
||||
}
|
||||
prompt.push_str(&self.state.indicator());
|
||||
self.priority.map(|p|
|
||||
prompt.push_str(&format!(" *{:02}", p)));
|
||||
self.priority
|
||||
.map(|p| prompt.push_str(&format!(" *{:02}", p)));
|
||||
prompt
|
||||
}
|
||||
|
||||
|
@ -453,7 +461,6 @@ impl TasksRelay {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// Helpers
|
||||
|
||||
fn resolve_tasks_rec<'a>(
|
||||
|
@ -495,8 +502,7 @@ impl TasksRelay {
|
|||
let mut found = false;
|
||||
for tag in event.tags.iter() {
|
||||
if let Some(event_tag) = match_event_tag(tag) {
|
||||
if event_tag.marker
|
||||
.as_ref()
|
||||
if event_tag.marker.as_ref()
|
||||
.is_none_or(|m| m.to_string() == MARKER_PROPERTY)
|
||||
{
|
||||
self.tasks.get_mut(&event_tag.id).map(|t| {
|
||||
|
@ -529,11 +535,7 @@ impl TasksRelay {
|
|||
}
|
||||
|
||||
// TODO sparse is deprecated and only left for tests
|
||||
pub(crate) fn filtered_tasks(
|
||||
&self,
|
||||
position: Option<EventId>,
|
||||
sparse: bool,
|
||||
) -> Vec<&Task> {
|
||||
pub(crate) fn filtered_tasks(&self, position: Option<EventId>, sparse: bool) -> Vec<&Task> {
|
||||
let roots = self.tasks.children_for(position);
|
||||
let mut current =
|
||||
self.resolve_tasks_rec(roots, sparse, self.search_depth + self.view_depth);
|
||||
|
@ -712,7 +714,7 @@ impl TasksRelay {
|
|||
};
|
||||
self.sender.submit(
|
||||
EventBuilder::new(Kind::Bookmarks, "mostr pins")
|
||||
.tags(self.bookmarks.iter().map(|id| Tag::event(*id)))
|
||||
.tags(self.bookmarks.iter().map(|id| Tag::event(*id))),
|
||||
)?;
|
||||
Ok(added)
|
||||
}
|
||||
|
@ -955,7 +957,7 @@ impl TasksRelay {
|
|||
self.track_at(time, target);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
self.view.clear();
|
||||
let pos = self.get_position();
|
||||
if target == pos {
|
||||
|
@ -989,20 +991,26 @@ impl TasksRelay {
|
|||
// Updates
|
||||
|
||||
pub(crate) fn make_event_tag_from_id(&self, id: EventId, marker: &str) -> Tag {
|
||||
Tag::custom(TagKind::SingleLetter(SingleLetterTag::lowercase(Alphabet::E)), [
|
||||
id.to_string(),
|
||||
to_string_or_default(self.sender.url.as_ref()),
|
||||
marker.to_string(),
|
||||
])
|
||||
Tag::custom(
|
||||
TagKind::SingleLetter(SingleLetterTag::lowercase(Alphabet::E)),
|
||||
[
|
||||
id.to_string(),
|
||||
to_string_or_default(self.sender.url.as_ref()),
|
||||
marker.to_string(),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn make_event_tag(&self, event: &Event, marker: &str) -> Tag {
|
||||
Tag::custom(TagKind::SingleLetter(SingleLetterTag::lowercase(Alphabet::E)), [
|
||||
event.id.to_string(),
|
||||
to_string_or_default(self.sender.url.as_ref()),
|
||||
marker.to_string(),
|
||||
event.pubkey.to_string(),
|
||||
])
|
||||
Tag::custom(
|
||||
TagKind::SingleLetter(SingleLetterTag::lowercase(Alphabet::E)),
|
||||
[
|
||||
event.id.to_string(),
|
||||
to_string_or_default(self.sender.url.as_ref()),
|
||||
marker.to_string(),
|
||||
event.pubkey.to_string(),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn parent_tag(&self) -> Option<Tag> {
|
||||
|
@ -1069,17 +1077,29 @@ impl TasksRelay {
|
|||
/// Creates a task including current tag filters
|
||||
///
|
||||
/// Sanitizes input
|
||||
pub(crate) fn make_task_with(&mut self, input: &str, tags: impl IntoIterator<Item=Tag>, set_state: bool) -> EventId {
|
||||
pub(crate) fn make_task_with(
|
||||
&mut self,
|
||||
input: &str,
|
||||
tags: impl IntoIterator<Item=Tag>,
|
||||
set_state: bool,
|
||||
) -> EventId {
|
||||
let (input, input_tags) = extract_tags(input.trim(), &self.users);
|
||||
let prio =
|
||||
if input_tags.iter().any(|t| t.kind().to_string() == PRIO) { None } else { self.priority.map(|p| to_prio_tag(p)) };
|
||||
info!("Created task \"{input}\" with tags [{}]", join_tags(&input_tags));
|
||||
if input_tags.iter().any(|t| t.kind().to_string() == PRIO) {
|
||||
None
|
||||
} else {
|
||||
self.priority.map(|p| to_prio_tag(p))
|
||||
};
|
||||
info!(
|
||||
"Created task \"{input}\" with tags [{}]",
|
||||
join_tags(&input_tags)
|
||||
);
|
||||
let id = self.submit(
|
||||
EventBuilder::new(TASK_KIND, &input)
|
||||
.tags(input_tags)
|
||||
.tags(self.context_hashtags())
|
||||
.tags(tags)
|
||||
.tags(prio)
|
||||
.tags(prio),
|
||||
);
|
||||
if set_state {
|
||||
self.state
|
||||
|
@ -1224,9 +1244,7 @@ impl TasksRelay {
|
|||
}
|
||||
|
||||
fn get_own_events_history(&self) -> impl DoubleEndedIterator<Item=&Event> + '_ {
|
||||
self.get_own_history()
|
||||
.into_iter()
|
||||
.flat_map(|t| t.values())
|
||||
self.get_own_history().into_iter().flat_map(|t| t.values())
|
||||
}
|
||||
|
||||
pub(super) fn history_before_now(&self) -> impl Iterator<Item=&Event> {
|
||||
|
@ -1310,7 +1328,8 @@ impl TasksRelay {
|
|||
self.get_by_id(&id)
|
||||
.and_then(|task| task.state_at(self.custom_time.unwrap_or_default()))
|
||||
.map(|ts| format!(" from {}", ts))
|
||||
.unwrap_or_default());
|
||||
.unwrap_or_default()
|
||||
);
|
||||
self.submit(prop)
|
||||
}
|
||||
|
||||
|
@ -1338,7 +1357,8 @@ impl TasksRelay {
|
|||
info!("Created {} {format}", if marker == MARKER_PROPERTY { "note" } else { "activity" } );
|
||||
self.submit(
|
||||
prop.tags(
|
||||
self.get_position().map(|pos| self.make_event_tag_from_id(pos, marker))))
|
||||
self.get_position()
|
||||
.map(|pos| self.make_event_tag_from_id(pos, marker))))
|
||||
}
|
||||
|
||||
// Properties
|
||||
|
@ -1381,9 +1401,7 @@ impl Display for TasksRelay {
|
|||
let state = t.state_or_default();
|
||||
let now = &now();
|
||||
let mut tracking_stamp: Option<Timestamp> = None;
|
||||
for elem in
|
||||
timestamps(self.get_own_events_history(), &[t.event.id])
|
||||
.map(|(e, _)| e) {
|
||||
for elem in timestamps(self.get_own_events_history(), &[t.event.id]).map(|(e, _)| e) {
|
||||
if tracking_stamp.is_some() && elem > now {
|
||||
break;
|
||||
}
|
||||
|
@ -1437,11 +1455,10 @@ impl Display for TasksRelay {
|
|||
let count = visible.len();
|
||||
let mut total_time = 0;
|
||||
for task in visible {
|
||||
writeln!(
|
||||
lock,
|
||||
"{}", self.properties.iter()
|
||||
.map(|p| self.get_property(task, p.as_str()))
|
||||
.join(" \t")
|
||||
writeln!(lock, "{}",
|
||||
self.properties.iter()
|
||||
.map(|p| self.get_property(task, p.as_str()))
|
||||
.join(" \t")
|
||||
)?;
|
||||
total_time += self.total_time_tracked(task.event.id) // TODO include parent if it matches
|
||||
}
|
||||
|
@ -1499,11 +1516,12 @@ where
|
|||
fn display_time(format: &str, secs: u64) -> String {
|
||||
Some(secs / 60)
|
||||
.filter(|t| t > &0)
|
||||
.map_or(String::new(), |mins| format
|
||||
.replace("MMM", &format!("{:3}", mins))
|
||||
.replace("HH", &format!("{:02}", mins.div(60)))
|
||||
.replace("MM", &format!("{:02}", mins.rem(60))),
|
||||
)
|
||||
.map_or(String::new(), |mins| {
|
||||
format
|
||||
.replace("MMM", &format!("{:3}", mins))
|
||||
.replace("HH", &format!("{:02}", mins.div(60)))
|
||||
.replace("MM", &format!("{:02}", mins.rem(60)))
|
||||
})
|
||||
}
|
||||
|
||||
/// Joins the tasks of this upwards iterator.
|
||||
|
@ -1521,7 +1539,8 @@ pub(crate) fn join_tasks<'a>(
|
|||
.take_if(|_| include_last_id)
|
||||
.and_then(|t| t.parent_id())
|
||||
.map(|id| id.to_string())
|
||||
.into_iter())
|
||||
.into_iter(),
|
||||
)
|
||||
.fold(None, |acc, val| {
|
||||
Some(acc.map_or_else(
|
||||
|| val.clone(),
|
||||
|
@ -1793,7 +1812,8 @@ mod tasks_test {
|
|||
($left:expr, $right:expr $(,)?) => {
|
||||
let tasks = $left.visible_tasks();
|
||||
assert_tasks!($left, tasks, $right,
|
||||
"\nQuick Access: {:?}", $left.quick_access_raw().map(|id| $left.get_relative_path(*id)).collect_vec());
|
||||
"\nQuick Access: {:?}",
|
||||
$left.quick_access_raw().map(|id| $left.get_relative_path(*id)).collect_vec());
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1828,14 +1848,20 @@ mod tasks_test {
|
|||
let parent = tasks.make_task("parent #tag1");
|
||||
tasks.move_to(Some(parent));
|
||||
let sub = tasks.make_task("sub #oi # tag2");
|
||||
assert_eq!(tasks.all_hashtags(), ["oi", "tag1", "tag2"].into_iter().map(Hashtag::from).collect());
|
||||
assert_eq!(
|
||||
tasks.all_hashtags(),
|
||||
["oi", "tag1", "tag2"].into_iter().map(Hashtag::from).collect()
|
||||
);
|
||||
tasks.make_note("note with #tag3 # yeah");
|
||||
let all_tags = ["oi", "tag1", "tag2", "tag3", "yeah"].into_iter().map(Hashtag::from).collect();
|
||||
assert_eq!(tasks.all_hashtags(), all_tags);
|
||||
|
||||
tasks.custom_time = Some(Timestamp::now());
|
||||
tasks.update_state("Finished #YeaH # oi", State::Done);
|
||||
assert_eq!(tasks.get_by_id(&parent).unwrap().list_hashtags().collect_vec(), ["YeaH", "oi", "tag3", "yeah", "tag1"].map(Hashtag::from));
|
||||
assert_eq!(
|
||||
tasks.get_by_id(&parent).unwrap().list_hashtags().collect_vec(),
|
||||
["YeaH", "oi", "tag3", "yeah", "tag1"].map(Hashtag::from)
|
||||
);
|
||||
assert_eq!(tasks.all_hashtags(), all_tags);
|
||||
|
||||
tasks.custom_time = Some(now());
|
||||
|
@ -1911,7 +1937,7 @@ mod tasks_test {
|
|||
let parent = tasks.make_task("parent");
|
||||
let sub = tasks.submit(
|
||||
EventBuilder::new(TASK_KIND, "sub")
|
||||
.tags([tasks.make_event_tag_from_id(parent, MARKER_PARENT)])
|
||||
.tags([tasks.make_event_tag_from_id(parent, MARKER_PARENT)]),
|
||||
);
|
||||
assert_tasks_view!(tasks, [parent]);
|
||||
tasks.track_at(Timestamp::now(), Some(sub));
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use nostr_sdk::{Keys, Metadata, PublicKey, Tag};
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
use nostr_sdk::{Keys, Metadata, PublicKey, Tag};
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct NostrUsers {
|
||||
users: HashMap<PublicKey, Metadata>
|
||||
users: HashMap<PublicKey, Metadata>,
|
||||
}
|
||||
|
||||
impl NostrUsers {
|
||||
|
@ -18,7 +18,7 @@ impl NostrUsers {
|
|||
let lowered = term.trim().to_ascii_lowercase();
|
||||
let term = lowered.as_str();
|
||||
if term.is_empty() {
|
||||
return None
|
||||
return None;
|
||||
}
|
||||
if let Ok(key) = PublicKey::from_str(term) {
|
||||
return self.users.get_key_value(&key);
|
||||
|
|
Loading…
Reference in New Issue