use anyhow::{bail, Result};
use chrono::NaiveDate;
use serde::Serialize;
use crate::config::Config;
use crate::elements::Element;
use crate::store::Store;
#[derive(Serialize)]
struct ExportRecord {
date: String,
#[serde(rename = "ref")]
ref_key: String,
#[serde(rename = "type")]
kind: String,
tags: String,
body: String,
#[serde(skip_serializing_if = "Option::is_none")]
status: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
at: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
start: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
end: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
name: Option<String>,
}
pub fn run(
config: &Config,
dates: Vec<NaiveDate>,
format: &str,
type_filter: Option<String>,
) -> Result<()> {
if !["json", "csv"].contains(&format) {
bail!("Unknown format '{}'. Use: json, csv", format);
}
let store = Store::new(&config.storage_dir);
let mut records: Vec<ExportRecord> = Vec::new();
for d in &dates {
let date_str = d.format("%Y-%m-%d").to_string();
let elements = store.parse_date(*d)?;
for (ref_key, el) in &elements {
if el.is_mps_group() || el.is_unknown() { continue; }
if let Some(ref tf) = type_filter {
if el.sign() != tf { continue; }
}
let rec = build_record(&date_str, ref_key, el);
records.push(rec);
}
}
if format == "json" {
println!("{}", serde_json::to_string_pretty(&records)?);
} else {
let mut wtr = csv::Writer::from_writer(std::io::stdout());
wtr.write_record(["date", "ref", "type", "tags", "body", "status", "at", "start", "end", "name"])?;
for r in &records {
wtr.write_record([
&r.date,
&r.ref_key,
&r.kind,
&r.tags,
&r.body,
r.status.as_deref().unwrap_or(""),
r.at.as_deref().unwrap_or(""),
r.start.as_deref().unwrap_or(""),
r.end.as_deref().unwrap_or(""),
r.name.as_deref().unwrap_or(""),
])?;
}
wtr.flush()?;
}
Ok(())
}
fn build_record(date_str: &str, ref_key: &str, el: &Element) -> ExportRecord {
let tags = el.tags().join(",");
let body = el.body_str().trim().to_string();
match el {
Element::Task { data, .. } => ExportRecord {
date: date_str.into(),
ref_key: ref_key.into(),
kind: "task".into(),
tags,
body,
status: Some(data.status_str().into()),
at: None, start: None, end: None, name: None,
},
Element::Note { .. } => ExportRecord {
date: date_str.into(), ref_key: ref_key.into(), kind: "note".into(),
tags, body, status: None, at: None, start: None, end: None, name: None,
},
Element::Log { data, .. } => ExportRecord {
date: date_str.into(), ref_key: ref_key.into(), kind: "log".into(),
tags, body, status: None, at: None,
start: data.start.clone(),
end: data.end.clone(),
name: None,
},
Element::Reminder { data, .. } => ExportRecord {
date: date_str.into(), ref_key: ref_key.into(), kind: "reminder".into(),
tags, body, status: None, at: data.at.clone(), start: None, end: None, name: None,
},
Element::Character { data, .. } => ExportRecord {
date: date_str.into(), ref_key: ref_key.into(), kind: "character".into(),
tags, body, status: None, at: None, start: None, end: None,
name: data.name.clone(),
},
_ => ExportRecord {
date: date_str.into(), ref_key: ref_key.into(),
kind: el.sign().into(),
tags, body, status: None, at: None, start: None, end: None, name: None,
},
}
}