use std::collections::BTreeMap;
use std::path::Path;
use anyhow::{Result, anyhow, bail};
use chrono::Local;
use clap::Args;
use org_core::{WriteContext, WriteEngine, load_settings, parse_capture_line};
#[derive(Args, Debug)]
pub struct CaptureCommand {
template: Option<String>,
text: Option<String>,
#[arg(short = 'i', long, conflicts_with_all = ["template", "text"])]
interactive: bool,
#[arg(long, default_value = "")]
body: String,
#[arg(long = "prompt", value_name = "KEY=VALUE")]
prompts: Vec<String>,
#[arg(long)]
dry_run: bool,
#[arg(long)]
json: bool,
}
impl CaptureCommand {
pub fn execute(self, config: Option<String>, root: Option<String>) -> Result<()> {
if self.interactive {
return crate::tui::run_capture(config, root);
}
let (Some(template), Some(text)) = (self.template, self.text) else {
bail!("usage: agd capture <template> \"<text>\" or agd capture -i");
};
let settings = load_settings(
config.as_deref().map(Path::new),
root.as_deref().map(Path::new),
)?
.settings;
let draft = parse_capture_line(&text, Local::now().date_naive())?;
let prompts = parse_prompts(self.prompts)?;
let mutation = WriteEngine::with_default_journal(settings)?.capture(
&template,
&draft,
&self.body,
&prompts,
self.dry_run,
&WriteContext {
actor: "cli".to_string(),
project: std::env::current_dir()
.map(|path| path.to_string_lossy().into_owned())
.unwrap_or_else(|_| "unknown".to_string()),
now: None,
},
)?;
if self.json {
println!("{}", serde_json::to_string_pretty(&mutation)?);
} else if self.dry_run {
print!("{}", mutation.diff);
} else {
println!("{}", mutation.item.item_ref);
}
Ok(())
}
}
fn parse_prompts(values: Vec<String>) -> Result<BTreeMap<String, String>> {
values
.into_iter()
.map(|value| {
let (key, value) = value
.split_once('=')
.ok_or_else(|| anyhow!("--prompt must be KEY=VALUE: {value}"))?;
if key.is_empty() {
bail!("--prompt key cannot be empty");
}
Ok((key.to_string(), value.to_string()))
})
.collect()
}