Compare commits
No commits in common. "aa8f83e3211b521dd1ad065ae0e69d3691e5a1f6" and "a8c41ec8335b3a819dddb57238b1f0d98da28ba2" have entirely different histories.
aa8f83e321
...
a8c41ec833
7 changed files with 10 additions and 364 deletions
11
README.md
11
README.md
|
|
@ -21,10 +21,7 @@ Within files, `@`-prefixed markers at the beginning of paragraphs or headings de
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
- `streamd` / `streamd new` — Create a new timestamped markdown entry, opening your editor
|
- `streamd` / `streamd new` — Create a new timestamped markdown entry, opening your editor
|
||||||
- `streamd todo` — Show all open tasks (shards with `@Task` markers), numbered for easy reference
|
- `streamd todo` — Show all open tasks (shards with `@Task` markers)
|
||||||
- `streamd todo N edit` — Edit task N in your editor, jumping to the task's line
|
|
||||||
- `streamd todo N done` — Mark task N as done by inserting `@Done` after `@Task`
|
|
||||||
- `streamd todo --show-future` — Include tasks with future dates in the listing
|
|
||||||
- `streamd edit [number]` — Edit a stream file by index (most recent first)
|
- `streamd edit [number]` — Edit a stream file by index (most recent first)
|
||||||
- `streamd timesheet` — Generate time reports from `@Timesheet` markers
|
- `streamd timesheet` — Generate time reports from `@Timesheet` markers
|
||||||
|
|
||||||
|
|
@ -63,8 +60,4 @@ The timesheet command will calculate expected vs actual working hours based on t
|
||||||
|
|
||||||
Running `streamd` opens your editor to create a new entry. After saving, the file is renamed based on its timestamp and any markers found in the content.
|
Running `streamd` opens your editor to create a new entry. After saving, the file is renamed based on its timestamp and any markers found in the content.
|
||||||
|
|
||||||
Running `streamd todo` finds all shards marked as open tasks and displays them numbered in your terminal. Tasks with future dates are hidden by default (use `--show-future` to include them). Tasks are sorted by date with oldest first (task 1 is the oldest).
|
Running `streamd todo` finds all shards marked as open tasks and displays them as rich-formatted panels in your terminal.
|
||||||
|
|
||||||
You can quickly edit or complete tasks by number:
|
|
||||||
- `streamd todo 1 edit` opens task 1 in your editor at the correct line
|
|
||||||
- `streamd todo 1 done` marks task 1 as done by inserting `@Done` after `@Task`
|
|
||||||
|
|
|
||||||
|
|
@ -387,40 +387,11 @@ Provide recursive search through the shard tree:
|
||||||
| Command | Description |
|
| Command | Description |
|
||||||
|---------|-------------|
|
|---------|-------------|
|
||||||
| `streamd new` | Create new timestamped file, open editor, rename with markers on close |
|
| `streamd new` | Create new timestamped file, open editor, rename with markers on close |
|
||||||
| `streamd todo` | List all shards with `task: "open"`, numbered, hiding future tasks |
|
| `streamd todo` | List all shards with `task: "open"` |
|
||||||
| `streamd todo --show-future` | Include tasks with future dates in the todo listing |
|
|
||||||
| `streamd todo N edit` | Edit task N in editor, cursor positioned at task line |
|
|
||||||
| `streamd todo N done` | Mark task N as done by inserting `@Done` after `@Task` |
|
|
||||||
| `streamd edit [n]` | Edit nth file (supports negative indexing for recent files) |
|
| `streamd edit [n]` | Edit nth file (supports negative indexing for recent files) |
|
||||||
| `streamd timesheet` | Generate formatted timesheet report with expected/actual hours |
|
| `streamd timesheet` | Generate formatted timesheet report with expected/actual hours |
|
||||||
| `streamd completions <shell>` | Generate shell completions (bash, zsh, fish, elvish, powershell) |
|
| `streamd completions <shell>` | Generate shell completions (bash, zsh, fish, elvish, powershell) |
|
||||||
|
|
||||||
### R21: Todo Command Behavior
|
|
||||||
|
|
||||||
**Task Numbering:**
|
|
||||||
- Tasks are numbered starting from 1 (oldest task = 1)
|
|
||||||
- Tasks are sorted by their `moment` field in ascending order
|
|
||||||
- Output format: `[N] --- file.md:line ---` followed by task content
|
|
||||||
|
|
||||||
**Future Task Filtering:**
|
|
||||||
- By default, tasks with `moment > now` are hidden from the listing
|
|
||||||
- The `--show-future` flag includes all tasks regardless of their moment
|
|
||||||
- When using `todo N edit` or `todo N done`, all tasks (including future) are considered for number lookup
|
|
||||||
|
|
||||||
**Edit Action (`todo N edit`):**
|
|
||||||
- Opens the task's file in `$EDITOR` (defaults to `vi`)
|
|
||||||
- Uses `+LINE` argument to position cursor at task's start line
|
|
||||||
- Errors if N is 0 or exceeds the task count
|
|
||||||
|
|
||||||
**Done Action (`todo N done`):**
|
|
||||||
- Reads the file and modifies the line at task's start_line
|
|
||||||
- Inserts ` @Done` immediately after `@Task`
|
|
||||||
- Preserves trailing newline if the original file had one
|
|
||||||
- Errors if:
|
|
||||||
- N is 0 or exceeds the task count
|
|
||||||
- Multiple `@Task` markers found on the same line
|
|
||||||
- No `@Task` marker found on the expected line
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Application Configuration
|
## Application Configuration
|
||||||
|
|
|
||||||
|
|
@ -13,34 +13,13 @@ pub struct Cli {
|
||||||
pub command: Option<Commands>,
|
pub command: Option<Commands>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
pub enum TodoAction {
|
|
||||||
/// Edit a task by its number
|
|
||||||
Edit {
|
|
||||||
/// Task number to edit
|
|
||||||
number: usize,
|
|
||||||
},
|
|
||||||
/// Mark a task as done
|
|
||||||
Done {
|
|
||||||
/// Task number to mark as done
|
|
||||||
number: usize,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub enum Commands {
|
pub enum Commands {
|
||||||
/// Create a new stream file
|
/// Create a new stream file
|
||||||
New,
|
New,
|
||||||
|
|
||||||
/// Display open tasks
|
/// Display open tasks
|
||||||
Todo {
|
Todo,
|
||||||
/// Show tasks with dates in the future
|
|
||||||
#[arg(long)]
|
|
||||||
show_future: bool,
|
|
||||||
|
|
||||||
#[command(subcommand)]
|
|
||||||
action: Option<TodoAction>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Edit a stream file by position
|
/// Edit a stream file by position
|
||||||
Edit {
|
Edit {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
use chrono::Utc;
|
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use crate::config::Settings;
|
use crate::config::Settings;
|
||||||
|
|
@ -35,26 +33,10 @@ fn all_files() -> Result<Vec<LocalizedShard>, StreamdError> {
|
||||||
Ok(shards)
|
Ok(shards)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn collect_open_tasks(show_future: bool) -> Result<Vec<LocalizedShard>, StreamdError> {
|
pub fn run() -> Result<(), StreamdError> {
|
||||||
let all_shards = all_files()?;
|
let all_shards = all_files()?;
|
||||||
let now = Utc::now();
|
|
||||||
|
|
||||||
let mut tasks: Vec<LocalizedShard> = find_shard_by_position(&all_shards, "task", "open")
|
for task_shard in find_shard_by_position(&all_shards, "task", "open") {
|
||||||
.into_iter()
|
|
||||||
.filter(|shard| show_future || shard.moment <= now)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Sort by moment ascending (oldest first = task 1)
|
|
||||||
tasks.sort_by(|a, b| a.moment.cmp(&b.moment));
|
|
||||||
|
|
||||||
Ok(tasks)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run_list(show_future: bool) -> Result<(), StreamdError> {
|
|
||||||
let tasks = collect_open_tasks(show_future)?;
|
|
||||||
|
|
||||||
for (index, task_shard) in tasks.iter().enumerate() {
|
|
||||||
let task_number = index + 1; // 1-indexed
|
|
||||||
if let Some(file_path) = task_shard.location.get("file") {
|
if let Some(file_path) = task_shard.location.get("file") {
|
||||||
let content = fs::read_to_string(file_path)?;
|
let content = fs::read_to_string(file_path)?;
|
||||||
let lines: Vec<&str> = content.lines().collect();
|
let lines: Vec<&str> = content.lines().collect();
|
||||||
|
|
@ -62,10 +44,7 @@ pub fn run_list(show_future: bool) -> Result<(), StreamdError> {
|
||||||
let start = task_shard.start_line.saturating_sub(1);
|
let start = task_shard.start_line.saturating_sub(1);
|
||||||
let end = std::cmp::min(task_shard.end_line, lines.len());
|
let end = std::cmp::min(task_shard.end_line, lines.len());
|
||||||
|
|
||||||
println!(
|
println!("--- {}:{} ---", file_path, task_shard.start_line);
|
||||||
"[{}] --- {}:{} ---",
|
|
||||||
task_number, file_path, task_shard.start_line
|
|
||||||
);
|
|
||||||
for line in &lines[start..end] {
|
for line in &lines[start..end] {
|
||||||
println!("{}", line);
|
println!("{}", line);
|
||||||
}
|
}
|
||||||
|
|
@ -75,257 +54,3 @@ pub fn run_list(show_future: bool) -> Result<(), StreamdError> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_edit(number: usize) -> Result<(), StreamdError> {
|
|
||||||
// Always include all tasks for edit (user might want to edit a future task)
|
|
||||||
let tasks = collect_open_tasks(true)?;
|
|
||||||
|
|
||||||
if number == 0 || number > tasks.len() {
|
|
||||||
return Err(StreamdError::InvalidTaskNumber(number, tasks.len()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let task = &tasks[number - 1]; // Convert to 0-indexed
|
|
||||||
let file_path = task
|
|
||||||
.location
|
|
||||||
.get("file")
|
|
||||||
.ok_or(StreamdError::MissingFilePath)?;
|
|
||||||
|
|
||||||
let editor = std::env::var("EDITOR").unwrap_or_else(|_| "vi".to_string());
|
|
||||||
let line_arg = format!("+{}", task.start_line);
|
|
||||||
|
|
||||||
let status = Command::new(&editor)
|
|
||||||
.arg(&line_arg)
|
|
||||||
.arg(file_path)
|
|
||||||
.status()?;
|
|
||||||
|
|
||||||
if !status.success() {
|
|
||||||
return Err(StreamdError::IoError(std::io::Error::other(
|
|
||||||
"Editor exited with non-zero status",
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run_done(number: usize) -> Result<(), StreamdError> {
|
|
||||||
// Always include all tasks for done (user might want to mark a future task as done)
|
|
||||||
let tasks = collect_open_tasks(true)?;
|
|
||||||
|
|
||||||
if number == 0 || number > tasks.len() {
|
|
||||||
return Err(StreamdError::InvalidTaskNumber(number, tasks.len()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let task = &tasks[number - 1];
|
|
||||||
let file_path = task
|
|
||||||
.location
|
|
||||||
.get("file")
|
|
||||||
.ok_or(StreamdError::MissingFilePath)?;
|
|
||||||
|
|
||||||
let content = fs::read_to_string(file_path)?;
|
|
||||||
let mut lines: Vec<String> = content.lines().map(String::from).collect();
|
|
||||||
|
|
||||||
// Find the line containing @Task (should be at start_line)
|
|
||||||
let task_line_idx = task.start_line.saturating_sub(1);
|
|
||||||
if task_line_idx >= lines.len() {
|
|
||||||
return Err(StreamdError::InvalidLineNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
let line = &lines[task_line_idx];
|
|
||||||
|
|
||||||
// Check for multiple @Task occurrences
|
|
||||||
let task_count = line.matches("@Task").count();
|
|
||||||
if task_count > 1 {
|
|
||||||
return Err(StreamdError::MultipleTaskMarkers(
|
|
||||||
file_path.clone(),
|
|
||||||
task.start_line,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if task_count == 0 {
|
|
||||||
return Err(StreamdError::NoTaskMarker(
|
|
||||||
file_path.clone(),
|
|
||||||
task.start_line,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert @Done after @Task
|
|
||||||
let new_line = line.replacen("@Task", "@Task @Done", 1);
|
|
||||||
lines[task_line_idx] = new_line;
|
|
||||||
|
|
||||||
// Write back to file, preserving trailing newline if present
|
|
||||||
let new_content = if content.ends_with('\n') {
|
|
||||||
format!("{}\n", lines.join("\n"))
|
|
||||||
} else {
|
|
||||||
lines.join("\n")
|
|
||||||
};
|
|
||||||
fs::write(file_path, new_content)?;
|
|
||||||
|
|
||||||
println!("Marked task {} as done", number);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run() -> Result<(), StreamdError> {
|
|
||||||
run_list(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use chrono::{Duration, TimeZone};
|
|
||||||
use indexmap::IndexMap;
|
|
||||||
|
|
||||||
fn make_task_shard(moment: chrono::DateTime<Utc>, file: &str) -> LocalizedShard {
|
|
||||||
let mut location = IndexMap::new();
|
|
||||||
location.insert("file".to_string(), file.to_string());
|
|
||||||
location.insert("task".to_string(), "open".to_string());
|
|
||||||
|
|
||||||
LocalizedShard {
|
|
||||||
markers: vec!["Task".to_string()],
|
|
||||||
tags: vec![],
|
|
||||||
start_line: 1,
|
|
||||||
end_line: 1,
|
|
||||||
moment,
|
|
||||||
location,
|
|
||||||
children: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_insert_done_after_task() {
|
|
||||||
let line = "Some content @Task with more text";
|
|
||||||
let result = line.replacen("@Task", "@Task @Done", 1);
|
|
||||||
assert_eq!(result, "Some content @Task @Done with more text");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_insert_done_at_line_end() {
|
|
||||||
let line = "Some content @Task";
|
|
||||||
let result = line.replacen("@Task", "@Task @Done", 1);
|
|
||||||
assert_eq!(result, "Some content @Task @Done");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_insert_done_only_first_task() {
|
|
||||||
let line = "Some @Task content @Task again";
|
|
||||||
let result = line.replacen("@Task", "@Task @Done", 1);
|
|
||||||
assert_eq!(result, "Some @Task @Done content @Task again");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_task_count_single() {
|
|
||||||
let line = "Some content @Task with more text";
|
|
||||||
assert_eq!(line.matches("@Task").count(), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_task_count_multiple() {
|
|
||||||
let line = "Some @Task content @Task again";
|
|
||||||
assert_eq!(line.matches("@Task").count(), 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_task_count_none() {
|
|
||||||
let line = "Some content without task marker";
|
|
||||||
assert_eq!(line.matches("@Task").count(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_filter_future_tasks_excludes_future_when_show_future_false() {
|
|
||||||
let now = Utc::now();
|
|
||||||
let past = now - Duration::hours(1);
|
|
||||||
let future = now + Duration::hours(1);
|
|
||||||
|
|
||||||
let tasks = vec![
|
|
||||||
make_task_shard(past, "past.md"),
|
|
||||||
make_task_shard(future, "future.md"),
|
|
||||||
];
|
|
||||||
|
|
||||||
let filtered: Vec<_> = tasks
|
|
||||||
.into_iter()
|
|
||||||
.filter(|shard| shard.moment <= now)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
assert_eq!(filtered.len(), 1);
|
|
||||||
assert_eq!(filtered[0].location.get("file").unwrap(), "past.md");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_filter_future_tasks_includes_all_when_show_future_true() {
|
|
||||||
let now = Utc::now();
|
|
||||||
let past = now - Duration::hours(1);
|
|
||||||
let future = now + Duration::hours(1);
|
|
||||||
|
|
||||||
let tasks = vec![
|
|
||||||
make_task_shard(past, "past.md"),
|
|
||||||
make_task_shard(future, "future.md"),
|
|
||||||
];
|
|
||||||
|
|
||||||
let filtered: Vec<_> = tasks.into_iter().filter(|_| true).collect();
|
|
||||||
|
|
||||||
assert_eq!(filtered.len(), 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_sort_tasks_by_moment_ascending() {
|
|
||||||
let oldest = Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap();
|
|
||||||
let middle = Utc.with_ymd_and_hms(2021, 1, 1, 0, 0, 0).unwrap();
|
|
||||||
let newest = Utc.with_ymd_and_hms(2022, 1, 1, 0, 0, 0).unwrap();
|
|
||||||
|
|
||||||
let mut tasks = [
|
|
||||||
make_task_shard(newest, "newest.md"),
|
|
||||||
make_task_shard(oldest, "oldest.md"),
|
|
||||||
make_task_shard(middle, "middle.md"),
|
|
||||||
];
|
|
||||||
|
|
||||||
tasks.sort_by(|a, b| a.moment.cmp(&b.moment));
|
|
||||||
|
|
||||||
assert_eq!(tasks[0].location.get("file").unwrap(), "oldest.md");
|
|
||||||
assert_eq!(tasks[1].location.get("file").unwrap(), "middle.md");
|
|
||||||
assert_eq!(tasks[2].location.get("file").unwrap(), "newest.md");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_preserve_trailing_newline() {
|
|
||||||
let content_with_newline = "line1\nline2\n";
|
|
||||||
let content_without_newline = "line1\nline2";
|
|
||||||
|
|
||||||
assert!(content_with_newline.ends_with('\n'));
|
|
||||||
assert!(!content_without_newline.ends_with('\n'));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_invalid_task_number_zero() {
|
|
||||||
let result = StreamdError::InvalidTaskNumber(0, 5);
|
|
||||||
assert_eq!(
|
|
||||||
result.to_string(),
|
|
||||||
"Invalid task number 0: only 5 tasks available"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_invalid_task_number_exceeds_count() {
|
|
||||||
let result = StreamdError::InvalidTaskNumber(10, 3);
|
|
||||||
assert_eq!(
|
|
||||||
result.to_string(),
|
|
||||||
"Invalid task number 10: only 3 tasks available"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_multiple_task_markers_error_message() {
|
|
||||||
let result = StreamdError::MultipleTaskMarkers("/path/file.md".to_string(), 42);
|
|
||||||
assert_eq!(
|
|
||||||
result.to_string(),
|
|
||||||
"Multiple @Task markers found in /path/file.md:42 - cannot auto-insert @Done"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_no_task_marker_error_message() {
|
|
||||||
let result = StreamdError::NoTaskMarker("/path/file.md".to_string(), 42);
|
|
||||||
assert_eq!(
|
|
||||||
result.to_string(),
|
|
||||||
"No @Task marker found in /path/file.md:42"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
pub mod args;
|
pub mod args;
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
|
|
||||||
pub use args::{Cli, Commands, TodoAction};
|
pub use args::{Cli, Commands};
|
||||||
|
|
|
||||||
15
src/error.rs
15
src/error.rs
|
|
@ -16,21 +16,6 @@ pub enum StreamdError {
|
||||||
|
|
||||||
#[error("TOML error: {0}")]
|
#[error("TOML error: {0}")]
|
||||||
TomlError(#[from] toml::de::Error),
|
TomlError(#[from] toml::de::Error),
|
||||||
|
|
||||||
#[error("Invalid task number {0}: only {1} tasks available")]
|
|
||||||
InvalidTaskNumber(usize, usize),
|
|
||||||
|
|
||||||
#[error("Task shard missing file path")]
|
|
||||||
MissingFilePath,
|
|
||||||
|
|
||||||
#[error("Invalid line number in task")]
|
|
||||||
InvalidLineNumber,
|
|
||||||
|
|
||||||
#[error("Multiple @Task markers found in {0}:{1} - cannot auto-insert @Done")]
|
|
||||||
MultipleTaskMarkers(String, usize),
|
|
||||||
|
|
||||||
#[error("No @Task marker found in {0}:{1}")]
|
|
||||||
NoTaskMarker(String, usize),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<StreamdError> for miette::Report {
|
impl From<StreamdError> for miette::Report {
|
||||||
|
|
|
||||||
11
src/main.rs
11
src/main.rs
|
|
@ -1,19 +1,12 @@
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use streamd::cli::{Cli, Commands, TodoAction};
|
use streamd::cli::{Cli, Commands};
|
||||||
|
|
||||||
fn main() -> miette::Result<()> {
|
fn main() -> miette::Result<()> {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
|
|
||||||
match cli.command {
|
match cli.command {
|
||||||
Some(Commands::New) => streamd::cli::commands::new::run()?,
|
Some(Commands::New) => streamd::cli::commands::new::run()?,
|
||||||
Some(Commands::Todo {
|
Some(Commands::Todo) => streamd::cli::commands::todo::run()?,
|
||||||
show_future,
|
|
||||||
action,
|
|
||||||
}) => match action {
|
|
||||||
None => streamd::cli::commands::todo::run_list(show_future)?,
|
|
||||||
Some(TodoAction::Edit { number }) => streamd::cli::commands::todo::run_edit(number)?,
|
|
||||||
Some(TodoAction::Done { number }) => streamd::cli::commands::todo::run_done(number)?,
|
|
||||||
},
|
|
||||||
Some(Commands::Edit { number }) => streamd::cli::commands::edit::run(number)?,
|
Some(Commands::Edit { number }) => streamd::cli::commands::edit::run(number)?,
|
||||||
Some(Commands::Timesheet) => streamd::cli::commands::timesheet::run()?,
|
Some(Commands::Timesheet) => streamd::cli::commands::timesheet::run()?,
|
||||||
Some(Commands::Completions { shell }) => {
|
Some(Commands::Completions { shell }) => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue