diff --git a/src/kinds.rs b/src/kinds.rs index 00ac436..ff73411 100644 --- a/src/kinds.rs +++ b/src/kinds.rs @@ -57,7 +57,7 @@ where EventBuilder::new( Kind::from(TRACKING_KIND), "", - id.into_iter().map(|id| Tag::event(id)), + id.into_iter().map(Tag::event), ) } @@ -65,7 +65,7 @@ where pub(crate) fn build_task(name: &str, tags: Vec, kind: Option<(&str, Kind)>) -> EventBuilder { info!("Created {}task \"{name}\" with tags [{}]", kind.map(|k| k.0).unwrap_or_default(), - tags.iter().map(|tag| format_tag(tag)).join(", ")); + tags.iter().map(format_tag).join(", ")); EventBuilder::new(kind.map(|k| k.1).unwrap_or(Kind::from(TASK_KIND)), name, tags) } @@ -107,7 +107,7 @@ fn format_tag(tag: &Tag) -> String { public_key, alias, .. - }) => format!("Key{}: {:.8}", public_key.to_string(), alias.as_ref().map(|s| format!(" {s}")).unwrap_or_default()), + }) => format!("Key{}: {:.8}", public_key, alias.as_ref().map(|s| format!(" {s}")).unwrap_or_default()), Some(TagStandard::Hashtag(content)) => format!("#{content}"), _ => tag.content().map_or_else( diff --git a/src/main.rs b/src/main.rs index 0834ec2..84d29a6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,7 @@ use log::{debug, error, info, LevelFilter, trace, warn}; use nostr_sdk::prelude::*; use nostr_sdk::TagStandard::Hashtag; use regex::Regex; -use rustyline::{DefaultEditor, Editor}; +use rustyline::DefaultEditor; use rustyline::error::ReadlineError; use tokio::sync::mpsc; use tokio::sync::mpsc::Sender; @@ -239,12 +239,12 @@ async fn main() -> Result<()> { client.connect().await; let sub1 = client.subscribe(vec![ - Filter::new().kinds(KINDS.into_iter().map(|k| Kind::from(k))) + Filter::new().kinds(KINDS.into_iter().map(Kind::from)) ], None).await; info!("Subscribed to tasks with {:?}", sub1); let sub2 = client.subscribe(vec![ - Filter::new().kinds(PROP_KINDS.into_iter().map(|k| Kind::from(k))) + Filter::new().kinds(PROP_KINDS.into_iter().map(Kind::from)) ], None).await; info!("Subscribed to updates with {:?}", sub2); @@ -418,19 +418,19 @@ async fn main() -> Result<()> { Some(arg) => { if arg.len() < CHARACTER_THRESHOLD { warn!("Note needs at least {CHARACTER_THRESHOLD} characters!"); - continue + continue; } tasks.make_note(arg) - }, + } } Some('>') => { - tasks.update_state(&arg_default, State::Done); + tasks.update_state(arg_default, State::Done); tasks.move_up(); } Some('<') => { - tasks.update_state(&arg_default, State::Closed); + tasks.update_state(arg_default, State::Closed); tasks.move_up(); } @@ -441,7 +441,7 @@ async fn main() -> Result<()> { Some('@') => { match arg { None => { - let today = Timestamp::from(Timestamp::now() - 80_000); + let today = Timestamp::now() - 80_000; info!("Filtering for tasks created in the last 22 hours"); tasks.set_filter( tasks.filtered_tasks(tasks.get_position_ref()) @@ -503,7 +503,7 @@ async fn main() -> Result<()> { } }, Some(arg) => 'arm: { - if arg.chars().next() != Some('|') { + if !arg.starts_with('|') { if let Some(pos) = tasks.get_position() { tasks.move_up(); tasks.make_task_with( @@ -562,7 +562,7 @@ async fn main() -> Result<()> { let (label, times) = tasks.times_tracked(); println!("{}\n{}", label.italic(), times.rev().take(15).join("\n")); } - // TODO show history from author / pubkey + // TODO show history of author / pubkey } else { let (label, mut times) = tasks.times_tracked(); println!("{}\n{}", label.italic(), times.join("\n")); @@ -629,7 +629,7 @@ async fn main() -> Result<()> { tasks.set_depth(depth); } else { let mut transform: Box String> = Box::new(|s: &str| s.to_string()); - if slice.chars().find(|c| c.is_ascii_uppercase()).is_none() { + if !slice.chars().any(|c| c.is_ascii_uppercase()) { // Smart-case - case-sensitive if any uppercase char is entered transform = Box::new(|s| s.to_ascii_lowercase()); } @@ -643,7 +643,7 @@ async fn main() -> Result<()> { .map(|t| t.event.id) .collect_vec(); if filtered.len() == 1 { - tasks.move_to(filtered.into_iter().nth(0)); + tasks.move_to(filtered.into_iter().next()); } else { tasks.move_to(pos.cloned()); tasks.set_filter(filtered); @@ -652,10 +652,10 @@ async fn main() -> Result<()> { } _ => - if Regex::new("^wss?://").unwrap().is_match(&input.trim()) { + if Regex::new("^wss?://").unwrap().is_match(input.trim()) { tasks.move_to(None); if let Some((url, tasks)) = relays.iter().find(|(key, _)| key.as_ref().is_some_and(|url| url.as_str().starts_with(&input))) { - selected_relay = url.clone(); + selected_relay.clone_from(url); or_warn!(tasks.print_tasks()); continue; } diff --git a/src/task.rs b/src/task.rs index dd6ff93..bfbfa76 100644 --- a/src/task.rs +++ b/src/task.rs @@ -46,7 +46,8 @@ impl Ord for Task { impl Task { pub(crate) fn new(event: Event) -> Task { let (refs, tags) = event.tags.iter().partition_map(|tag| match tag.as_standardized() { - Some(TagStandard::Event { event_id, marker, .. }) => Left((marker.as_ref().map_or(MARKER_PARENT.to_string(), |m| m.to_string()), event_id.clone())), + Some(TagStandard::Event { event_id, marker, .. }) => + Left((marker.as_ref().map_or(MARKER_PARENT.to_string(), |m| m.to_string()), *event_id)), _ => Right(tag.clone()), }); // Separate refs for dependencies @@ -82,13 +83,7 @@ impl Task { } pub(crate) fn description_events(&self) -> impl Iterator + '_ { - self.props.iter().filter_map(|event| { - if event.kind == Kind::TextNote { - Some(event) - } else { - None - } - }) + self.props.iter().filter(|event| event.kind == Kind::TextNote) } pub(crate) fn descriptions(&self) -> impl Iterator + '_ { @@ -105,7 +100,7 @@ impl Task { event.kind.try_into().ok().map(|s| TaskState { name: some_non_empty(&event.content), state: s, - time: event.created_at.clone(), + time: event.created_at, }) }) } @@ -142,9 +137,9 @@ impl Task { P: FnMut(&&Tag) -> bool, { self.tags.as_ref().map(|tags| { - tags.into_iter() + tags.iter() .filter(predicate) - .map(|t| format!("{}", t.content().unwrap())) + .map(|t| t.content().unwrap().to_string()) .join(" ") }) } @@ -261,10 +256,7 @@ impl TryFrom for State { } impl State { pub(crate) fn is_open(&self) -> bool { - match self { - State::Open | State::Pending | State::Procedure => true, - _ => false, - } + matches!(self, State::Open | State::Pending | State::Procedure) } pub(crate) fn kind(self) -> u16 { diff --git a/src/tasks.rs b/src/tasks.rs index bbd7227..9c23125 100644 --- a/src/tasks.rs +++ b/src/tasks.rs @@ -49,8 +49,9 @@ pub(crate) struct Tasks { sender: EventSender, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub(crate) enum StateFilter { + #[default] Default, All, State(String), @@ -68,7 +69,7 @@ impl StateFilter { match self { StateFilter::Default => { let state = task.pure_state(); - state.is_open() || (state == State::Done && task.parent_id() != None) + state.is_open() || (state == State::Done && task.parent_id().is_some()) } StateFilter::All => true, StateFilter::State(filter) => task.state().is_some_and(|t| t.matches_label(filter)), @@ -83,11 +84,6 @@ impl StateFilter { } } } -impl Default for StateFilter { - fn default() -> Self { - StateFilter::Default - } -} impl Display for StateFilter { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( @@ -104,12 +100,7 @@ impl Display for StateFilter { impl Tasks { pub(crate) fn from(url: Option, tx: &tokio::sync::mpsc::Sender, keys: &Keys, metadata: Option) -> Self { - let mut new = Self::with_sender(EventSender { - url, - tx: tx.clone(), - keys: keys.clone(), - queue: Default::default(), - }); + let mut new = Self::with_sender(EventSender::from(url, tx, keys)); metadata.map(|m| new.users.insert(keys.public_key(), m)); new } @@ -156,7 +147,7 @@ impl Tasks { } fn now() -> Timestamp { - Timestamp::from(Timestamp::now() + Self::MAX_OFFSET) + Timestamp::now() + Self::MAX_OFFSET } pub(crate) fn get_position_ref(&self) -> Option<&EventId> { @@ -166,7 +157,7 @@ impl Tasks { } /// Ids of all subtasks recursively found for id, including itself - pub(crate) fn get_task_tree<'a>(&'a self, id: &'a EventId) -> ChildIterator { + fn get_task_tree<'a>(&'a self, id: &'a EventId) -> ChildIterator { ChildIterator::from(self, id) } @@ -220,7 +211,7 @@ impl Tasks { vec.push(format!("{} started by {}", format_timestamp_local(stamp), self.get_author(key)))); vec }).sorted_unstable(); // TODO sorting depends on timestamp format - needed to interleave different people - (format!("Times Tracked on {:?}", self.get_task_title(&id)), Box::from(history)) + (format!("Times Tracked on {:?}", self.get_task_title(id)), Box::from(history)) } } } @@ -314,7 +305,7 @@ impl Tasks { iter: impl Iterator, depth: i8, ) -> Box> { - iter.filter_map(|id| self.get_by_id(&id)) + iter.filter_map(|id| self.get_by_id(id)) .flat_map(move |task| { let new_depth = depth - 1; if new_depth == 0 { @@ -372,7 +363,7 @@ impl Tasks { // TODO apply filters in transit self.state.matches(t) && t.tags.as_ref().map_or(true, |tags| { - tags.iter().find(|tag| self.tags_excluded.contains(tag)).is_none() + !tags.iter().any(|tag| self.tags_excluded.contains(tag)) }) && (self.tags.is_empty() || t.tags.as_ref().map_or(false, |tags| { @@ -386,7 +377,7 @@ impl Tasks { if self.depth == 0 { return vec![]; } - if self.view.len() > 0 { + if !self.view.is_empty() { return self.resolve_tasks(self.view.iter()).collect(); } self.filtered_tasks(self.get_position_ref()).collect() @@ -399,12 +390,12 @@ impl Tasks { let now = &Self::now(); let mut tracking_stamp: Option = None; for elem in - timestamps(self.history.get(&self.sender.pubkey()).into_iter().flatten(), &vec![t.get_id()]) + timestamps(self.history.get(&self.sender.pubkey()).into_iter().flatten(), &[t.get_id()]) .map(|(e, _)| e) { if tracking_stamp.is_some() && elem > now { break; } - tracking_stamp = Some(elem.clone()) + tracking_stamp = Some(*elem) } writeln!( lock, @@ -458,9 +449,8 @@ impl Tasks { fn get_property(&self, task: &Task, str: &str) -> String { let progress = - self - .total_progress(task.get_id()) - .filter(|_| task.children.len() > 0); + self.total_progress(task.get_id()) + .filter(|_| !task.children.is_empty()); let prog_string = progress.map_or(String::new(), |p| format!("{:2.0}%", p * 100.0)); match str { "subtasks" => { @@ -496,7 +486,7 @@ impl Tasks { // TODO format strings configurable "time" => display_time("MMMm", self.time_tracked(*task.get_id())), "rtime" => display_time("HH:MM", self.total_time_tracked(*task.get_id())), - prop => task.get(prop).unwrap_or(String::new()), + prop => task.get(prop).unwrap_or_default(), } } @@ -596,7 +586,7 @@ impl Tasks { if filtered.is_empty() { return filtered_more; } - return filtered; + filtered } /// Finds out what to do with the given string. @@ -609,7 +599,7 @@ impl Tasks { self.view.clear(); if arg.len() < CHARACTER_THRESHOLD { warn!("New task name needs at least {CHARACTER_THRESHOLD} characters"); - return None + return None; } Some(self.make_task_with(arg, self.position_tags_for(position), true)) } @@ -727,7 +717,7 @@ impl Tasks { let id = self.submit( build_task(input, input_tags, None) .add_tags(self.tags.iter().cloned()) - .add_tags(tags.into_iter()) + .add_tags(tags) ); if set_state { self.state.as_option().inspect(|s| self.set_state_for_with(id, s)); @@ -845,13 +835,13 @@ impl Tasks { pub(crate) fn update_state(&mut self, comment: &str, state: State) -> Option { let id = self.get_position_ref()?; - Some(self.set_state_for(id.clone(), comment, state)) + Some(self.set_state_for(*id, comment, state)) } pub(crate) fn make_note(&mut self, note: &str) { if let Some(id) = self.get_position_ref() { if self.get_by_id(id).is_some_and(|t| t.is_task()) { - let prop = build_prop(Kind::TextNote, note.trim(), id.clone()); + let prop = build_prop(Kind::TextNote, note.trim(), *id); self.submit(prop); return; } @@ -968,7 +958,7 @@ fn referenced_events(event: &Event) -> Option<&EventId> { }) } -fn matching_tag_id<'a>(event: &'a Event, ids: &'a Vec<&'a EventId>) -> Option<&'a EventId> { +fn matching_tag_id<'a>(event: &'a Event, ids: &'a [&'a EventId]) -> Option<&'a EventId> { event.tags.iter().find_map(|tag| match tag.as_standardized() { Some(TagStandard::Event { event_id, .. }) if ids.contains(&event_id) => Some(event_id), _ => None @@ -976,10 +966,10 @@ fn matching_tag_id<'a>(event: &'a Event, ids: &'a Vec<&'a EventId>) -> Option<&' } /// Filters out event timestamps to those that start or stop one of the given events -fn timestamps<'a>(events: impl Iterator, ids: &'a Vec<&'a EventId>) -> impl Iterator)> { +fn timestamps<'a>(events: impl Iterator, ids: &'a [&'a EventId]) -> impl Iterator)> { events.map(|event| (&event.created_at, matching_tag_id(event, ids))) .dedup_by(|(_, e1), (_, e2)| e1 == e2) - .skip_while(|element| element.1 == None) + .skip_while(|element| element.1.is_none()) } /// Iterates Events to accumulate times tracked @@ -1016,7 +1006,7 @@ impl Iterator for Durations<'_> { } } let now = self.threshold.unwrap_or(Timestamp::now()).as_u64(); - return start.filter(|t| t < &now).map(|stamp| Duration::from_secs(now.saturating_sub(stamp))); + start.filter(|t| t < &now).map(|stamp| Duration::from_secs(now.saturating_sub(stamp))) } } @@ -1065,7 +1055,7 @@ impl<'a> Iterator for ChildIterator<'a> { return None; } let id = self.queue[self.index]; - if let Some(task) = self.tasks.get(&id) { + if let Some(task) = self.tasks.get(id) { self.queue.reserve(task.children.len()); self.queue.extend(task.children.iter()); } else { @@ -1097,7 +1087,7 @@ impl<'a> Iterator for ParentIterator<'a> { fn next(&mut self) -> Option { self.current.and_then(|id| self.tasks.get(&id)).map(|t| { - self.prev.map(|id| assert!(t.children.contains(&id))); + self.prev.inspect(|id| assert!(t.children.contains(id))); self.prev = self.current; self.current = t.parent_id().cloned(); t