use anyhow::{Context, Result, ensure};
use std::{
fs,
io::IsTerminal,
process::{Command, Stdio},
};
pub fn uses_builtin(external: bool) -> bool {
!external && std::io::stdin().is_terminal() && std::io::stderr().is_terminal()
}
pub fn compose(
description: &str,
external: bool,
navigation: Option<&crate::db::Db>,
) -> Result<crate::tui::Outcome> {
if uses_builtin(external) {
return crate::tui::compose(description, navigation);
}
compose_external_outcome(description)
}
pub fn compose_existing(
description: &str,
external: bool,
task_id: i64,
db: &crate::db::Db,
) -> Result<crate::tui::Outcome> {
if uses_builtin(external) {
return crate::tui::compose_existing(description, task_id, &db.image_references(task_id)?);
}
compose_external_outcome(description)
}
fn compose_external_outcome(description: &str) -> Result<crate::tui::Outcome> {
let description = compose_external(description)?;
Ok(crate::tui::Outcome {
composition: crate::tui::draft::Composition {
description,
images: Vec::new(),
image_spans: Vec::new(),
},
target_id: None,
})
}
fn compose_external(description: &str) -> Result<String> {
let editor = std::env::var("EDITOR")
.context("Set EDITOR to compose or edit a task, or provide fields inline")?;
ensure!(
!editor.trim().is_empty(),
"Set EDITOR to compose or edit a task, or provide fields inline"
);
let draft_dir = tempfile::Builder::new()
.prefix("qqq-edit-")
.tempdir()
.context("Failed to create task draft directory")?;
let draft = draft_dir.path().join("task.txt");
fs::write(&draft, description).context("Failed to write task draft")?;
let status = Command::new("sh")
.arg("-c")
.arg(format!("exec {editor} \"$1\""))
.arg("qqq-editor")
.arg(&draft)
.stdin(Stdio::inherit())
.stdout(Stdio::from(std::io::stderr()))
.stderr(Stdio::inherit())
.status()
.context("Failed to start EDITOR")?;
ensure!(
status.success(),
"Editor exited unsuccessfully ({status}); task not saved"
);
let content = fs::read_to_string(&draft).context("Failed to read edited task")?;
ensure!(
!content.trim().is_empty(),
"Task description cannot be empty; task not saved"
);
Ok(content)
}