use crate::error::SmoothError;
use crate::pandoc::Pandoc;
use crate::util;
use crate::OutputFormat;
use std::collections::HashMap;
use std::env;
use std::fs;
use std::io::prelude::*;
use std::path::PathBuf;
use serde::Deserialize;
use serde_json;
use serde_json::value::Value;
const JSON_TEMPLATE_PATH: &str = "rsmooth-metadata.pandoc-tpl";
const JSON_TEMPLATE: &str = "$meta-json$";
#[derive(Debug, Deserialize)]
struct Header {
template: Option<String>,
reference: Option<String>,
#[serde(default = "default_engine")]
engine: String,
pandoc_optons: Option<String>,
#[serde(default = "default_do_tera")]
do_tera: bool,
pub tera_context: Option<HashMap<String, Value>>,
#[serde(default = "default_break_description")]
break_description: bool,
bibliography: Option<String>,
pub csl: Option<String>,
}
fn default_engine() -> String {
String::from("xelatex")
}
fn default_do_tera() -> bool {
false
}
fn default_break_description() -> bool {
false
}
impl<'a> Header {
fn from(file: &PathBuf) -> Result<Self, SmoothError<'a>> {
let json_tpl = Header::create_template()?;
let raw = match Pandoc::new().convert_with_template_to_str(file, json_tpl.clone()) {
Ok(x) => x,
Err(x) => {
Header::remove_template(json_tpl.clone())?;
return Err(SmoothError::Pandoc(x));
}
};
let data: Self = match serde_json::from_str(&raw) {
Ok(x) => x,
Err(e) => {
Header::remove_template(json_tpl.clone())?;
return Err(SmoothError::MetadataParseFailure(e));
}
};
Header::remove_template(json_tpl.clone())?;
debug!("parsed {:?}", data);
Ok(data)
}
fn create_template() -> Result<PathBuf, SmoothError<'a>> {
let path = env::temp_dir().join(PathBuf::from(JSON_TEMPLATE_PATH));
if path.exists() {
return Err(SmoothError::JsonTemplateExists(path));
}
let mut file = match fs::File::create(&path) {
Ok(x) => x,
Err(e) => return Err(SmoothError::CreateJsonTemplateFailed(path, e)),
};
match file.write_all(JSON_TEMPLATE.as_bytes()) {
Ok(_) => Ok(path),
Err(e) => Err(SmoothError::CreateJsonTemplateFailed(path, e)),
}
}
fn remove_template(path: PathBuf) -> Result<(), SmoothError<'a>> {
match fs::remove_file(&path) {
Ok(_) => Ok(()),
Err(e) => Err(SmoothError::RemoveJsonTemplateFailed(path, e)),
}
}
}
enum PathType {
Template,
Reference,
Bibliography,
CitationStyle,
}
#[derive(Debug, Clone)]
pub struct Metadata {
pub template: Option<PathBuf>,
pub reference: Option<PathBuf>,
pub engine: String,
pub pandoc_options: Option<Vec<String>>,
pub do_tera: bool,
pub tera_context: Option<HashMap<String, Value>>,
pub break_description: bool,
pub bibliography: Option<PathBuf>,
pub csl: Option<PathBuf>,
}
impl<'a> Metadata {
pub fn from(
path: &PathBuf,
parent: &PathBuf,
output_format: &OutputFormat,
) -> Result<Self, SmoothError<'a>> {
let header = Header::from(path)?;
Ok(Self {
template: match header.template {
Some(x) => Some(Metadata::normalize_path(
x,
parent,
PathType::Template,
output_format,
)?),
None => None,
},
reference: match header.reference {
Some(x) => Some(Metadata::normalize_path(
x,
parent,
PathType::Reference,
output_format,
)?),
None => None,
},
engine: header.engine,
pandoc_options: match header.pandoc_optons {
Some(x) => Some(x.split_whitespace().map(|y| String::from(y)).collect()),
None => None,
},
do_tera: header.do_tera,
tera_context: header.tera_context,
break_description: header.break_description,
bibliography: match header.bibliography {
Some(x) => Some(Metadata::normalize_path(
x,
parent,
PathType::Bibliography,
output_format,
)?),
None => None,
},
csl: match header.csl {
Some(x) => Some(Metadata::normalize_path(
x,
parent,
PathType::CitationStyle,
output_format,
)?),
None => None,
},
})
}
fn normalize_path(
path: String,
parent: &PathBuf,
typ: PathType,
output_format: &OutputFormat,
) -> Result<PathBuf, SmoothError<'a>> {
let rsl = util::normalize_path(&path, Some(parent))?;
if let PathType::Reference = typ {
let extension = rsl.extension().unwrap().to_str().unwrap();
match output_format {
OutputFormat::Odt => match extension {
"odt" | "fodt" => {}
_ => return Err(SmoothError::IncompatibleReferenceFile(rsl, "odt")),
},
OutputFormat::Docx => match extension {
"docx" | "docm" => {}
_ => return Err(SmoothError::IncompatibleReferenceFile(rsl, "docx")),
},
_ => {},
};
}
match rsl.exists() {
true => Ok(rsl),
false => match typ {
PathType::Template => Err(SmoothError::TemplateNotFound(rsl)),
PathType::Reference => Err(SmoothError::ReferenceNotFound(rsl)),
PathType::Bibliography => Err(SmoothError::BibliographyNotFound(rsl)),
PathType::CitationStyle => Err(SmoothError::CitationStyleNotFound(rsl)),
},
}
}
}