Compare commits

...

2 Commits

Author SHA1 Message Date
xeruf ed5e4d97d7 style: reorder tasks methods logically 2024-07-25 22:10:01 +03:00
xeruf 81f226fa43 style: reformat all code 2024-07-25 22:09:24 +03:00
3 changed files with 170 additions and 122 deletions

View File

@ -1,15 +1,13 @@
use std::borrow::Borrow;
use std::env::args; use std::env::args;
use std::fmt::Display; use std::fmt::Display;
use std::fs; use std::fs;
use std::io::{Read, stdin, stdout, Write}; use std::io::{stdin, stdout, Write};
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::ops::Deref; use std::ops::Deref;
use std::str::FromStr; use std::str::FromStr;
use std::sync::mpsc; use std::sync::mpsc;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use nostr_sdk::async_utility::futures_util::TryFutureExt;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
@ -31,17 +29,13 @@ mod tasks;
*/ */
static TASK_KIND: u64 = 1621; static TASK_KIND: u64 = 1621;
static MY_KEYS: Lazy<Keys> = Lazy::new(|| { static MY_KEYS: Lazy<Keys> = Lazy::new(|| match fs::read_to_string("keys") {
match fs::read_to_string("keys") { Ok(key) => Keys::from_str(&key).unwrap(),
Ok(key) => { Err(e) => {
Keys::from_str(&key).unwrap() eprintln!("{}", e);
} let keys = Keys::generate();
Err(e) => { fs::write("keys", keys.secret_key().unwrap().to_string());
eprintln!("{}", e); keys
let keys = Keys::generate();
fs::write("keys", keys.secret_key().unwrap().to_string());
keys
},
} }
}); });
@ -73,7 +67,7 @@ async fn main() {
let client = Client::new(MY_KEYS.deref()); let client = Client::new(MY_KEYS.deref());
client.add_relay("ws://localhost:4736").await; client.add_relay("ws://localhost:4736").await;
println!("My key: {}", MY_KEYS.public_key()); println!("My public key: {}", MY_KEYS.public_key());
//client.add_relay("wss://relay.damus.io").await; //client.add_relay("wss://relay.damus.io").await;
//client //client
// .add_relay_with_opts( // .add_relay_with_opts(
@ -193,10 +187,8 @@ async fn main() {
} }
} }
}, },
Some('-') => { Some('-') => tasks.add_note(&input[1..]),
tasks.add_note(&input[1..])
}
Some('>') | Some('<') => { Some('>') | Some('<') => {
tasks.update_state(&input[1..], |_| { tasks.update_state(&input[1..], |_| {

View File

@ -100,7 +100,13 @@ impl Task {
"state" => self.state().map(|s| s.to_string()), "state" => self.state().map(|s| s.to_string()),
"name" => Some(self.event.content.clone()), "name" => Some(self.event.content.clone()),
"time" => Some(self.time_tracked().to_string()), // TODO: format properly "time" => Some(self.time_tracked().to_string()), // TODO: format properly
"props" => Some(format!("{:?}", self.props.iter().map(|e| format!("{} kind {} '{}'", e.created_at, e.kind, e.content)).collect::<Vec<String>>())), "props" => Some(format!(
"{:?}",
self.props
.iter()
.map(|e| format!("{} kind {} '{}'", e.created_at, e.kind, e.content))
.collect::<Vec<String>>()
)),
"desc" | "description" => self.descriptions().fold(None, |total, s| { "desc" | "description" => self.descriptions().fold(None, |total, s| {
Some(match total { Some(match total {
None => s, None => s,

View File

@ -1,6 +1,5 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::iter::once; use std::iter::once;
use std::sync::mpsc;
use nostr_sdk::{Event, EventBuilder, EventId, Keys, Kind, Tag}; use nostr_sdk::{Event, EventBuilder, EventId, Keys, Kind, Tag};
@ -23,7 +22,7 @@ pub(crate) struct Tasks {
/// A filtered view of the current tasks /// A filtered view of the current tasks
view: Vec<EventId>, view: Vec<EventId>,
sender: EventSender sender: EventSender,
} }
impl Tasks { impl Tasks {
@ -34,12 +33,14 @@ impl Tasks {
position: None, position: None,
view: Default::default(), view: Default::default(),
depth: 1, depth: 1,
sender sender,
} }
} }
} }
impl Tasks { impl Tasks {
// Accessors
pub(crate) fn get_position(&self) -> Option<EventId> { pub(crate) fn get_position(&self) -> Option<EventId> {
self.position self.position
} }
@ -55,35 +56,77 @@ impl Tasks {
}) })
} }
pub(crate) fn set_filter(&mut self, view: Vec<EventId>) { // Parents
self.view = view
pub(crate) fn parent(&self, id: Option<EventId>) -> Option<EventId> {
id.and_then(|id| self.tasks.get(&id))
.and_then(|t| t.parent_id())
} }
fn resolve_tasks<'a>(&self, iter: impl IntoIterator<Item=&'a EventId>) -> Vec<&Task> { pub(crate) fn taskpath(&self, id: Option<EventId>) -> String {
join_tasks(self.traverse_up_from(id))
}
pub(crate) fn traverse_up_from(&self, id: Option<EventId>) -> ParentIterator {
ParentIterator {
tasks: &self.tasks,
current: id,
prev: None,
}
}
// Helpers
fn resolve_tasks<'a>(&self, iter: impl IntoIterator<Item = &'a EventId>) -> Vec<&Task> {
self.resolve_tasks_rec(iter, self.depth) self.resolve_tasks_rec(iter, self.depth)
} }
fn resolve_tasks_rec<'a>(&self, iter: impl IntoIterator<Item=&'a EventId>, depth: i8) -> Vec<&Task> { fn resolve_tasks_rec<'a>(
iter.into_iter().filter_map(|id| self.tasks.get(&id)).flat_map(|task| { &self,
let new_depth = depth - 1; iter: impl IntoIterator<Item = &'a EventId>,
if new_depth < 0 { depth: i8,
let tasks = self.resolve_tasks_rec(task.children.iter(), new_depth).into_iter().collect::<Vec<&Task>>(); ) -> Vec<&Task> {
if tasks.is_empty() { iter.into_iter()
vec![task] .filter_map(|id| self.tasks.get(&id))
.flat_map(|task| {
let new_depth = depth - 1;
if new_depth < 0 {
let tasks = self
.resolve_tasks_rec(task.children.iter(), new_depth)
.into_iter()
.collect::<Vec<&Task>>();
if tasks.is_empty() {
vec![task]
} else {
tasks
}
} else if new_depth > 0 {
self.resolve_tasks_rec(task.children.iter(), new_depth)
.into_iter()
.chain(once(task))
.collect()
} else { } else {
tasks vec![task]
} }
} else if new_depth > 0 { })
self.resolve_tasks_rec(task.children.iter(), new_depth).into_iter().chain(once(task)).collect() .collect()
} else { }
vec![task]
pub(crate) fn referenced_tasks<F: Fn(&mut Task)>(&mut self, event: &Event, f: F) {
for tag in event.tags.iter() {
if let Tag::Event { event_id, .. } = tag {
self.tasks.get_mut(event_id).map(|t| f(t));
} }
}).collect() }
} }
pub(crate) fn current_tasks(&self) -> Vec<&Task> { pub(crate) fn current_tasks(&self) -> Vec<&Task> {
if self.depth == 0 { if self.depth == 0 {
return self.position.and_then(|id| self.tasks.get(&id)).into_iter().collect(); return self
.position
.and_then(|id| self.tasks.get(&id))
.into_iter()
.collect();
} }
let res: Vec<&Task> = self.resolve_tasks(self.view.iter()); let res: Vec<&Task> = self.resolve_tasks(self.view.iter());
if res.len() > 0 { if res.len() > 0 {
@ -94,12 +137,24 @@ impl Tasks {
if self.depth > 8 { if self.depth > 8 {
self.tasks.values().collect() self.tasks.values().collect()
} else if self.depth == 1 { } else if self.depth == 1 {
self.tasks.values().filter(|t| t.parent_id() == None).collect() self.tasks
.values()
.filter(|t| t.parent_id() == None)
.collect()
} else { } else {
self.resolve_tasks(self.tasks.values().filter(|t| t.parent_id() == None).map(|t| &t.event.id)) self.resolve_tasks(
self.tasks
.values()
.filter(|t| t.parent_id() == None)
.map(|t| &t.event.id),
)
} }
}, },
|p| self.tasks.get(&p).map_or(Vec::new(), |t| self.resolve_tasks(t.children.iter())), |p| {
self.tasks
.get(&p)
.map_or(Vec::new(), |t| self.resolve_tasks(t.children.iter()))
},
) )
} }
@ -112,7 +167,10 @@ impl Tasks {
.iter() .iter()
.map(|p| match p.as_str() { .map(|p| match p.as_str() {
"path" => self.taskpath(Some(task.event.id)), "path" => self.taskpath(Some(task.event.id)),
"rpath" => join_tasks(self.traverse_up_from(Some(task.event.id)).take_while(|t| Some(t.event.id) != self.position)), "rpath" => join_tasks(
self.traverse_up_from(Some(task.event.id))
.take_while(|t| Some(t.event.id) != self.position)
),
"ttime" => self.total_time_tracked(&task.event.id).to_string(), "ttime" => self.total_time_tracked(&task.event.id).to_string(),
prop => task.get(prop).unwrap_or(String::new()), prop => task.get(prop).unwrap_or(String::new()),
}) })
@ -123,59 +181,10 @@ impl Tasks {
println!(); println!();
} }
pub(crate) fn make_task(&mut self, input: &str) -> Option<EventId> { // Movement and Selection
self.sender.submit(self.build_task(input)).map(|e| {
let id = e.id;
self.add_task(e);
id
})
}
pub(crate) fn build_task(&self, input: &str) -> EventBuilder { pub(crate) fn set_filter(&mut self, view: Vec<EventId>) {
let mut tags: Vec<Tag> = Vec::new(); self.view = view
self.position.inspect(|p| tags.push(Tag::event(*p)));
return match input.split_once(": ") {
None => EventBuilder::new(Kind::from(TASK_KIND), input, tags),
Some(s) => {
tags.append(
&mut s
.1
.split(" ")
.map(|t| Tag::Hashtag(t.to_string()))
.collect(),
);
EventBuilder::new(Kind::from(TASK_KIND), s.0, tags)
}
};
}
pub(crate) fn referenced_tasks<F: Fn(&mut Task)>(&mut self, event: &Event, f: F) {
for tag in event.tags.iter() {
if let Tag::Event { event_id, .. } = tag {
self.tasks.get_mut(event_id).map(|t| f(t));
}
}
}
pub(crate) fn add(&mut self, event: Event) {
if event.kind.as_u64() == 1621 {
self.add_task(event)
} else {
self.add_prop(&event)
}
}
pub(crate) fn add_task(&mut self, event: Event) {
self.referenced_tasks(&event, |t| { t.children.insert(event.id); });
if self.tasks.contains_key(&event.id) {
//eprintln!("Did not insert duplicate event {}", event.id);
} else {
self.tasks.insert(event.id, Task::new(event));
}
}
pub(crate) fn add_prop(&mut self, event: &Event) {
self.referenced_tasks(&event, |t| { t.props.insert(event.clone()); });
} }
pub(crate) fn move_up(&mut self) { pub(crate) fn move_up(&mut self) {
@ -208,37 +217,75 @@ impl Tasks {
}); });
} }
pub(crate) fn parent(&self, id: Option<EventId>) -> Option<EventId> { // Updates
id.and_then(|id| self.tasks.get(&id))
.and_then(|t| t.parent_id()) pub(crate) fn build_task(&self, input: &str) -> EventBuilder {
let mut tags: Vec<Tag> = Vec::new();
self.position.inspect(|p| tags.push(Tag::event(*p)));
return match input.split_once(": ") {
None => EventBuilder::new(Kind::from(TASK_KIND), input, tags),
Some(s) => {
tags.append(
&mut s
.1
.split(" ")
.map(|t| Tag::Hashtag(t.to_string()))
.collect(),
);
EventBuilder::new(Kind::from(TASK_KIND), s.0, tags)
}
};
} }
pub(crate) fn taskpath(&self, id: Option<EventId>) -> String { pub(crate) fn make_task(&mut self, input: &str) -> Option<EventId> {
join_tasks(self.traverse_up_from(id)) self.sender.submit(self.build_task(input)).map(|e| {
let id = e.id;
self.add_task(e);
id
})
} }
pub(crate) fn traverse_up_from(&self, id: Option<EventId>) -> ParentIterator { pub(crate) fn add(&mut self, event: Event) {
ParentIterator { if event.kind.as_u64() == 1621 {
tasks: &self.tasks, self.add_task(event)
current: id, } else {
prev: None, self.add_prop(&event)
} }
} }
pub(crate) fn add_task(&mut self, event: Event) {
self.referenced_tasks(&event, |t| {
t.children.insert(event.id);
});
if self.tasks.contains_key(&event.id) {
//eprintln!("Did not insert duplicate event {}", event.id);
} else {
self.tasks.insert(event.id, Task::new(event));
}
}
pub(crate) fn add_prop(&mut self, event: &Event) {
self.referenced_tasks(&event, |t| {
t.props.insert(event.clone());
});
}
pub(crate) fn update_state_for<F>(&mut self, id: &EventId, comment: &str, f: F) -> Option<Event> pub(crate) fn update_state_for<F>(&mut self, id: &EventId, comment: &str, f: F) -> Option<Event>
where where
F: FnOnce(&Task) -> Option<State>, F: FnOnce(&Task) -> Option<State>,
{ {
self.tasks.get_mut(id).and_then(|task| { self.tasks.get_mut(id).and_then(|task| {
f(task).and_then(|state| { f(task)
self.sender.submit(EventBuilder::new( .and_then(|state| {
state.kind(), self.sender.submit(EventBuilder::new(
comment, state.kind(),
vec![Tag::event(task.event.id)], comment,
)) vec![Tag::event(task.event.id)],
}).inspect(|e| { ))
task.props.insert(e.clone()); })
}) .inspect(|e| {
task.props.insert(e.clone());
})
}) })
} }
@ -266,14 +313,15 @@ impl Tasks {
} }
} }
pub(crate) fn join_tasks<'a>(iter: impl IntoIterator<Item=&'a Task>) -> String{ pub(crate) fn join_tasks<'a>(iter: impl IntoIterator<Item = &'a Task>) -> String {
iter.into_iter() iter.into_iter()
.map(|t| t.event.content.clone()) .map(|t| t.event.content.clone())
.fold(None, |acc, val| Some(acc.map_or_else(|| val.clone(), |cur| format!("{}>{}", val, cur)))) .fold(None, |acc, val| {
Some(acc.map_or_else(|| val.clone(), |cur| format!("{}>{}", val, cur)))
})
.unwrap_or(String::new()) .unwrap_or(String::new())
} }
struct ParentIterator<'a> { struct ParentIterator<'a> {
tasks: &'a TaskMap, tasks: &'a TaskMap,
current: Option<EventId>, current: Option<EventId>,
@ -295,6 +343,8 @@ impl<'a> Iterator for ParentIterator<'a> {
#[test] #[test]
fn test_depth() { fn test_depth() {
use std::sync::mpsc;
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
let mut tasks = Tasks::from(EventSender { let mut tasks = Tasks::from(EventSender {
tx, tx,
@ -305,7 +355,7 @@ fn test_depth() {
assert_eq!(tasks.current_tasks().len(), 1); assert_eq!(tasks.current_tasks().len(), 1);
tasks.depth = 0; tasks.depth = 0;
assert_eq!(tasks.current_tasks().len(), 0); assert_eq!(tasks.current_tasks().len(), 0);
tasks.move_to(t1); tasks.move_to(t1);
tasks.depth = 2; tasks.depth = 2;
assert_eq!(tasks.current_tasks().len(), 0); assert_eq!(tasks.current_tasks().len(), 0);
@ -313,7 +363,7 @@ fn test_depth() {
assert_eq!(tasks.current_tasks().len(), 1); assert_eq!(tasks.current_tasks().len(), 1);
let t3 = tasks.make_task("t3"); let t3 = tasks.make_task("t3");
assert_eq!(tasks.current_tasks().len(), 2); assert_eq!(tasks.current_tasks().len(), 2);
tasks.move_to(t2); tasks.move_to(t2);
assert_eq!(tasks.current_tasks().len(), 0); assert_eq!(tasks.current_tasks().len(), 0);
let t4 = tasks.make_task("t4"); let t4 = tasks.make_task("t4");
@ -350,4 +400,4 @@ fn test_depth() {
assert_eq!(tasks.current_tasks().len(), 4); assert_eq!(tasks.current_tasks().len(), 4);
tasks.depth = -1; tasks.depth = -1;
assert_eq!(tasks.current_tasks().len(), 2); assert_eq!(tasks.current_tasks().len(), 2);
} }