use std::path::Path;
use dbmd_core::emit::{self, Emit, EmittedFile};
use dbmd_core::store::{EdgeSpan, Layer};
use dbmd_core::Store;
use crate::cli::EmitArgs;
use crate::context::Context;
use crate::error::{CliError, CliResult, ExitCode};
pub fn run(ctx: &Context, args: &EmitArgs) -> CliResult {
let store = Store::open_strict(Path::new(&args.dir))?;
if args.ndjson {
return ndjson_dump(&store);
}
let dump = emit::compute(&store).map_err(CliError::from)?;
if ctx.json {
println!("{}", json_dump(&args.dir, &dump));
} else {
print!("{}", text_dump(&dump));
}
Ok(())
}
fn ndjson_dump(store: &Store) -> CliResult {
use std::io::Write as _;
let rels = emit::walk_rels(store).map_err(CliError::from)?;
let stdout = std::io::stdout();
let mut out = std::io::BufWriter::new(stdout.lock());
let mut written: std::io::Result<()> = Ok(());
for rel in &rels {
let file = emit::emit_file(store, rel).map_err(CliError::from)?;
let line = serde_json::to_string(&file_json(&file))
.map_err(|e| CliError::new(ExitCode::Runtime, "JSON_ENCODE_FAILED", e.to_string()))?;
written = writeln!(out, "{line}");
if written.is_err() {
break;
}
}
if written.is_ok() {
written = out.flush();
}
match written {
Ok(()) => Ok(()),
Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => Ok(()),
Err(e) => Err(CliError::from(e)),
}
}
fn text_dump(dump: &Emit) -> String {
let mut out = String::new();
for f in &dump.files {
out.push_str(&f.path);
out.push('\n');
}
out
}
fn json_dump(store_dir: &str, dump: &Emit) -> String {
let files: Vec<serde_json::Value> = dump.files.iter().map(file_json).collect();
serde_json::json!({
"store": store_dir,
"files": files,
"summary": {
"files": dump.files.len(),
"sources": dump.sources,
"records": dump.records,
},
})
.to_string()
}
fn file_json(f: &EmittedFile) -> serde_json::Value {
serde_json::json!({
"path": f.path,
"layer": f.layer.map(layer_word),
"frontmatter": f.frontmatter,
"type": f.type_,
"meta_type": f.meta_type,
"title": f.title,
"summary": f.summary,
"body": f.body,
"links": f.links,
"link_spans": f.link_spans.iter().map(link_span_json).collect::<Vec<_>>(),
"created": f.created.map(|t| t.to_rfc3339()),
"updated": f.updated.map(|t| t.to_rfc3339()),
"sha256": f.sha256,
})
}
fn link_span_json(s: &EdgeSpan) -> serde_json::Value {
serde_json::json!({
"target": s.target,
"raw": s.raw,
"alias": s.alias,
"start": s.start,
"end": s.end,
})
}
fn layer_word(layer: Layer) -> &'static str {
match layer {
Layer::Sources => "source",
Layer::Records => "record",
}
}