style: reformat code

This commit is contained in:
xeruf 2024-12-05 14:39:47 +01:00
parent db11b54220
commit 0dba23bcc6
6 changed files with 104 additions and 76 deletions

View file

@ -36,7 +36,7 @@ impl<T: TimeZone> ToTimestamp for DateTime<T> {
/// Parses the hour from a plain number in the String, /// Parses the hour from a plain number in the String,
/// with max of max_future hours into the future. /// 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>> { pub fn parse_hour(str: &str, max_future: i64) -> Option<DateTime<Local>> {
str.parse::<u32>().ok().and_then(|hour| { str.parse::<u32>().ok().and_then(|hour| {
let now = Local::now(); let now = Local::now();

View file

@ -119,7 +119,7 @@ pub(crate) fn extract_tags(input: &str, users: &NostrUsers) -> (String, Vec<Tag>
} }
if let Ok(num) = s[1..].parse::<Prio>() { if let Ok(num) = s[1..].parse::<Prio>() {
tags.push(to_prio_tag(num * (if s.len() > 2 { 1 } else { 10 }))); tags.push(to_prio_tag(num * (if s.len() > 2 { 1 } else { 10 })));
return false return false;
} }
} }
true true

View file

@ -9,6 +9,7 @@ use std::str::FromStr;
use std::time::Duration; use std::time::Duration;
use crate::event_sender::MostrMessage; use crate::event_sender::MostrMessage;
use crate::hashtag::Hashtag;
use crate::helpers::*; use crate::helpers::*;
use crate::kinds::{format_tag_basic, match_event_tag, Prio, BASIC_KINDS, PROPERTY_COLUMNS, PROP_KINDS}; use crate::kinds::{format_tag_basic, match_event_tag, Prio, BASIC_KINDS, PROPERTY_COLUMNS, PROP_KINDS};
use crate::task::{State, Task, TaskState, MARKER_PROPERTY}; use crate::task::{State, Task, TaskState, MARKER_PROPERTY};
@ -28,7 +29,6 @@ use rustyline::DefaultEditor;
use tokio::sync::mpsc; use tokio::sync::mpsc;
use tokio::time::error::Elapsed; use tokio::time::error::Elapsed;
use tokio::time::timeout; use tokio::time::timeout;
use crate::hashtag::Hashtag;
mod helpers; mod helpers;
mod task; mod task;
@ -492,7 +492,8 @@ async fn main() -> Result<()> {
tasks.set_key_filter(key) tasks.set_key_filter(key)
} else { } else {
if parse_hour(arg, 1) 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| { .map(|time| {
info!("Filtering for tasks from {}", format_datetime_relative(time)); info!("Filtering for tasks from {}", format_datetime_relative(time));
tasks.set_filter_since(time.to_timestamp()) tasks.set_filter_since(time.to_timestamp())

View file

@ -7,15 +7,16 @@ use std::iter::once;
use std::str::FromStr; use std::str::FromStr;
use std::string::ToString; 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 colored::{ColoredString, Colorize};
use itertools::Either::{Left, Right}; use itertools::Either::{Left, Right};
use itertools::Itertools; use itertools::Itertools;
use log::{debug, error, info, trace, warn}; use log::{debug, error, info, trace, warn};
use nostr_sdk::{Alphabet, Event, EventId, Kind, PublicKey, SingleLetterTag, Tag, TagKind, Timestamp}; 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_PARENT: &str = "parent";
pub static MARKER_DEPENDS: &str = "depends"; pub static MARKER_DEPENDS: &str = "depends";

View file

@ -9,14 +9,20 @@ use std::time::Duration;
use crate::event_sender::{EventSender, MostrMessage}; use crate::event_sender::{EventSender, MostrMessage};
use crate::hashtag::Hashtag; 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::kinds::*;
use crate::task::{State, Task, TaskState, MARKER_DEPENDS, MARKER_PARENT, MARKER_PROPERTY}; use crate::task::{State, Task, TaskState, MARKER_DEPENDS, MARKER_PARENT, MARKER_PROPERTY};
use crate::tasks::nostr_users::NostrUsers; use crate::tasks::nostr_users::NostrUsers;
use colored::Colorize; use colored::Colorize;
use itertools::Itertools; use itertools::Itertools;
use log::{debug, error, info, trace, warn}; 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 regex::bytes::Regex;
use tokio::sync::mpsc::Sender; use tokio::sync::mpsc::Sender;
@ -64,7 +70,7 @@ pub(crate) struct TasksRelay {
/// The task properties currently visible /// The task properties currently visible
properties: Vec<String>, properties: Vec<String>,
/// The task properties sorted by /// 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. /// A filtered view of the current tasks.
/// Would like this to be Task references /// Would like this to be Task references
@ -270,7 +276,9 @@ impl TasksRelay {
hist.values().filter_map(move |event| { hist.values().filter_map(move |event| {
let new = some_non_empty(&event.tags.iter() let new = some_non_empty(&event.tags.iter()
.filter_map(|t| t.content()) .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(" ")); .join(" "));
if new != last { if new != last {
// TODO omit intervals <2min - but I think I need threeway variable tracking for that // TODO omit intervals <2min - but I think I need threeway variable tracking for that
@ -310,7 +318,7 @@ impl TasksRelay {
match self.get_position() { match self.get_position() {
None => self.times_tracked_for(key), None => self.times_tracked_for(key),
Some(id) => { Some(id) => {
// TODO show current recursive with pubkey // TODO show current recursive if there is a pubkey
let ids = [id]; let ids = [id];
let mut history = let mut history =
self.history.iter().flat_map(|(key, set)| { self.history.iter().flat_map(|(key, set)| {
@ -335,8 +343,7 @@ impl TasksRelay {
)) ))
}); });
vec vec
}) }).collect_vec();
.collect_vec();
// TODO sorting depends on timestamp format - needed to interleave different people // TODO sorting depends on timestamp format - needed to interleave different people
history.sort_unstable(); history.sort_unstable();
( (
@ -404,13 +411,14 @@ impl TasksRelay {
pub(crate) fn pubkey_str(&self) -> Option<String> { pub(crate) fn pubkey_str(&self) -> Option<String> {
match self.pubkey { match self.pubkey {
None => { Some("ALL".to_string()) } None => Some("ALL".to_string()),
Some(key) => Some(key) => {
if key != self.sender.pubkey() { if key != self.sender.pubkey() {
Some(self.users.get_username(&key)) Some(self.users.get_username(&key))
} else { } else {
None None
}, }
}
} }
} }
@ -425,8 +433,8 @@ impl TasksRelay {
prompt.push_str(&format!(" -#{}", tag)); prompt.push_str(&format!(" -#{}", tag));
} }
prompt.push_str(&self.state.indicator()); prompt.push_str(&self.state.indicator());
self.priority.map(|p| self.priority
prompt.push_str(&format!(" *{:02}", p))); .map(|p| prompt.push_str(&format!(" *{:02}", p)));
prompt prompt
} }
@ -453,7 +461,6 @@ impl TasksRelay {
} }
} }
// Helpers // Helpers
fn resolve_tasks_rec<'a>( fn resolve_tasks_rec<'a>(
@ -495,8 +502,7 @@ impl TasksRelay {
let mut found = false; let mut found = false;
for tag in event.tags.iter() { for tag in event.tags.iter() {
if let Some(event_tag) = match_event_tag(tag) { if let Some(event_tag) = match_event_tag(tag) {
if event_tag.marker if event_tag.marker.as_ref()
.as_ref()
.is_none_or(|m| m.to_string() == MARKER_PROPERTY) .is_none_or(|m| m.to_string() == MARKER_PROPERTY)
{ {
self.tasks.get_mut(&event_tag.id).map(|t| { self.tasks.get_mut(&event_tag.id).map(|t| {
@ -529,11 +535,7 @@ impl TasksRelay {
} }
// TODO sparse is deprecated and only left for tests // TODO sparse is deprecated and only left for tests
pub(crate) fn filtered_tasks( pub(crate) fn filtered_tasks(&self, position: Option<EventId>, sparse: bool) -> Vec<&Task> {
&self,
position: Option<EventId>,
sparse: bool,
) -> Vec<&Task> {
let roots = self.tasks.children_for(position); let roots = self.tasks.children_for(position);
let mut current = let mut current =
self.resolve_tasks_rec(roots, sparse, self.search_depth + self.view_depth); self.resolve_tasks_rec(roots, sparse, self.search_depth + self.view_depth);
@ -712,7 +714,7 @@ impl TasksRelay {
}; };
self.sender.submit( self.sender.submit(
EventBuilder::new(Kind::Bookmarks, "mostr pins") 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) Ok(added)
} }
@ -989,20 +991,26 @@ impl TasksRelay {
// Updates // Updates
pub(crate) fn make_event_tag_from_id(&self, id: EventId, marker: &str) -> Tag { pub(crate) fn make_event_tag_from_id(&self, id: EventId, marker: &str) -> Tag {
Tag::custom(TagKind::SingleLetter(SingleLetterTag::lowercase(Alphabet::E)), [ Tag::custom(
TagKind::SingleLetter(SingleLetterTag::lowercase(Alphabet::E)),
[
id.to_string(), id.to_string(),
to_string_or_default(self.sender.url.as_ref()), to_string_or_default(self.sender.url.as_ref()),
marker.to_string(), marker.to_string(),
]) ],
)
} }
pub(crate) fn make_event_tag(&self, event: &Event, marker: &str) -> Tag { pub(crate) fn make_event_tag(&self, event: &Event, marker: &str) -> Tag {
Tag::custom(TagKind::SingleLetter(SingleLetterTag::lowercase(Alphabet::E)), [ Tag::custom(
TagKind::SingleLetter(SingleLetterTag::lowercase(Alphabet::E)),
[
event.id.to_string(), event.id.to_string(),
to_string_or_default(self.sender.url.as_ref()), to_string_or_default(self.sender.url.as_ref()),
marker.to_string(), marker.to_string(),
event.pubkey.to_string(), event.pubkey.to_string(),
]) ],
)
} }
pub(crate) fn parent_tag(&self) -> Option<Tag> { pub(crate) fn parent_tag(&self) -> Option<Tag> {
@ -1069,17 +1077,29 @@ impl TasksRelay {
/// Creates a task including current tag filters /// Creates a task including current tag filters
/// ///
/// Sanitizes input /// 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 (input, input_tags) = extract_tags(input.trim(), &self.users);
let prio = let prio =
if input_tags.iter().any(|t| t.kind().to_string() == PRIO) { None } else { self.priority.map(|p| to_prio_tag(p)) }; if input_tags.iter().any(|t| t.kind().to_string() == PRIO) {
info!("Created task \"{input}\" with tags [{}]", join_tags(&input_tags)); None
} else {
self.priority.map(|p| to_prio_tag(p))
};
info!(
"Created task \"{input}\" with tags [{}]",
join_tags(&input_tags)
);
let id = self.submit( let id = self.submit(
EventBuilder::new(TASK_KIND, &input) EventBuilder::new(TASK_KIND, &input)
.tags(input_tags) .tags(input_tags)
.tags(self.context_hashtags()) .tags(self.context_hashtags())
.tags(tags) .tags(tags)
.tags(prio) .tags(prio),
); );
if set_state { if set_state {
self.state self.state
@ -1224,9 +1244,7 @@ impl TasksRelay {
} }
fn get_own_events_history(&self) -> impl DoubleEndedIterator<Item=&Event> + '_ { fn get_own_events_history(&self) -> impl DoubleEndedIterator<Item=&Event> + '_ {
self.get_own_history() self.get_own_history().into_iter().flat_map(|t| t.values())
.into_iter()
.flat_map(|t| t.values())
} }
pub(super) fn history_before_now(&self) -> impl Iterator<Item=&Event> { pub(super) fn history_before_now(&self) -> impl Iterator<Item=&Event> {
@ -1310,7 +1328,8 @@ impl TasksRelay {
self.get_by_id(&id) self.get_by_id(&id)
.and_then(|task| task.state_at(self.custom_time.unwrap_or_default())) .and_then(|task| task.state_at(self.custom_time.unwrap_or_default()))
.map(|ts| format!(" from {}", ts)) .map(|ts| format!(" from {}", ts))
.unwrap_or_default()); .unwrap_or_default()
);
self.submit(prop) self.submit(prop)
} }
@ -1338,7 +1357,8 @@ impl TasksRelay {
info!("Created {} {format}", if marker == MARKER_PROPERTY { "note" } else { "activity" } ); info!("Created {} {format}", if marker == MARKER_PROPERTY { "note" } else { "activity" } );
self.submit( self.submit(
prop.tags( 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 // Properties
@ -1381,9 +1401,7 @@ impl Display for TasksRelay {
let state = t.state_or_default(); let state = t.state_or_default();
let now = &now(); let now = &now();
let mut tracking_stamp: Option<Timestamp> = None; let mut tracking_stamp: Option<Timestamp> = None;
for elem in for elem in timestamps(self.get_own_events_history(), &[t.event.id]).map(|(e, _)| e) {
timestamps(self.get_own_events_history(), &[t.event.id])
.map(|(e, _)| e) {
if tracking_stamp.is_some() && elem > now { if tracking_stamp.is_some() && elem > now {
break; break;
} }
@ -1437,9 +1455,8 @@ impl Display for TasksRelay {
let count = visible.len(); let count = visible.len();
let mut total_time = 0; let mut total_time = 0;
for task in visible { for task in visible {
writeln!( writeln!(lock, "{}",
lock, self.properties.iter()
"{}", self.properties.iter()
.map(|p| self.get_property(task, p.as_str())) .map(|p| self.get_property(task, p.as_str()))
.join(" \t") .join(" \t")
)?; )?;
@ -1499,11 +1516,12 @@ where
fn display_time(format: &str, secs: u64) -> String { fn display_time(format: &str, secs: u64) -> String {
Some(secs / 60) Some(secs / 60)
.filter(|t| t > &0) .filter(|t| t > &0)
.map_or(String::new(), |mins| format .map_or(String::new(), |mins| {
format
.replace("MMM", &format!("{:3}", mins)) .replace("MMM", &format!("{:3}", mins))
.replace("HH", &format!("{:02}", mins.div(60))) .replace("HH", &format!("{:02}", mins.div(60)))
.replace("MM", &format!("{:02}", mins.rem(60))), .replace("MM", &format!("{:02}", mins.rem(60)))
) })
} }
/// Joins the tasks of this upwards iterator. /// Joins the tasks of this upwards iterator.
@ -1521,7 +1539,8 @@ pub(crate) fn join_tasks<'a>(
.take_if(|_| include_last_id) .take_if(|_| include_last_id)
.and_then(|t| t.parent_id()) .and_then(|t| t.parent_id())
.map(|id| id.to_string()) .map(|id| id.to_string())
.into_iter()) .into_iter(),
)
.fold(None, |acc, val| { .fold(None, |acc, val| {
Some(acc.map_or_else( Some(acc.map_or_else(
|| val.clone(), || val.clone(),
@ -1793,7 +1812,8 @@ mod tasks_test {
($left:expr, $right:expr $(,)?) => { ($left:expr, $right:expr $(,)?) => {
let tasks = $left.visible_tasks(); let tasks = $left.visible_tasks();
assert_tasks!($left, tasks, $right, 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"); let parent = tasks.make_task("parent #tag1");
tasks.move_to(Some(parent)); tasks.move_to(Some(parent));
let sub = tasks.make_task("sub #oi # tag2"); 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"); tasks.make_note("note with #tag3 # yeah");
let all_tags = ["oi", "tag1", "tag2", "tag3", "yeah"].into_iter().map(Hashtag::from).collect(); let all_tags = ["oi", "tag1", "tag2", "tag3", "yeah"].into_iter().map(Hashtag::from).collect();
assert_eq!(tasks.all_hashtags(), all_tags); assert_eq!(tasks.all_hashtags(), all_tags);
tasks.custom_time = Some(Timestamp::now()); tasks.custom_time = Some(Timestamp::now());
tasks.update_state("Finished #YeaH # oi", State::Done); 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); assert_eq!(tasks.all_hashtags(), all_tags);
tasks.custom_time = Some(now()); tasks.custom_time = Some(now());
@ -1911,7 +1937,7 @@ mod tasks_test {
let parent = tasks.make_task("parent"); let parent = tasks.make_task("parent");
let sub = tasks.submit( let sub = tasks.submit(
EventBuilder::new(TASK_KIND, "sub") 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]); assert_tasks_view!(tasks, [parent]);
tasks.track_at(Timestamp::now(), Some(sub)); tasks.track_at(Timestamp::now(), Some(sub));

View file

@ -1,10 +1,10 @@
use nostr_sdk::{Keys, Metadata, PublicKey, Tag};
use std::collections::HashMap; use std::collections::HashMap;
use std::str::FromStr; use std::str::FromStr;
use nostr_sdk::{Keys, Metadata, PublicKey, Tag};
#[derive(Default, Debug, Clone, PartialEq, Eq)] #[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct NostrUsers { pub struct NostrUsers {
users: HashMap<PublicKey, Metadata> users: HashMap<PublicKey, Metadata>,
} }
impl NostrUsers { impl NostrUsers {
@ -18,7 +18,7 @@ impl NostrUsers {
let lowered = term.trim().to_ascii_lowercase(); let lowered = term.trim().to_ascii_lowercase();
let term = lowered.as_str(); let term = lowered.as_str();
if term.is_empty() { if term.is_empty() {
return None return None;
} }
if let Ok(key) = PublicKey::from_str(term) { if let Ok(key) = PublicKey::from_str(term) {
return self.users.get_key_value(&key); return self.users.get_key_value(&key);