use std::{io::IsTerminal, path::Path};
pub mod commands;
pub use heddle_cli_args as cli_args;
pub use heddle_cli_args::*;
pub use heddle_cli_contract::cli::help;
pub use heddle_cli_render::cli::{progress_render, render, style, tips, warning_render};
use repo::{Config, Repository};
use crate::config::UserConfig;
pub fn is_tty() -> bool {
std::io::stdout().is_terminal()
}
pub fn is_interactive_tty() -> bool {
std::io::stdin().is_terminal()
&& std::io::stdout().is_terminal()
&& std::io::stderr().is_terminal()
}
pub fn execution_context_from_cli(cli: &Cli) -> anyhow::Result<verbs::ExecutionContext> {
let cwd = std::env::current_dir()?;
let start = cli.repo.as_ref().unwrap_or(&cwd).to_path_buf();
let repo = cli.open_repo()?;
let config = UserConfig::load_default()?;
Ok(execution_context_from_cli_parts(
&start,
Some(repo),
&config,
))
}
pub(crate) fn execution_context_from_cli_parts(
start: &Path,
repo: Option<Repository>,
config: &UserConfig,
) -> verbs::ExecutionContext {
let fsmonitor_mode = config
.worktree_status_options(repo.as_ref().map(Repository::config))
.fsmonitor
.mode;
let mut builder = verbs::ExecutionContext::builder()
.start_path(start.to_path_buf())
.principal_fallback(
config
.principal_pair()
.map(|(name, email)| (name.to_string(), email.to_string())),
)
.fsmonitor_mode(fsmonitor_mode);
if let Some(repo) = repo {
builder = builder.repo(repo);
}
builder.build()
}
pub fn user_config_or_exit() -> &'static UserConfig {
Cli::user_config_or_exit()
}
pub fn load_user_config_or_exit() -> UserConfig {
user_config_or_exit().clone()
}
pub fn output_is_compact(cli: &Cli) -> bool {
matches!(cli.output_mode(), Some(OutputMode::JsonCompact))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum JsonOutputMode {
Text,
Json,
Jsonl,
}
pub fn json_output_mode_for_kind(
cli: &Cli,
config: Option<&Config>,
json_kind: &str,
) -> JsonOutputMode {
match json_kind {
"jsonl" => {
if matches!(
cli.output_mode(),
Some(OutputMode::Json | OutputMode::JsonCompact)
) {
JsonOutputMode::Jsonl
} else {
JsonOutputMode::Text
}
}
"json" | "json_or_jsonl" => {
if should_output_json(cli, config) {
JsonOutputMode::Json
} else {
JsonOutputMode::Text
}
}
"none" => JsonOutputMode::Text,
_ => JsonOutputMode::Text,
}
}
pub fn worktree_status_options(config: Option<&Config>) -> repo::WorktreeStatusOptions {
user_config_or_exit().worktree_status_options(config)
}
#[cfg(test)]
mod tests {
use clap::Parser;
use super::*;
#[test]
fn jsonl_commands_require_explicit_json_output() {
let auto = Cli::try_parse_from(["heddle", "watch"]).expect("watch should parse");
assert_eq!(
json_output_mode_for_kind(&auto, None, "jsonl"),
JsonOutputMode::Text
);
let json = Cli::try_parse_from(["heddle", "--output", "json", "watch"])
.expect("watch --output json should parse");
assert_eq!(
json_output_mode_for_kind(&json, None, "jsonl"),
JsonOutputMode::Jsonl
);
let text = Cli::try_parse_from(["heddle", "--output", "text", "watch"])
.expect("watch --output text should parse");
assert_eq!(
json_output_mode_for_kind(&text, None, "jsonl"),
JsonOutputMode::Text
);
}
#[test]
fn canonical_context_adapter_maps_cli_state_for_an_injected_repository() {
let temp = tempfile::tempdir().expect("temp repository");
Repository::init_default(temp.path()).expect("init repository");
let repo = Repository::open(temp.path()).expect("open repository");
let config = UserConfig::default();
let ctx = execution_context_from_cli_parts(temp.path(), Some(repo), &config);
assert_eq!(ctx.start_path(), Some(temp.path()));
assert_eq!(ctx.require_repo().expect("repo").root(), temp.path());
assert_eq!(
ctx.fsmonitor_mode(),
config
.worktree_status_options(Some(ctx.require_repo().expect("repo").config()))
.fsmonitor
.mode
);
}
}