tangled
alpha
login
or
join now
ngp.computer
/
tsk
A file-based task manager
0
fork
atom
overview
issues
pulls
pipelines
ADD: completion and edit commands
ngp.computer
1 year ago
6f57b846
9c01bd41
+65
-16
2 changed files
expand all
collapse all
unified
split
src
main.rs
workspace.rs
+34
-9
src/main.rs
···
2
mod stack;
3
mod util;
4
mod workspace;
0
0
5
use std::path::PathBuf;
6
use std::{env::current_dir, io::Read};
7
-
use clap_complete::Shell;
8
use workspace::Workspace;
9
10
//use smol;
11
//use iocraft::prelude::*;
12
-
use clap::{Args, Parser, Subcommand};
13
use edit::edit as open_editor;
14
15
fn default_dir() -> PathBuf {
···
59
60
Edit {
61
#[arg(short = 't')]
62
-
task_id: u32
63
},
64
65
Completion {
66
#[arg(short = 's')]
67
-
shell: Shell
0
0
0
0
0
0
68
}
0
69
}
70
71
#[derive(Args)]
···
88
command_push(cli.dir.unwrap_or(default_dir()), edit, body, title)
89
}
90
Commands::List { all, count } => command_list(cli.dir.unwrap_or(default_dir()), all, count),
91
-
Commands::Swap => command_swap(cli.dir.unwrap_or(default_dir()))
0
0
92
}
93
}
94
···
98
99
fn command_push(dir: PathBuf, edit: bool, body: Option<String>, title: Title) {
100
let workspace = Workspace::from_path(dir).expect("Unable to find .tsk dir");
101
-
let title = if let Some(title) = title.title {
102
title
103
} else if let Some(title) = title.title_simple {
104
let joined = title.join(" ");
···
116
.expect("Failed to read stdin");
117
}
118
if edit {
119
-
body = open_editor(format!("{title}\n\n{body}")).expect("Failed to edit file");
0
0
0
0
120
}
121
let task = workspace
122
.new_task(title, body)
···
147
workspace.swap_top().expect("swap to work");
148
}
149
150
-
fn command_edit(dir: PathBuf) {
151
let workspace = Workspace::from_path(dir).expect("Unable to find .tsk dir");
152
-
let task = workspace.
0
0
0
0
0
0
0
0
0
0
0
153
}
···
2
mod stack;
3
mod util;
4
mod workspace;
5
+
use clap_complete::{generate, Shell};
6
+
use std::io;
7
use std::path::PathBuf;
8
use std::{env::current_dir, io::Read};
0
9
use workspace::Workspace;
10
11
//use smol;
12
//use iocraft::prelude::*;
13
+
use clap::{Args, CommandFactory, Parser, Subcommand};
14
use edit::edit as open_editor;
15
16
fn default_dir() -> PathBuf {
···
60
61
Edit {
62
#[arg(short = 't')]
63
+
task_id: u32,
64
},
65
66
Completion {
67
#[arg(short = 's')]
68
+
shell: Shell,
69
+
},
70
+
71
+
/*
72
+
Drop {
73
+
#[arg(short = 't')]
74
+
task_id: Option<u32>,
75
}
76
+
*/
77
}
78
79
#[derive(Args)]
···
96
command_push(cli.dir.unwrap_or(default_dir()), edit, body, title)
97
}
98
Commands::List { all, count } => command_list(cli.dir.unwrap_or(default_dir()), all, count),
99
+
Commands::Swap => command_swap(cli.dir.unwrap_or(default_dir())),
100
+
Commands::Edit { task_id } => command_edit(cli.dir.unwrap_or(default_dir()), task_id),
101
+
Commands::Completion { shell } => command_completion(shell),
102
}
103
}
104
···
108
109
fn command_push(dir: PathBuf, edit: bool, body: Option<String>, title: Title) {
110
let workspace = Workspace::from_path(dir).expect("Unable to find .tsk dir");
111
+
let mut title = if let Some(title) = title.title {
112
title
113
} else if let Some(title) = title.title_simple {
114
let joined = title.join(" ");
···
126
.expect("Failed to read stdin");
127
}
128
if edit {
129
+
let new_content = open_editor(format!("{title}\n\n{body}")).expect("Failed to edit file");
130
+
if let Some(content) = new_content.split_once("\n") {
131
+
title = content.0.to_string();
132
+
body = content.1.to_string();
133
+
}
134
}
135
let task = workspace
136
.new_task(title, body)
···
161
workspace.swap_top().expect("swap to work");
162
}
163
164
+
fn command_edit(dir: PathBuf, id: u32) {
165
let workspace = Workspace::from_path(dir).expect("Unable to find .tsk dir");
166
+
let mut task = workspace.task(id.into()).expect("To read task from disk");
167
+
let new_content =
168
+
open_editor(format!("{}\n\n{}", task.title.trim(), task.body.trim())).expect("Failed to edit file");
169
+
if let Some((title, body)) = new_content.split_once("\n") {
170
+
task.title = title.to_string();
171
+
task.body = body.to_string();
172
+
task.save().expect("Failed to save task");
173
+
}
174
+
}
175
+
176
+
fn command_completion(shell: Shell) {
177
+
generate(shell, &mut Cli::command(), "tsk", &mut io::stdout())
178
}
+31
-7
src/workspace.rs
···
6
use crate::util;
7
use std::fmt::Display;
8
use std::fs::File;
9
-
use std::io::{Read, Seek};
10
use std::path::PathBuf;
11
use std::str::FromStr;
12
use std::{fs::OpenOptions, io::Write};
···
30
impl Display for Id {
31
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32
write!(f, "tsk-{}", self.0)
0
0
0
0
0
0
33
}
34
}
35
···
82
// reset the files contents
83
file.set_len(0)?;
84
// TODO: figure out if this is necessary
85
-
file.seek(std::io::SeekFrom::Start(0))?;
86
// store the *next* if
87
file.write_all(format!("{}\n", id + 1).as_bytes())?;
88
Ok(Id(id))
···
105
}
106
107
pub fn task(&self, id: Id) -> Result<Task> {
108
-
let mut file = util::flopen(
109
self.path.join("tasks").join(format!("tsk-{}.tsk", id.0)),
110
FlockArg::LockExclusive,
111
)?;
112
-
0
0
0
0
0
0
0
0
0
0
0
113
}
114
115
pub fn read_stack(&self, count: Option<usize>) -> Result<TaskStack> {
···
138
pub file: Flock<File>,
139
}
140
141
-
#[cfg(test)]
142
-
mod test {
143
-
fn test_next_id() {}
0
0
0
0
0
0
0
144
}
···
6
use crate::util;
7
use std::fmt::Display;
8
use std::fs::File;
9
+
use std::io::{BufRead as _, BufReader, Read, Seek, SeekFrom};
10
use std::path::PathBuf;
11
use std::str::FromStr;
12
use std::{fs::OpenOptions, io::Write};
···
30
impl Display for Id {
31
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32
write!(f, "tsk-{}", self.0)
33
+
}
34
+
}
35
+
36
+
impl From<u32> for Id {
37
+
fn from(value: u32) -> Self {
38
+
Id(value)
39
}
40
}
41
···
88
// reset the files contents
89
file.set_len(0)?;
90
// TODO: figure out if this is necessary
91
+
file.seek(SeekFrom::Start(0))?;
92
// store the *next* if
93
file.write_all(format!("{}\n", id + 1).as_bytes())?;
94
Ok(Id(id))
···
111
}
112
113
pub fn task(&self, id: Id) -> Result<Task> {
114
+
let file = util::flopen(
115
self.path.join("tasks").join(format!("tsk-{}.tsk", id.0)),
116
FlockArg::LockExclusive,
117
)?;
118
+
let mut title = String::new();
119
+
let mut body = String::new();
120
+
let mut reader = BufReader::new(&*file);
121
+
reader.read_line(&mut title)?;
122
+
reader.read_to_string(&mut body)?;
123
+
drop(reader);
124
+
Ok(Task {
125
+
id,
126
+
title,
127
+
body,
128
+
file,
129
+
})
130
}
131
132
pub fn read_stack(&self, count: Option<usize>) -> Result<TaskStack> {
···
155
pub file: Flock<File>,
156
}
157
158
+
impl Task {
159
+
/// Consumes a task and saves it to disk.
160
+
pub fn save(mut self) -> Result<()> {
161
+
self.file.set_len(0)?;
162
+
self.file.seek(SeekFrom::Start(0))?;
163
+
self.file.write_all(self.title.trim().as_bytes())?;
164
+
self.file.write_all(b"\n\n")?;
165
+
self.file.write_all(self.body.trim().as_bytes())?;
166
+
Ok(())
167
+
}
168
}