A file-based task manager

WIP: task stack

+77 -15
+51
src/buffer.rs
··· 1 + use std::{ 2 + collections::BinaryHeap, 3 + sync::{Condvar, Mutex}, 4 + }; 5 + struct PooledHandle<'a, T: Ord + Default> { 6 + parent: &'a Pool<T>, 7 + item: Option<T>, 8 + } 9 + 10 + struct Pool<T: Ord + Default>(Mutex<BinaryHeap<T>>, Condvar); 11 + 12 + impl<'a, T: Ord + Default> Pool<T> { 13 + pub fn give(&self, item: T) { 14 + self.0.lock().expect("Poisoned lock").push(item) 15 + } 16 + 17 + /// Retrieves 18 + pub fn take(&self) -> Option<T> { 19 + self.0.lock().expect("Poisoned lock").pop() 20 + } 21 + 22 + pub fn lease(&self) -> PooledHandle<'a, T> { 23 + let guard = self.0.lock().expect("Poisoned lock"); 24 + match guard.pop() { 25 + Some(item) => PooledHandle { 26 + parent: self, 27 + item: Some(item), 28 + }, 29 + None => { 30 + let mut item = None; 31 + while item.is_none() { 32 + let guard = self.1.wait(guard).expect("Poisoned lock"); 33 + item = guard.pop(); 34 + } 35 + 36 + }, 37 + } 38 + } 39 + } 40 + 41 + impl<T: Ord> AsRef<T> for PooledHandle<'_, T> { 42 + fn as_ref(&self) -> &T { 43 + &self.item 44 + } 45 + } 46 + 47 + impl<T: Ord> Drop for PooledHandle<'_, T> { 48 + fn drop(&mut self) { 49 + self.parent.give(self.item) 50 + } 51 + }
+1
src/main.rs
··· 2 2 mod workspace; 3 3 mod stack; 4 4 mod util; 5 + mod buffer; 5 6 use std::path::PathBuf; 6 7 use std::{env::current_dir, io::Read}; 7 8 use workspace::Workspace;
+6 -12
src/stack.rs
··· 3 3 4 4 use crate::errors::{Error, Result}; 5 5 use crate::util; 6 - use std::io::{self, BufRead}; 7 - use std::num::ParseIntError; 8 - use std::{fs::File, io::Read, path::PathBuf}; 6 + use std::io::{self, BufRead, Read}; 7 + use std::{fs::File, path::PathBuf}; 9 8 10 9 use nix::fcntl::{Flock, FlockArg}; 11 10 12 - use crate::workspace::Id; 13 - 14 - const INDEXFILE: &str = "index"; 15 - const TITLECACHEFILE: &str = "cache"; 11 + use crate::workspace::{Id, Workspace}; 16 12 17 13 struct StackItem { 18 14 id: Id, ··· 28 24 } 29 25 30 26 impl StackItem { 31 - fn from_reader(reader: &mut impl BufRead) -> Result<Self> { 27 + fn from_reader(workspace_path: &PathBuf, reader: &mut impl BufRead) -> Result<Self> { 32 28 let mut buf = String::new(); 33 29 reader.read_line(&mut buf)?; 34 30 if buf.is_empty() { ··· 38 34 ))); 39 35 } 40 36 let (id, next) = Self::parse(&buf)?; 37 + let title = util::flopen(workspace_path.join("tasks").join(id), mode) 41 38 todo!(); 42 39 } 43 40 ··· 63 60 } 64 61 65 62 impl TaskStack { 66 - fn from_tskdir(path: &PathBuf) -> Self { 67 - let index = util::flopen(&path.join(INDEXFILE), FlockArg::LockExclusive); 68 - let cache = util::flopen(&path.join(TITLECACHEFILE), FlockArg::LockShared); 69 - 63 + fn from_tskdir(path: &PathBuf) -> Result<Self> { 70 64 todo!() 71 65 } 72 66 }
+1 -1
src/util.rs
··· 6 6 7 7 use nix::fcntl::{Flock, FlockArg}; 8 8 9 - pub fn flopen(path: &PathBuf, mode: FlockArg) -> Result<Flock<File>> { 9 + pub fn flopen(path: PathBuf, mode: FlockArg) -> Result<Flock<File>> { 10 10 let file = OpenOptions::new() 11 11 .read(true) 12 12 .write(true)
+18 -2
src/workspace.rs
··· 2 2 use nix::fcntl::{Flock, FlockArg}; 3 3 4 4 use crate::errors::{Error, Result}; 5 + use crate::stack::TaskStack; 5 6 use crate::util; 6 7 use std::fs::File; 7 8 use std::io::{Read, Seek}; ··· 9 10 use std::str::FromStr; 10 11 use std::{fs::OpenOptions, io::Write}; 11 12 13 + const INDEXFILE: &str = "index"; 14 + const TITLECACHEFILE: &str = "cache"; 12 15 /// A unique identifier for a task. When referenced in text, it is prefixed with `tsk-`. 13 16 pub struct Id(u32); 14 17 ··· 58 61 } 59 62 60 63 pub fn next_id(&self) -> Result<Id> { 61 - let mut file = util::flopen(&self.path.join("next"), FlockArg::LockExclusive)?; 64 + let mut file = util::flopen(self.path.join("next"), FlockArg::LockExclusive)?; 62 65 let mut buf = String::new(); 63 66 file.read_to_string(&mut buf)?; 64 67 let id = buf.trim().parse::<u32>()?; ··· 75 78 // TODO: we could improperly increment the id if the task is not written to disk/errors 76 79 let id = self.next_id()?; 77 80 let mut file = util::flopen( 78 - &self.path.join("tasks").join(format!("tsk-{}.tsk", id.0)), 81 + self.path.join("tasks").join(format!("tsk-{}.tsk", id.0)), 79 82 FlockArg::LockExclusive, 80 83 )?; 81 84 file.write_all(format!("{title}\n\n{body}").as_bytes())?; ··· 85 88 body, 86 89 file, 87 90 }) 91 + } 92 + 93 + fn read_stack(&self) -> Result<TaskStack> { 94 + let mut index = String::new(); 95 + let mut cache = String::new(); 96 + let mut index_file = util::flopen(self.path.join(INDEXFILE), FlockArg::LockExclusive)?; 97 + let mut cache_file = util::flopen(self.path.join(TITLECACHEFILE), FlockArg::LockShared)?; 98 + index_file.read_to_string(&mut index)?; 99 + for line in index.lines() { 100 + 101 + } 102 + 103 + todo!(); 88 104 } 89 105 } 90 106