Skip to content

Commit

Permalink
initial work on inline svg files
Browse files Browse the repository at this point in the history
  • Loading branch information
dylanowen committed Jun 15, 2020
1 parent e1b7352 commit 7313882
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 121 deletions.
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,8 @@ mdbook = "0.3"
clap = "2.33"
serde_json = "1.0"
pulldown-cmark = "0.5"
pulldown-cmark-to-cmark = "1.2.4"
pulldown-cmark-to-cmark = "1.2.4"

lazy_static = "1.4.0"
regex = "1.3"
toml = "0.5"
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ clippy:
build: format clippy
cargo build

install: format
test: format clippy
cargo test

install: format clippy
cargo install --force --path .

default: build
Expand Down
17 changes: 11 additions & 6 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
mod preprocessor;
mod renderer;
#[macro_use]
extern crate lazy_static;

use std::io;
use std::process;

use crate::preprocessor::{Graphviz, PREPROCESSOR_NAME};
use clap::{App, Arg, ArgMatches, SubCommand};
use mdbook::errors::Error;
use mdbook::preprocess::{CmdPreprocessor, Preprocessor};
use std::io;
use std::process;

use crate::preprocessor::{GraphvizPreprocessor, PREPROCESSOR_NAME};

mod preprocessor;
mod renderer;

