use crate::parser::parse_scenario_snippet;
use crate::Bindings;
use crate::PartialStep;
use crate::ScenarioStep;
use crate::StepKind;
use crate::SubplotError;
use crate::{DotMarkup, GraphMarkup, PikchrMarkup, PlantumlMarkup};
use crate::{Warning, Warnings};
use pandoc_ast::Attr;
use pandoc_ast::Block;
use pandoc_ast::Inline;
use pandoc_ast::Target;
pub fn error(err: SubplotError) -> Block {
let msg = format!("ERROR: {}", err);
Block::Para(error_msg(&msg))
}
pub fn error_msg(msg: &str) -> Vec<Inline> {
vec![Inline::Strong(vec![inlinestr(msg)])]
}
pub fn inlinestr(s: &str) -> Inline {
Inline::Str(String::from(s))
}
pub fn file_block(attr: &Attr, text: &str) -> Block {
let filename = inlinestr(&attr.0);
let filename = Inline::Strong(vec![filename]);
let intro = Block::Para(vec![inlinestr("File:"), space(), filename]);
let mut cbattrs = attr.clone();
if cbattrs.1.iter().any(|s| s == "noNumberLines") {
cbattrs.1.retain(|s| s != "noNumberLines");
} else if cbattrs.1.iter().all(|s| s != "numberLines") {
cbattrs.1.push("numberLines".to_string());
}
let codeblock = Block::CodeBlock(cbattrs, text.to_string());
let noattr = ("".to_string(), vec![], vec![]);
Block::Div(noattr, vec![intro, codeblock])
}
pub fn scenario_snippet(bindings: &Bindings, snippet: &str, warnings: &mut Warnings) -> Block {
let lines = parse_scenario_snippet(snippet);
let mut steps = vec![];
let mut prevkind: Option<StepKind> = None;
for line in lines {
let (this, thiskind) = step(bindings, line, prevkind, warnings);
steps.push(this);
prevkind = thiskind;
}
Block::LineBlock(steps)
}
fn step(
bindings: &Bindings,
text: &str,
prevkind: Option<StepKind>,
warnings: &mut Warnings,
) -> (Vec<Inline>, Option<StepKind>) {
let step = ScenarioStep::new_from_str(text, prevkind);
if step.is_err() {
return (
error_msg(&format!("Could not parse step: {}", text)),
prevkind,
);
}
let step = step.unwrap();
let m = match bindings.find("", &step) {
Ok(m) => m,
Err(e) => {
let w = Warning::UnknownBinding(format!("{}", e));
warnings.push(w.clone());
return (error_msg(&format!("{}", w)), prevkind);
}
};
let mut inlines = vec![keyword(&step, prevkind), space()];
for part in m.parts() {
match part {
PartialStep::UncapturedText(s) => inlines.push(uncaptured(s.text())),
PartialStep::CapturedText { text, .. } => inlines.push(captured(text)),
}
}
(inlines, Some(step.kind()))
}
fn keyword(step: &ScenarioStep, prevkind: Option<StepKind>) -> Inline {
let actual = inlinestr(&format!("{}", step.kind()));
let and = inlinestr("and");
let keyword = if let Some(prevkind) = prevkind {
if prevkind == step.kind() {
and
} else {
actual
}
} else {
actual
};
Inline::Emph(vec![keyword])
}
fn space() -> Inline {
Inline::Space
}
fn uncaptured(s: &str) -> Inline {
inlinestr(s)
}
fn captured(s: &str) -> Inline {
Inline::Strong(vec![inlinestr(s)])
}
pub fn link_as_note(attr: Attr, text: Vec<Inline>, target: Target) -> Inline {
let (url, _) = target.clone();
let url = Inline::Code(attr.clone(), url);
let link = Inline::Link(attr.clone(), vec![url], target);
let note = Inline::Note(vec![Block::Para(vec![link])]);
let mut text = text;
text.push(note);
Inline::Span(attr, text)
}
pub fn pikchr_to_block(pikchr: &str, class: Option<&str>, warnings: &mut Warnings) -> Block {
match PikchrMarkup::new(pikchr, class).as_svg() {
Ok(svg) => typeset_svg(svg),
Err(err) => {
warnings.push(Warning::Pikchr(format!("{}", err)));
error(err)
}
}
}
pub fn dot_to_block(dot: &str, warnings: &mut Warnings) -> Block {
match DotMarkup::new(dot).as_svg() {
Ok(svg) => typeset_svg(svg),
Err(err) => {
warnings.push(Warning::Dot(format!("{}", err)));
error(err)
}
}
}
pub fn plantuml_to_block(markup: &str, warnings: &mut Warnings) -> Block {
match PlantumlMarkup::new(markup).as_svg() {
Ok(svg) => typeset_svg(svg),
Err(err) => {
warnings.push(Warning::Plantuml(format!("{}", err)));
error(err)
}
}
}
pub fn roadmap_to_block(yaml: &str, warnings: &mut Warnings) -> Block {
match roadmap::from_yaml(yaml) {
Ok(ref mut roadmap) => {
roadmap.set_missing_statuses();
let width = 50;
match roadmap.format_as_dot(width) {
Ok(dot) => dot_to_block(&dot, warnings),
Err(e) => Block::Para(vec![inlinestr(&e.to_string())]),
}
}
Err(e) => Block::Para(vec![inlinestr(&e.to_string())]),
}
}
fn typeset_svg(svg: Vec<u8>) -> Block {
let url = svg_as_data_url(svg);
let attr = ("".to_string(), vec![], vec![]);
let img = Inline::Image(attr, vec![], (url, "".to_string()));
Block::Para(vec![img])
}
fn svg_as_data_url(svg: Vec<u8>) -> String {
let svg = base64::encode(&svg);
format!("data:image/svg+xml;base64,{}", svg)
}