use std::path::Path;
use dbmd_core::linkmd::{self, LinkError, MAX_PROPOSE_BYTES};
use serde_json::Value;
use crate::cli::ProposeArgs;
use crate::context::Context;
use crate::error::{CliError, CliResult, ExitCode};
use crate::sanitize::sanitize;
pub fn run(ctx: &Context, args: &ProposeArgs) -> CliResult {
let body = match (&args.body, &args.body_file) {
(Some(text), None) => text.clone(),
(None, Some(path)) => {
if let Ok(meta) = std::fs::metadata(path) {
if meta.len() > MAX_PROPOSE_BYTES {
return Err(LinkError::ProposeTooLarge { bytes: meta.len() }.into());
}
}
std::fs::read_to_string(path).map_err(|e| {
CliError::new(
ExitCode::Runtime,
"IO_ERROR",
format!("reading --body-file {path}: {e}"),
)
})?
}
_ => {
return Err(CliError::new(
ExitCode::Runtime,
"BAD_BODY",
"exactly one body source is required",
)
.with_hint("pass --body <text> or --body-file <path>"));
}
};
let site = args.site.trim().trim_start_matches('@');
let cfg = linkmd::hub_config(args.hub.as_deref(), Path::new(&args.dir))?;
let receipt = linkmd::propose(&cfg, site, &args.app, &body)?;
if ctx.json {
println!(
"{}",
serde_json::to_string_pretty(&receipt).unwrap_or_default()
);
return Ok(());
}
println!(
"proposed to @{site}/{} — landed as {}",
args.app,
sanitize(
receipt
.get("path")
.and_then(Value::as_str)
.unwrap_or("(inbox)")
),
);
Ok(())
}