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
// Release builds on Windows use the "windows" subsystem so launching
// todoke from explorer / shortcut / file association doesn't flash a
// transient console window. Debug builds stay on the default "console"
// subsystem to keep dev ergonomics unchanged.
#![cfg_attr(all(windows, not(debug_assertions)), windows_subsystem = "windows")]
use anyhow::Result;
use clap::{CommandFactory, Parser};
mod backends;
mod cli;
mod config;
#[cfg(windows)]
mod console_attach;
mod dispatcher;
mod input;
mod matcher;
mod platform;
mod registry;
mod style;
mod template;
use cli::{Cli, Command};
#[tokio::main]
async fn main() -> Result<()> {
// MUST be the very first thing in main().
//
// Rust's io::stdout / io::stderr / io::stdin cache their OS handle
// on first use. Any stdio access before this runs would freeze the
// null handle in place and the SetStdHandle calls below would
// become no-ops for Rust's stdio. Keep this at the top; don't
// introduce earlier stdio writers (panic hooks, static init, etc.)
// without rechecking this invariant.
//
// On Windows, attach to the parent terminal (if any) so logs and
// stdout reach whoever launched us from PowerShell / cmd. When
// launched from explorer there is no parent console — the call
// is a no-op and stdio sinks silently.
#[cfg(windows)]
console_attach::attach_parent_console();
let mut cli = Cli::parse();
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| cli.log_level().into()),
)
.with_writer(std::io::stderr)
.init();
match cli.command.take() {
None => dispatcher::dispatch(&cli, &cli.files).await,
Some(Command::List { alive_only }) => dispatcher::list(&cli, alive_only).await,
Some(Command::Kill { group, all, force }) => {
dispatcher::kill(&cli, group.as_deref(), all, force).await
}
Some(Command::Check { inputs }) => dispatcher::check(&cli, &inputs).await,
Some(Command::Doctor) => dispatcher::doctor(&cli).await,
Some(Command::Config(sub)) => cli::config::run(sub, cli.config.as_deref()).await,
Some(Command::Completion { shell }) => {
clap_complete::generate(shell, &mut Cli::command(), "todoke", &mut std::io::stdout());
Ok(())
}
}
}