pub fn make_app() -> App<'static, 'static> {
App::new(PREPROCESSOR_NAME)
Expand All @@ -21,7 +26,7 @@ pub fn make_app() -> App<'static, 'static> {
fn main() {
let matches = make_app().get_matches();

let preprocessor = Graphviz::command_line_renderer();
let preprocessor = GraphvizPreprocessor;

if let Some(sub_args) = matches.subcommand_matches("supports") {
handle_supports(&preprocessor, sub_args);
Expand Down
185 changes: 92 additions & 93 deletions src/preprocessor.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,39 @@
use std::marker::PhantomData;
use std::path::PathBuf;

use mdbook::book::{Book, Chapter};
use mdbook::errors::Error;
use mdbook::errors::Result;
use mdbook::preprocess::{Preprocessor, PreprocessorContext};
use mdbook::BookItem;
use pulldown_cmark::{Event, LinkType, Parser, Tag};
use pulldown_cmark::{Event, Parser, Tag};
use pulldown_cmark_to_cmark::fmt::cmark;
use std::path::PathBuf;
use toml::Value;

use crate::renderer::{CommandLineGraphviz, GraphvizRenderer};
use crate::renderer::{CLIGraphviz, CLIGraphvizToFile, GraphvizRenderer};

pub static PREPROCESSOR_NAME: &str = "graphviz";
pub static INFO_STRING_PREFIX: &str = "dot process";

pub struct Graphviz {
renderer: Box<dyn GraphvizRenderer>,
}
pub struct GraphvizPreprocessor;

impl Graphviz {
pub fn command_line_renderer() -> Graphviz {
let renderer = CommandLineGraphviz;

Graphviz {
renderer: Box::new(renderer),
}
}
pub struct Graphviz<R: GraphvizRenderer> {
_phantom: PhantomData<*const R>,
}

impl Preprocessor for Graphviz {
impl Preprocessor for GraphvizPreprocessor {
fn name(&self) -> &str {
PREPROCESSOR_NAME
}

fn run(&self, ctx: &PreprocessorContext, mut book: Book) -> Result<Book> {
let output_to_file = ctx
.config
.get_preprocessor(self.name())
.and_then(|t| t.get("output-to-file"))
.and_then(Value::as_bool)
.unwrap_or(false);

let src_dir = ctx.root.clone().join(&ctx.config.book.src);
let mut error = Ok(());

Expand All @@ -44,7 +46,11 @@ impl Preprocessor for Graphviz {
// remove the chapter filename
full_path.pop();

error = self.process_chapter(chapter, &full_path)
error = if !output_to_file {
Graphviz::<CLIGraphviz>::new().process_chapter(chapter, &full_path)
} else {
Graphviz::<CLIGraphvizToFile>::new().process_chapter(chapter, &full_path)
};
}
}
});
Expand All @@ -53,23 +59,30 @@ impl Preprocessor for Graphviz {
}

fn supports_renderer(&self, _renderer: &str) -> bool {
// since we're just outputting markdown images, this should support any renderer
// since we're just outputting markdown images or inline html, this "should" support any renderer
true
}
}

impl Graphviz {
impl<R: GraphvizRenderer> Graphviz<R> {
fn new() -> Graphviz<R> {
Graphviz {
_phantom: PhantomData,
}
}

fn process_chapter(&self, chapter: &mut Chapter, chapter_path: &PathBuf) -> Result<()> {
let mut buf = String::with_capacity(chapter.content.len());
let mut graphviz_block_builder: Option<GraphvizBlockBuilder> = None;
let mut image_index = 0;

let event_results: Result<Vec<Vec<Event>>> = Parser::new(&chapter.content)
.map(|e| {
if let Some(ref mut builder) = graphviz_block_builder {
if let Some(mut builder) = graphviz_block_builder.take() {
match e {
Event::Text(ref text) => {
builder.append_code(&**text);
graphviz_block_builder = Some(builder);

Ok(vec![])
}
Expand All @@ -83,13 +96,14 @@ impl Graphviz {
// finish our digraph
let block = builder.build(image_index);
image_index += 1;
graphviz_block_builder = None;

block.render_graphviz(&*self.renderer)?;
R::render_graphviz(block)
}
_ => {
graphviz_block_builder = Some(builder);

Ok(block.tag_events())
Ok(vec![])
}
_ => Ok(vec![]),
}
} else {
match e {
Expand Down Expand Up @@ -158,72 +172,55 @@ impl GraphvizBlockBuilder {
self.code.push_str(&code.into());
}

fn build(&self, index: usize) -> GraphvizBlock {
let cleaned_code = self.code.trim();
fn build(self, index: usize) -> GraphvizBlock {
let GraphvizBlockBuilder {
chapter_name,
graph_name,
code,
path,
} = self;
let cleaned_code = code.trim();

GraphvizBlock {
graph_name,
code: cleaned_code.into(),
chapter_name,
chapter_path: path,
index,
}
}
}

pub struct GraphvizBlock {
pub graph_name: String,
pub code: String,
pub chapter_name: String,
pub chapter_path: PathBuf,
pub index: usize,
}

impl GraphvizBlock {
pub fn file_name(&self) -> String {
let image_name = if !self.graph_name.is_empty() {
format!(
"{}_{}_{}.generated",
normalize_id(&self.chapter_name),
normalize_id(&self.graph_name),
index
self.index
)
} else {
format!("{}_{}.generated", normalize_id(&self.chapter_name), index)
format!(
"{}_{}.generated",
normalize_id(&self.chapter_name),
self.index
)
};

GraphvizBlock::new(
self.graph_name.clone(),
image_name,
cleaned_code.into(),
self.path.clone(),
)
format!("{}.svg", image_name)
}
}

struct GraphvizBlock {
graph_name: String,
image_name: String,
code: String,
chapter_path: PathBuf,
}

impl GraphvizBlock {
fn new<S: Into<String>>(graph_name: S, image_name: S, code: S, path: PathBuf) -> GraphvizBlock {
let image_name = image_name.into();

GraphvizBlock {
graph_name: graph_name.into(),
image_name,
code: code.into(),
chapter_path: path,
}
}

fn tag_events<'a, 'b>(&'a self) -> Vec<Event<'b>> {
vec![
Event::Start(self.image_tag()),
Event::End(self.image_tag()),
Event::Text("\n\n".into()),
]
}

fn render_graphviz(&self, renderer: &dyn GraphvizRenderer) -> Result<()> {
let output_path = self.chapter_path.join(self.file_name());

renderer.render_graphviz(&self.code, &output_path)
}

fn image_tag<'a, 'b>(&'a self) -> Tag<'b> {
Tag::Image(
LinkType::Inline,
self.file_name().into(),
self.graph_name.clone().into(),
)
}

fn file_name(&self) -> String {
format!("{}.svg", self.image_name)
pub fn output_path(&self) -> PathBuf {
self.chapter_path.join(self.file_name())
}
}

Expand Down Expand Up @@ -252,8 +249,16 @@ mod test {
struct NoopRenderer;

impl GraphvizRenderer for NoopRenderer {
fn render_graphviz(&self, _code: &String, _output_path: &PathBuf) -> Result<()> {
Ok(())
fn render_graphviz<'a>(block: GraphvizBlock) -> Result<Vec<Event<'a>>> {
let file_name = block.file_name();
let output_path = block.output_path();
let GraphvizBlock {
graph_name, index, ..
} = block;

Ok(vec![Event::Text(
format!("{}|{:?}|{}|{}", file_name, output_path, graph_name, index).into(),
)])
}
}

Expand All @@ -271,7 +276,7 @@ digraph Test {

process_chapter(&mut chapter).unwrap();

assert_eq!(expected, chapter.content);
assert_eq!(chapter.content, expected);
}

#[test]
Expand All @@ -290,15 +295,13 @@ digraph Test {
let expected = format!(
r#"# Chapter
![]({}_0.generated.svg)
"#,
NORMALIZED_CHAPTER_NAME
{}_0.generated.svg|"./{}_0.generated.svg"||0"#,
NORMALIZED_CHAPTER_NAME, NORMALIZED_CHAPTER_NAME
);

process_chapter(&mut chapter).unwrap();

assert_eq!(expected, chapter.content);
assert_eq!(chapter.content, expected);
}

#[test]
Expand All @@ -317,21 +320,17 @@ digraph Test {
let expected = format!(
r#"# Chapter
![]({}_graph_name_0.generated.svg "Graph Name")
"#,
NORMALIZED_CHAPTER_NAME
{}_graph_name_0.generated.svg|"./{}_graph_name_0.generated.svg"|Graph Name|0"#,
NORMALIZED_CHAPTER_NAME, NORMALIZED_CHAPTER_NAME
);

process_chapter(&mut chapter).unwrap();

assert_eq!(expected, chapter.content);
assert_eq!(chapter.content, expected);
}

fn process_chapter(chapter: &mut Chapter) -> Result<()> {
let graphviz = Graphviz {
renderer: Box::new(NoopRenderer),
};
let graphviz = Graphviz::<NoopRenderer>::new();

graphviz.process_chapter(chapter, &PathBuf::from("./"))
}
Expand Down
Loading

0 comments on commit 7313882

Please sign in to comment.