about summary refs log tree commit diff stats
path: root/pkgs/by-name/ts/tskm/src/cli.rs
blob: 1c72b3c231db4007fd91e8ef7a120ceb9ed22ee1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use std::path::PathBuf;

use anyhow::{bail, Result};
use clap::{ArgAction, Parser, Subcommand};
use url::Url;

use crate::{
    interface::{input::Input, project::ProjectName},
    state::State,
    task,
};

#[derive(Parser, Debug)]
#[command(author, version, about, long_about, verbatim_doc_comment)]
/// This is the core interface to the system-integrated task management
///
/// `tskm` effectively combines multiple applications together:
/// - `taskwarrior` projects are raised connected to `firefox` profiles, making it possible to “open”
///   a project.
/// - Every `taskwarrior` project has a determined `neorg` path, so that extra information for a
///   `project` can be stored in this `norg` file.
/// - `tskm` can track inputs for you. These are URLs with optional tags which you can that
///   “review” to open tasks based on them.
pub struct CliArgs {
    #[command(subcommand)]
    pub command: Command,

    /// Increase message verbosity
    #[arg(long="verbose", short = 'v', action = ArgAction::Count, default_value_t = 2)]
    pub verbosity: u8,

    /// Silence all output
    #[arg(long, short = 'q')]
    pub quiet: bool,
}

#[derive(Subcommand, Debug)]
pub enum Command {
    /// Interact with projects.
    Projects {
        #[command(subcommand)]
        command: ProjectCommand,
    },

    /// Manage the input queue.
    Inputs {
        #[command(subcommand)]
        command: InputCommand,
    },

    /// Access the associated `neorg` workspace for the project/task.
    Neorg {
        #[command(subcommand)]
        command: NeorgCommand,
    },

    /// Interface with the Firefox profile of each project.
    Open {
        #[command(subcommand)]
        command: OpenCommand,
    },
}

#[derive(Subcommand, Debug)]
pub enum ProjectCommand {
    /// Lists all available projects.
    List,

    /// Allows you to quickly add projects.
    Add {
        /// The name of the new project.
        #[arg(value_parser = ProjectName::try_from_project)]
        new_project_name: ProjectName,
    },
}

#[derive(Subcommand, Debug, Clone, Copy)]
pub enum NeorgCommand {
    /// Open the `neorg` project associated with id of the task.
    Task {
        /// The working set id of the task
        #[arg(value_parser = task_from_working_set_id)]
        id: task::Task,
    },
}

fn task_from_working_set_id(id: &str) -> Result<task::Task> {
    let id: usize = id.parse()?;
    let mut state = State::new_ro()?;

    let Some(task) = task::Task::from_working_set(id, &mut state)? else {
        bail!("Working set id '{id}' is not valid!")
    };
    Ok(task)
}

#[derive(Subcommand, Debug)]
pub enum OpenCommand {
    /// Open each project's Firefox profile consecutively, that was opened since the last review.
    ///
    /// This allows you to remove stale opened tabs and to commit open tabs to the `inputs`.
    Review,

    /// Opens Firefox with either the supplied project or the currently active project profile.
    Project {
        /// The project to open.
        #[arg(value_parser = task::Project::from_project_string)]
        project: task::Project,

        /// The URL to open.
        url: Option<Url>,
    },

    /// Open a selected project in it's Firefox profile.
    ///
    /// This will use rofi's dmenu mode to select one project from the list of all registered
    /// projects.
    Select {
        /// The URL to open.
        url: Option<Url>,
    },

    /// List all open tabs in the project.
    ListTabs {
        /// The project to open.
        #[arg(value_parser = task::Project::from_project_string)]
        project: Option<task::Project>,
    },
}

#[derive(Subcommand, Debug)]
pub enum InputCommand {
    /// Add URLs as inputs to be categorized.
    Add { inputs: Vec<Input> },
    /// Remove URLs
    Remove { inputs: Vec<Input> },

    /// Add all URLs in the file as inputs to be categorized.
    ///
    /// This expects each line to contain one URL.
    File { file: PathBuf },

    /// Like 'review', but for the inputs that have previously been added.
    /// It takes a project in which to open the URLs.
    Review {
        /// Opens all the URLs in this project.
        #[arg(value_parser = task::Project::from_project_string)]
        project: task::Project,
    },

    /// List all the previously added inputs.
    List,
}