use self::super::super::{MachineDataKind, WrappedElement, LanguageTag, TagName};
use self::super::super::super::util::{BLOGUEN_VERSION, normalise_datetime};
use chrono::format::{Fixed as FixedTimeFormatItem, Item as TimeFormatItem};
use chrono::{FixedOffset, DateTime, TimeZone, Local, Utc};
use std::collections::{BTreeMap, BTreeSet};
use std::io::{Error as IoError, Write};
use self::super::super::super::Error;
use std::iter::FromIterator;
use self::super::err_io;
use std::borrow::Cow;
pub fn machine_output_kind<W, E, Tz, St, Sc>(kind: &MachineDataKind)
-> (fn(blog_name: &str,
language: &LanguageTag,
additional_data_sets: &[&BTreeMap<String, String>],
raw_post_name: &str,
number: usize,
title: &str,
author: &str,
post_date: &DateTime<Tz>,
tags: &[&[TagName]],
styles: &[&[St]],
scripts: &[&[Sc]],
into: &mut W,
out_name_err: E)
-> Result<Cow<'static, str>, Error>)
where W: Write,
E: Into<Cow<'static, str>>,
Tz: TimeZone,
St: WrappedElement,
Sc: WrappedElement
{
match kind {
MachineDataKind::Json => machine_output_json,
}
}
pub fn machine_output_json<W, E, Tz, St, Sc>(blog_name: &str, language: &LanguageTag, additional_data_sets: &[&BTreeMap<String, String>],
raw_post_name: &str, number: usize, title: &str, author: &str, post_date: &DateTime<Tz>, tags: &[&[TagName]],
styles: &[&[St]], scripts: &[&[Sc]], into: &mut W, out_name_err: E)
-> Result<Cow<'static, str>, Error>
where W: Write,
E: Into<Cow<'static, str>>,
Tz: TimeZone,
St: WrappedElement,
Sc: WrappedElement
{
machine_output_json_impl(blog_name,
language,
additional_data_sets,
raw_post_name,
number,
title,
author,
normalise_datetime(post_date),
tags,
styles,
scripts,
into,
out_name_err.into())
}
fn machine_output_json_impl<W, St, Sc>(blog_name: &str, language: &LanguageTag, additional_data_sets: &[&BTreeMap<String, String>], raw_post_name: &str,
number: usize, title: &str, author: &str, post_date: DateTime<FixedOffset>, tags: &[&[TagName]], styles: &[&[St]],
scripts: &[&[Sc]], into: &mut W, out_name_err: Cow<'static, str>)
-> Result<Cow<'static, str>, Error>
where W: Write,
St: WrappedElement,
Sc: WrappedElement
{
let mut out_name_err = Some(out_name_err);
(|| {
into.write_all(b"{\n ").map_err(|e| (e, "header".into()))?;
into.write_all(b"\"number\": ").map_err(|e| (e, "post number pre".into()))?;
into.write_fmt(format_args!("{}", number)).map_err(|e| (e, "post number".into()))?;
into.write_all(b",\n ").map_err(|e| (e, "post number post".into()))?;
write_string_variable("language", &language, into)?;
into.write_all(b",\n ").map_err(|e| (e, "newline".into()))?;
write_string_variable("title", title, into)?;
into.write_all(b",\n ").map_err(|e| (e, "newline".into()))?;
write_string_variable("author", author, into)?;
into.write_all(b",\n\n ").map_err(|e| (e, "newline".into()))?;
write_string_variable("raw_post_name", raw_post_name, into)?;
into.write_all(b",\n ").map_err(|e| (e, "newline".into()))?;
write_string_variable("blog_name", blog_name, into)?;
into.write_all(b",\n\n ").map_err(|e| (e, "newline".into()))?;
write_date("post_date_rfc3339", &post_date, FixedTimeFormatItem::RFC3339, into)?;
into.write_all(b",\n ").map_err(|e| (e, "newline".into()))?;
write_date("post_date_rfc2822", &post_date, FixedTimeFormatItem::RFC2822, into)?;
into.write_all(b",\n ").map_err(|e| (e, "newline".into()))?;
let now_utc = normalise_datetime(&Utc::now());
write_date("generation_date_utc_rfc3339", &now_utc, FixedTimeFormatItem::RFC3339, into)?;
into.write_all(b",\n ").map_err(|e| (e, "newline".into()))?;
write_date("generation_date_utc_rfc2822", &now_utc, FixedTimeFormatItem::RFC2822, into)?;
into.write_all(b",\n ").map_err(|e| (e, "newline".into()))?;
let now_local = normalise_datetime(&Local::now());
write_date("generation_date_local_rfc3339", &now_local, FixedTimeFormatItem::RFC3339, into)?;
into.write_all(b",\n ").map_err(|e| (e, "newline".into()))?;
write_date("generation_date_local_rfc2822", &now_local, FixedTimeFormatItem::RFC2822, into)?;
into.write_all(b",\n\n ").map_err(|e| (e, "newline".into()))?;
write_array("tags", tags, |t| &*t, into)?;
into.write_all(b",\n ").map_err(|e| (e, "newline".into()))?;
write_data("additional_data", additional_data_sets, into)?;
into.write_all(b",\n\n ").map_err(|e| (e, "newline".into()))?;
write_array("styles", styles, |s| s.content(), into)?;
into.write_all(b",\n ").map_err(|e| (e, "newline".into()))?;
write_array("scripts", scripts, |s| s.content(), into)?;
into.write_all(b",\n\n ").map_err(|e| (e, "newline".into()))?;
write_string_variable("bloguen-version", BLOGUEN_VERSION, into)?;
into.write_all(b"\n}").map_err(|e| (e, "footer".into()))?;
Ok(())
})().map_err(|(e, d): (_, Cow<'static, str>)| err_io("write", format!("{} when writing JSON machine output {}", e, d), out_name_err.take().unwrap()))?;
Ok(out_name_err.unwrap())
}
fn write_date<W: Write>(name: &str, value: &DateTime<FixedOffset>, format: FixedTimeFormatItem, into: &mut W) -> Result<(), (IoError, Cow<'static, str>)> {
into.write_all(b"\"").map_err(|e| (e, "string pre".into()))?;
into.write_all(name.as_bytes()).map_err(|e| (e, format!("{} field name", name).into()))?;
into.write_all(b"\": \"").map_err(|e| (e, "string center".into()))?;
into.write_fmt(format_args!("{}", value.format_with_items([TimeFormatItem::Fixed(format)].iter().cloned())))
.map_err(|e| (e, format!("{} date", name).into()))?;
into.write_all(b"\"").map_err(|e| (e, "string post".into()))?;
Ok(())
}
fn write_string_variable<W: Write>(name: &str, value: &str, into: &mut W) -> Result<(), (IoError, Cow<'static, str>)> {
into.write_all(b"\"").map_err(|e| (e, "string pre".into()))?;
into.write_all(name.as_bytes()).map_err(|e| (e, format!("{} field name", name).into()))?;
into.write_all(b"\": ").map_err(|e| (e, "string center".into()))?;
into.write_fmt(format_args!("{:?}", value)).map_err(|e| (e, format!("{} field content", name).into()))?;
Ok(())
}
fn write_array<El, M: Fn(&El) -> &str, W: Write>(name: &str, arrs: &[&[El]], map: M, into: &mut W) -> Result<(), (IoError, Cow<'static, str>)> {
into.write_all(b"\"").map_err(|e| (e, "array pre".into()))?;
into.write_all(name.as_bytes()).map_err(|e| (e, format!("{} field name", name).into()))?;
into.write_all(b"\": [").map_err(|e| (e, "array center".into()))?;
let mut first = true;
Result::from_iter(arrs.iter().flat_map(|arr| arr.iter()).map(|a| {
if !first {
into.write_all(b",").map_err(|e| (e, "string array comma".into()))?;
}
into.write_all(b"\n ").map_err(|e| (e, "indented newline".into()))?;
into.write_fmt(format_args!("{:?}", map(a))).map_err(|e| (e, "string array element".into()))?;
first = false;
Ok(())
}))?;
into.write_all(b"\n ]").map_err(|e| (e, "aray post".into()))?;
Ok(())
}
fn write_data<W: Write>(name: &str, datas: &[&BTreeMap<String, String>], into: &mut W) -> Result<(), (IoError, Cow<'static, str>)> {
into.write_all(b"\"").map_err(|e| (e, "map pre".into()))?;
into.write_all(name.as_bytes()).map_err(|e| (e, format!("{} field name", name).into()))?;
into.write_all(b"\": {").map_err(|e| (e, "map center".into()))?;
let mut first = true;
datas.iter()
.rev()
.flat_map(|dt| dt.iter())
.fold(Ok(BTreeSet::new()), |mut acc, el| {
if acc.is_err() || acc.as_ref().unwrap().contains(el.0) {
return acc;
}
if !first {
into.write_all(b",").map_err(|e| (e, "aux data string comma".into()))?;
}
into.write_all(b"\n ").map_err(|e| (e, "indented newline".into()))?;
into.write_fmt(format_args!("{:?}", el.0)).map_err(|e| (e, "aux data string map element".into()))?;
into.write_all(b": ").map_err(|e| (e, "aux data string map post".into()))?;
into.write_fmt(format_args!("{:?}", el.1)).map_err(|e| (e, "aux data string map element".into()))?;
acc.as_mut().unwrap().insert(el.0);
first = false;
acc
})?;
into.write_all(b"\n }").map_err(|e| (e, "newline".into()))?;
Ok(())
}