mirror of
https://github.com/lise-henry/crowbook
synced 2024-11-18 00:13:55 +01:00
no longer change current directory, as it generated problems in multithreaded envs
This commit is contained in:
parent
431907cf39
commit
bd2971ab61
217
src/lib/book.rs
217
src/lib/book.rs
@ -13,8 +13,7 @@ use number::Number;
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{self, Write,Read};
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@ -28,13 +27,13 @@ title:str:Untitled # The title of the book
|
||||
lang:str:en # The language of the book
|
||||
subject:str # Subject of the book (used for EPUB metadata)
|
||||
description:str # Description of the book (used for EPUB metadata)
|
||||
cover:str # File name of the cover of the book
|
||||
cover:path # File name of the cover of the book
|
||||
# Output options
|
||||
output.epub:str # Output file name for EPUB rendering
|
||||
output.html:str # Output file name for HTML rendering
|
||||
output.tex:str # Output file name for LaTeX rendering
|
||||
output.pdf:str # Output file name for PDF rendering
|
||||
output.odt:str # Output file name for ODT rendering
|
||||
output.epub:path # Output file name for EPUB rendering
|
||||
output.html:path # Output file name for HTML rendering
|
||||
output.tex:path # Output file name for LaTeX rendering
|
||||
output.pdf:path # Output file name for PDF rendering
|
||||
output.odt:path # Output file name for ODT rendering
|
||||
|
||||
|
||||
# Misc options
|
||||
@ -45,22 +44,22 @@ autoclean:bool:true # Toggles cleaning of input markdown (not us
|
||||
verbose:bool:false # Toggle verbose mode
|
||||
side_notes:bool:false # Display footnotes as side notes in HTML/Epub
|
||||
nb_char:char:' ' # The non-breaking character to use for autoclean when lang is set to fr
|
||||
temp_dir:str:. # Path where to create a temporary directory
|
||||
temp_dir:path:. # Path where to create a temporary directory
|
||||
numbering_template:str:{{number}}. {{title}} # Format of numbered titles
|
||||
|
||||
# HTML options
|
||||
html.template:str # Path of an HTML template
|
||||
html.css:str # Path of a stylesheet to use with HTML rendering
|
||||
html.template:path # Path of an HTML template
|
||||
html.css:path # Path of a stylesheet to use with HTML rendering
|
||||
|
||||
# EPUB options
|
||||
epub.version:int:2 # The EPUB version to generate
|
||||
epub.css:str # Path of a stylesheet to use with EPUB rendering
|
||||
epub.template:str # Path of an epub template for chapter
|
||||
epub.css:path # Path of a stylesheet to use with EPUB rendering
|
||||
epub.template:path # Path of an epub template for chapter
|
||||
|
||||
# LaTeX options
|
||||
tex.links_as_footnotes:bool:true # If set to true, will add foontotes to URL of links in LaTeX/PDF output
|
||||
tex.command:str:pdflatex # LaTeX flavour to use for generating PDF
|
||||
tex.template:str # Path of a LaTeX template file
|
||||
tex.template:path # Path of a LaTeX template file
|
||||
";
|
||||
|
||||
|
||||
@ -103,11 +102,13 @@ pub struct Book {
|
||||
valid_bools: Vec<&'static str>,
|
||||
valid_chars: Vec<&'static str>,
|
||||
valid_strings: Vec<&'static str>,
|
||||
valid_paths: Vec<&'static str>,
|
||||
valid_ints: Vec<&'static str>,
|
||||
|
||||
/// root path of the book
|
||||
root: PathBuf,
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl Book {
|
||||
/// Creates a new, empty `Book` with default options
|
||||
pub fn new() -> Book {
|
||||
@ -118,6 +119,8 @@ impl Book {
|
||||
valid_chars:vec!(),
|
||||
valid_ints:vec!(),
|
||||
valid_strings:vec!(),
|
||||
valid_paths:vec!(),
|
||||
root: PathBuf::new(),
|
||||
};
|
||||
for (_, key, option_type, default_value) in Book::options_to_vec() {
|
||||
if key.is_none() {
|
||||
@ -129,6 +132,7 @@ impl Book {
|
||||
"bool" => book.valid_bools.push(key),
|
||||
"int" => book.valid_ints.push(key),
|
||||
"char" => book.valid_chars.push(key),
|
||||
"path" => book.valid_paths.push(key),
|
||||
_ => panic!(format!("Ill-formatted OPTIONS string: unrecognized type '{}'", option_type.unwrap())),
|
||||
}
|
||||
if let Some(value) = default_value {
|
||||
@ -138,48 +142,36 @@ impl Book {
|
||||
book
|
||||
}
|
||||
|
||||
|
||||
/// Returns a description of all options valid to pass to a book.
|
||||
/// Creates a new book from a file
|
||||
///
|
||||
/// # arguments
|
||||
/// * `md`: whether the output should be formatted in Markdown
|
||||
pub fn description(md: bool) -> String {
|
||||
let mut out = String::new();
|
||||
let mut previous_is_comment = true;
|
||||
for (comment, key, o_type, default) in Book::options_to_vec() {
|
||||
if key.is_none() {
|
||||
if !previous_is_comment {
|
||||
out.push_str("\n");
|
||||
previous_is_comment = true;
|
||||
}
|
||||
out.push_str(&format!("### {} ###\n", comment));
|
||||
continue;
|
||||
}
|
||||
previous_is_comment = false;
|
||||
let o_type = match o_type.unwrap() {
|
||||
"bool" => "boolean",
|
||||
"int" => "integer",
|
||||
"char" => "char",
|
||||
"str" => "string",
|
||||
_ => unreachable!()
|
||||
};
|
||||
let def = if let Some(value) = default {
|
||||
value
|
||||
} else {
|
||||
"not set"
|
||||
};
|
||||
if md {
|
||||
out.push_str(&format!("- **`{}`**
|
||||
- **type**: {}
|
||||
- **default value**: `{}`
|
||||
- {}\n", key.unwrap(), o_type, def, comment));
|
||||
} else {
|
||||
out.push_str(&format!("- {} (type: {}) (default: {}) {}\n", key.unwrap(), o_type, def,comment));
|
||||
}
|
||||
/// Note that this method also changes the current directory to the one of this file
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `filename`: the path of file to load.
|
||||
/// * `verbose`: sets the book to verbose mode even if the file's doesn't specify it
|
||||
/// or specifies `verbose: false`
|
||||
pub fn new_from_file(filename: &str, verbose: bool) -> Result<Book> {
|
||||
let mut book = Book::new();
|
||||
if verbose {
|
||||
book.set_option("verbose", "true").unwrap();
|
||||
}
|
||||
out
|
||||
|
||||
let path = Path::new(filename);
|
||||
let mut f = try!(File::open(&path).map_err(|_| Error::FileNotFound(String::from(filename))));
|
||||
// Set book path to book's directory
|
||||
if let Some(parent) = path.parent() {
|
||||
book.root = parent.to_owned();
|
||||
}
|
||||
|
||||
let mut s = String::new();
|
||||
try!(f.read_to_string(&mut s).map_err(|_| Error::ConfigParser("file contains invalid UTF-8, could not parse it",
|
||||
filename.to_owned())));
|
||||
|
||||
try!(book.set_from_config(&s));
|
||||
Ok(book)
|
||||
}
|
||||
|
||||
|
||||
/// Sets an option
|
||||
///
|
||||
/// # Arguments
|
||||
@ -205,6 +197,9 @@ impl Book {
|
||||
if self.valid_strings.contains(&key) {
|
||||
self.options.insert(key.to_owned(), BookOption::String(value.to_owned()));
|
||||
Ok(())
|
||||
} else if self.valid_paths.contains(&key) {
|
||||
self.options.insert(key.to_owned(), BookOption::Path(value.to_owned()));
|
||||
Ok(())
|
||||
} else if self.valid_chars.contains(&key) {
|
||||
let words: Vec<_> = value.trim().split('\'').collect();
|
||||
if words.len() != 3 {
|
||||
@ -333,40 +328,6 @@ impl Book {
|
||||
}
|
||||
|
||||
|
||||
/// Creates a new book from a file
|
||||
///
|
||||
/// Note that this method also changes the current directory to the one of this file
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `filename`: the path of file to load.
|
||||
/// * `verbose`: sets the book to verbose mode even if the file's doesn't specify it
|
||||
/// or specifies `verbose: false`
|
||||
pub fn new_from_file(filename: &str, verbose: bool) -> Result<Book> {
|
||||
let path = Path::new(filename);
|
||||
let mut f = try!(File::open(&path).map_err(|_| Error::FileNotFound(String::from(filename))));
|
||||
|
||||
// change current directory
|
||||
if let Some(parent) = path.parent() {
|
||||
if !parent.to_string_lossy().is_empty() {
|
||||
if !env::set_current_dir(&parent).is_ok() {
|
||||
return Err(Error::ConfigParser("could not change current directory to the one of the config file",
|
||||
format!("{}", parent.display())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let mut s = String::new();
|
||||
|
||||
try!(f.read_to_string(&mut s).map_err(|_| Error::ConfigParser("file contains invalid UTF-8, could not parse it",
|
||||
String::from(filename))));
|
||||
let mut book = Book::new();
|
||||
if verbose {
|
||||
book.set_option("verbose", "true").unwrap();
|
||||
}
|
||||
try!(book.set_from_config(&s));
|
||||
Ok(book)
|
||||
}
|
||||
|
||||
/// Adds a chapter, as a file name, to the book
|
||||
///
|
||||
@ -383,12 +344,56 @@ impl Book {
|
||||
pub fn add_chapter(&mut self, number: Number, file: &str) -> Result<()> {
|
||||
self.debug(&format!("Parsing chapter: {}...", file));
|
||||
let mut parser = Parser::new();
|
||||
let file = self.root.join(file);
|
||||
let v = try!(parser.parse_file(file));
|
||||
self.chapters.push((number, v));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// Returns a description of all options valid to pass to a book.
|
||||
///
|
||||
/// # arguments
|
||||
/// * `md`: whether the output should be formatted in Markdown
|
||||
pub fn description(md: bool) -> String {
|
||||
let mut out = String::new();
|
||||
let mut previous_is_comment = true;
|
||||
for (comment, key, o_type, default) in Book::options_to_vec() {
|
||||
if key.is_none() {
|
||||
if !previous_is_comment {
|
||||
out.push_str("\n");
|
||||
previous_is_comment = true;
|
||||
}
|
||||
out.push_str(&format!("### {} ###\n", comment));
|
||||
continue;
|
||||
}
|
||||
previous_is_comment = false;
|
||||
let o_type = match o_type.unwrap() {
|
||||
"bool" => "boolean",
|
||||
"int" => "integer",
|
||||
"char" => "char",
|
||||
"str" => "string",
|
||||
_ => unreachable!()
|
||||
};
|
||||
let def = if let Some(value) = default {
|
||||
value
|
||||
} else {
|
||||
"not set"
|
||||
};
|
||||
if md {
|
||||
out.push_str(&format!("- **`{}`**
|
||||
- **type**: {}
|
||||
- **default value**: `{}`
|
||||
- {}\n", key.unwrap(), o_type, def, comment));
|
||||
} else {
|
||||
out.push_str(&format!("- {} (type: {}) (default: {}) {}\n", key.unwrap(), o_type, def,comment));
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Adds a chapter, as a string, to the book
|
||||
///
|
||||
/// `Book` will then parse the string and store the AST (i.e., a vector
|
||||
@ -458,11 +463,31 @@ impl Book {
|
||||
self.options.get(key).ok_or(Error::InvalidOption(format!("option {} is not present", key)))
|
||||
}
|
||||
|
||||
/// gets a string option as str
|
||||
/// Gets a string option
|
||||
pub fn get_str(&self, key: &str) -> Result<&str> {
|
||||
try!(self.get_option(key)).as_str()
|
||||
}
|
||||
|
||||
/// Get a path option
|
||||
///
|
||||
/// Adds book's root path before it
|
||||
pub fn get_path(&self, key: &str) -> Result<String> {
|
||||
let path: &str = try!(try!(self.get_option(key)).as_path());
|
||||
let new_path:PathBuf = self.root.join(path);
|
||||
if let Some(path) = new_path.to_str() {
|
||||
Ok(path.to_owned())
|
||||
} else {
|
||||
Err(Error::BookOption(format!("'{}''s path contains invalid UTF-8 code", key)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a path option
|
||||
///
|
||||
/// Don't add book's root path before it
|
||||
pub fn get_relative_path(&self, key: &str) -> Result<&str> {
|
||||
try!(self.get_option(key)).as_path()
|
||||
}
|
||||
|
||||
/// gets a bool option
|
||||
pub fn get_bool(&self, key: &str) -> Result<bool> {
|
||||
try!(self.get_option(key)).as_bool()
|
||||
@ -485,7 +510,7 @@ impl Book {
|
||||
let mut latex = LatexRenderer::new(&self);
|
||||
let result = try!(latex.render_pdf());
|
||||
self.debug(&result);
|
||||
self.println(&format!("Successfully generated pdf file: {}", self.get_str("output.pdf").unwrap()));
|
||||
self.println(&format!("Successfully generated pdf file: {}", self.get_path("output.pdf").unwrap()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -495,7 +520,7 @@ impl Book {
|
||||
let mut epub = EpubRenderer::new(&self);
|
||||
let result = try!(epub.render_book());
|
||||
self.debug(&result);
|
||||
self.println(&format!("Successfully generated epub file: {}", self.get_str("output.epub").unwrap()));
|
||||
self.println(&format!("Successfully generated epub file: {}", self.get_path("output.epub").unwrap()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -505,7 +530,7 @@ impl Book {
|
||||
let mut odt = OdtRenderer::new(&self);
|
||||
let result = try!(odt.render_book());
|
||||
self.debug(&result);
|
||||
self.println(&format!("Successfully generated odt file: {}", self.get_str("output.odt").unwrap()));
|
||||
self.println(&format!("Successfully generated odt file: {}", self.get_path("output.odt").unwrap()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -539,22 +564,22 @@ impl Book {
|
||||
try!(self.render_epub());
|
||||
}
|
||||
|
||||
if let Ok(file) = self.get_str("output.html") {
|
||||
if let Ok(file) = self.get_path("output.html") {
|
||||
did_some_stuff = true;
|
||||
let mut f = try!(File::create(file).map_err(|_| Error::Render("could not create HTML file")));
|
||||
try!(self.render_html(&mut f));
|
||||
}
|
||||
if let Ok(file) = self.get_str("output.tex") {
|
||||
if let Ok(file) = self.get_path("output.tex") {
|
||||
did_some_stuff = true;
|
||||
let mut f = try!(File::create(file).map_err(|_| Error::Render("could not create LaTeX file")));
|
||||
try!(self.render_tex(&mut f));
|
||||
}
|
||||
if self.get_str("output.pdf").is_ok() {
|
||||
if self.get_option("output.pdf").is_ok() {
|
||||
did_some_stuff = true;
|
||||
try!(self.render_pdf());
|
||||
}
|
||||
|
||||
if self.get_str("output.odt").is_ok() {
|
||||
if self.get_option("output.odt").is_ok() {
|
||||
did_some_stuff = true;
|
||||
try!(self.render_odt());
|
||||
}
|
||||
|
@ -3,10 +3,11 @@ use error::{Error,Result};
|
||||
/// Structure for storing a book option
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum BookOption {
|
||||
String(String), /// Stores a string
|
||||
String(String), // Stores a string
|
||||
Bool(bool), // stores a boolean
|
||||
Char(char), // stores a char
|
||||
Int(i32), // stores an int
|
||||
Path(String), // Stores a path
|
||||
}
|
||||
|
||||
impl BookOption {
|
||||
@ -18,6 +19,14 @@ impl BookOption {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the BookOption as a &str, but only if it is a path
|
||||
pub fn as_path(&self) -> Result<&str> {
|
||||
match *self {
|
||||
BookOption::Path(ref s) => Ok(s),
|
||||
_ => Err(Error::BookOption(format!("{:?} is not a path", self)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Retuns the BookOption as a bool
|
||||
pub fn as_bool(&self) -> Result<bool> {
|
||||
match *self {
|
||||
|
@ -55,7 +55,7 @@ impl<'a> EpubRenderer<'a> {
|
||||
|
||||
/// Render a book
|
||||
pub fn render_book(&mut self) -> Result<String> {
|
||||
let mut zipper = try!(Zipper::new(&self.book.get_str("temp_dir").unwrap()));
|
||||
let mut zipper = try!(Zipper::new(&self.book.get_path("temp_dir").unwrap()));
|
||||
|
||||
// Write mimetype
|
||||
try!(zipper.write("mimetype", b"application/epub+zip", true));
|
||||
@ -104,19 +104,18 @@ impl<'a> EpubRenderer<'a> {
|
||||
try!(zipper.write("toc.ncx", &try!(self.render_toc()).as_bytes(), true));
|
||||
|
||||
// Write the cover (if needs be)
|
||||
if let Ok(cover) = self.book.get_str("cover") {
|
||||
let s: &str = &*cover;
|
||||
let mut f = try!(File::open(s).map_err(|_| Error::FileNotFound(String::from(s))));
|
||||
if let Ok(ref s) = self.book.get_path("cover") {
|
||||
let mut f = try!(File::open(s).map_err(|_| Error::FileNotFound(s.to_owned())));
|
||||
let mut content = vec!();
|
||||
try!(f.read_to_end(&mut content).map_err(|_| Error::Render("Error while reading cover file")));
|
||||
try!(zipper.write(s, &content, true));
|
||||
try!(zipper.write(self.book.get_relative_path("cover").unwrap(), &content, true));
|
||||
|
||||
// also write cover.xhtml
|
||||
try!(zipper.write("cover.xhtml", &try!(self.render_cover()).as_bytes(), true));
|
||||
}
|
||||
|
||||
if let Ok(epub_file) = self.book.get_str("output.epub") {
|
||||
let res = try!(zipper.generate_epub(epub_file));
|
||||
if let Ok(epub_file) = self.book.get_path("output.epub") {
|
||||
let res = try!(zipper.generate_epub(&epub_file));
|
||||
Ok(res)
|
||||
} else {
|
||||
Err(Error::Render("no output epub file specified in book config"))
|
||||
@ -174,7 +173,7 @@ impl<'a> EpubRenderer<'a> {
|
||||
if let Ok(s) = self.book.get_str("subject") {
|
||||
optional.push_str(&format!("<dc:subject>{}</dc:subject>\n", s));
|
||||
}
|
||||
if let Ok(s) = self.book.get_str("cover") {
|
||||
if let Ok(s) = self.book.get_relative_path("cover") {
|
||||
optional.push_str(&format!("<meta name = \"cover\" content = \"{}\" />\n", s));
|
||||
cover_xhtml.push_str(&format!("<reference type=\"cover\" title=\"Cover\" href=\"cover.xhtml\" />"));
|
||||
}
|
||||
@ -188,7 +187,7 @@ impl<'a> EpubRenderer<'a> {
|
||||
let mut items = String::new();
|
||||
let mut itemrefs = String::new();
|
||||
let mut coverref = String::new();
|
||||
if self.book.get_str("cover").is_ok() {
|
||||
if self.book.get_option("cover").is_ok() {
|
||||
items.push_str("<item id = \"cover_xhtml\" href = \"cover.xhtml\" media-type = \"application/xhtml+xml\" />\n");
|
||||
coverref.push_str("<itemref idref = \"cover_xhtml\" />");
|
||||
}
|
||||
@ -200,7 +199,7 @@ impl<'a> EpubRenderer<'a> {
|
||||
itemrefs.push_str(&format!("<itemref idref=\"{}\" />\n", to_id(&filename)));
|
||||
}
|
||||
// oh we must put cover in the manifest too
|
||||
if let Ok(s) = self.book.get_str("cover") {
|
||||
if let Ok(s) = self.book.get_relative_path("cover") {
|
||||
let format = if let Some(ext) = Path::new(s).extension() {
|
||||
if let Some(extension) = ext.to_str() {
|
||||
match extension {
|
||||
@ -247,7 +246,7 @@ impl<'a> EpubRenderer<'a> {
|
||||
|
||||
/// Render cover.xhtml
|
||||
fn render_cover(&self) -> Result<String> {
|
||||
if let Ok(ref cover) = self.book.get_str("cover") {
|
||||
if let Ok(cover) = self.book.get_relative_path("cover") {
|
||||
let template = mustache::compile_str(if self.book.get_i32("epub.version").unwrap() == 3 {epub3::COVER} else {COVER});
|
||||
let data = self.book.get_mapbuilder("none")
|
||||
.insert_str("cover", cover.clone())
|
||||
|
@ -44,13 +44,11 @@ impl<'a> LatexRenderer<'a> {
|
||||
|
||||
/// Render pdf in a file
|
||||
pub fn render_pdf(&mut self) -> Result<String> {
|
||||
if let Ok(pdf_file) = self.book.get_str("output.pdf") {
|
||||
let base_file = try!(Path::new(pdf_file).file_stem().ok_or(Error::Render("could not stem pdf filename")));
|
||||
let tex_file = format!("{}.tex", base_file.to_str().unwrap());
|
||||
if let Ok(pdf_file) = self.book.get_path("output.pdf") {
|
||||
let content = try!(self.render_book());
|
||||
let mut zipper = try!(Zipper::new(self.book.get_str("temp_dir").unwrap()));
|
||||
try!(zipper.write(&tex_file, &content.as_bytes(), false));
|
||||
zipper.generate_pdf(&self.book.get_str("tex.command").unwrap(), &tex_file, pdf_file)
|
||||
let mut zipper = try!(Zipper::new(&self.book.get_path("temp_dir").unwrap()));
|
||||
try!(zipper.write("result.tex", &content.as_bytes(), false));
|
||||
zipper.generate_pdf(&self.book.get_str("tex.command").unwrap(), "result.tex", &pdf_file)
|
||||
} else {
|
||||
Err(Error::Render("no output pdf file specified in book config"))
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ impl<'a> OdtRenderer<'a> {
|
||||
pub fn render_book(&mut self) -> Result<String> {
|
||||
let content = try!(self.render_content());
|
||||
|
||||
let mut zipper = try!(Zipper::new(self.book.get_str("temp_dir").unwrap()));
|
||||
let mut zipper = try!(Zipper::new(&self.book.get_path("temp_dir").unwrap()));
|
||||
|
||||
// Write template.odt there
|
||||
try!(zipper.write("template.odt", odt::ODT, false));
|
||||
@ -58,7 +58,7 @@ impl<'a> OdtRenderer<'a> {
|
||||
// Complete it with content.xml
|
||||
try!(zipper.write("content.xml", &content.as_bytes(), false));
|
||||
// Zip and copy
|
||||
if let Ok(file) = self.book.get_str("output.odt") {
|
||||
if let Ok(ref file) = self.book.get_path("output.odt") {
|
||||
zipper.generate_odt(file)
|
||||
} else {
|
||||
panic!("odt.render_book called while book.output_odt is not set");
|
||||
|
@ -19,6 +19,8 @@ use token::Token;
|
||||
use error::{Result,Error};
|
||||
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
use std::convert::AsRef;
|
||||
use std::io::Read;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@ -62,11 +64,12 @@ impl Parser {
|
||||
}
|
||||
|
||||
/// Parse a file and returns an AST or an error
|
||||
pub fn parse_file(&mut self, filename: &str) -> Result<Vec<Token>> {
|
||||
let mut f = try!(File::open(filename).map_err(|_| Error::FileNotFound(String::from(filename))));
|
||||
pub fn parse_file<P: AsRef<Path>>(&mut self, filename: P) -> Result<Vec<Token>> {
|
||||
let path: &Path = filename.as_ref();
|
||||
let mut f = try!(File::open(path).map_err(|_| Error::FileNotFound(format!("{}", path.display()))));
|
||||
let mut s = String::new();
|
||||
|
||||
try!(f.read_to_string(&mut s).map_err(|_| Error::Parser(format!("file {} contains invalid UTF-8, could not parse it", filename))));
|
||||
try!(f.read_to_string(&mut s).map_err(|_| Error::Parser(format!("file {} contains invalid UTF-8, could not parse it", path.display()))));
|
||||
self.parse(&s)
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ impl Zipper {
|
||||
/// Unzip a file and deletes it afterwards
|
||||
pub fn unzip(&mut self, file: &str) -> Result<()> {
|
||||
// change to dest directory to unzip file
|
||||
let dir = try!(env::current_dir().map_err(|_| Error::Zipper("could not get current directory".to_owned())));
|
||||
let dir = env::current_dir().unwrap();
|
||||
try!(env::set_current_dir(&self.path).map_err(|_| Error::Zipper("could not change current directory".to_owned())));
|
||||
let output = Command::new("unzip")
|
||||
.arg(file)
|
||||
@ -88,7 +88,7 @@ impl Zipper {
|
||||
.map_err(|e| Error::Zipper(format!("failed to execute unzip on {}: {}", file, e)));
|
||||
|
||||
// change back to original current directory before try! ing anything
|
||||
try!(env::set_current_dir(dir).map_err(|_| Error::Zipper("could not change back to old directory".to_owned())));
|
||||
env::set_current_dir(dir).unwrap();
|
||||
try!(output);
|
||||
|
||||
fs::remove_file(self.path.join(file))
|
||||
@ -96,18 +96,15 @@ impl Zipper {
|
||||
}
|
||||
|
||||
/// run command and copy file name (supposed to result from the command) to current dir
|
||||
pub fn run_command(&mut self, mut command: Command, file: &str) -> Result<String> {
|
||||
let dir = try!(env::current_dir().map_err(|_| Error::Zipper("could not get current directory".to_owned())));
|
||||
try!(env::set_current_dir(&self.path).map_err(|_| Error::Zipper("could not change current directory".to_owned())));
|
||||
|
||||
pub fn run_command(&mut self, mut command: Command, in_file: &str, out_file: &str) -> Result<String> {
|
||||
let res_output = command.args(&self.args)
|
||||
.current_dir(&self.path)
|
||||
.output()
|
||||
.map_err(|e| Error::Zipper(format!("failed to execute process: {}", e)));
|
||||
try!(env::set_current_dir(dir).map_err(|_| Error::Zipper("could not change back to old directory".to_owned())));
|
||||
let output = try!(res_output);
|
||||
try!(fs::copy(self.path.join(file), file).map_err(|_| {
|
||||
try!(fs::copy(self.path.join(in_file), out_file).map_err(|_| {
|
||||
println!("{}", &String::from_utf8_lossy(&output.stdout));
|
||||
Error::Zipper(format!("could not copy file {}", file))
|
||||
Error::Zipper(format!("could not copy file {} to {}", in_file, out_file))
|
||||
}));
|
||||
Ok(String::from_utf8_lossy(&output.stdout).into_owned())
|
||||
}
|
||||
@ -116,34 +113,32 @@ impl Zipper {
|
||||
pub fn generate_odt(&mut self, odt_file: &str) -> Result<String> {
|
||||
let mut command = Command::new("zip");
|
||||
command.arg("-r");
|
||||
command.arg(odt_file);
|
||||
command.arg("result.odt");
|
||||
command.arg(".");
|
||||
self.run_command(command, odt_file)
|
||||
self.run_command(command, "result.odt", odt_file)
|
||||
}
|
||||
|
||||
|
||||
/// generate a pdf file into given file name
|
||||
pub fn generate_pdf(&mut self, command: &str, tex_file: &str, pdf_file: &str) -> Result<String> {
|
||||
// first pass
|
||||
let dir = try!(env::current_dir().map_err(|_| Error::Zipper("could not get current directory".to_owned())));
|
||||
try!(env::set_current_dir(&self.path).map_err(|_| Error::Zipper("could not change current directory".to_owned())));
|
||||
let _ = Command::new(command)
|
||||
.current_dir(&self.path)
|
||||
.arg(tex_file)
|
||||
.output();
|
||||
try!(env::set_current_dir(dir).map_err(|_| Error::Zipper("could not change back to old directory".to_owned())));
|
||||
|
||||
// second pass
|
||||
let mut command = Command::new(command);
|
||||
command.arg(tex_file);
|
||||
self.run_command(command, pdf_file)
|
||||
self.run_command(command, "result.pdf", pdf_file)
|
||||
}
|
||||
|
||||
/// generate an epub into given file name
|
||||
pub fn generate_epub(&mut self, file: &str) -> Result<String> {
|
||||
let mut command = Command::new("zip");
|
||||
command.arg("-X");
|
||||
command.arg(file);
|
||||
self.run_command(command, file)
|
||||
command.arg("result.epub");
|
||||
self.run_command(command, "result.epub", file)
|
||||
}
|
||||
}
|
||||
|
||||
|
15
tests/book.rs
Normal file
15
tests/book.rs
Normal file
@ -0,0 +1,15 @@
|
||||
extern crate crowbook;
|
||||
use crowbook::Book;
|
||||
use std::env;
|
||||
|
||||
#[test]
|
||||
fn test_book() {
|
||||
let book = Book::new_from_file(&format!("{}/{}", env!("CARGO_MANIFEST_DIR"), "tests/test.book"), false).unwrap();
|
||||
book.render_all().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn book_example() {
|
||||
let book = Book::new_from_file(&format!("{}/{}", env!("CARGO_MANIFEST_DIR"), "book_example/config.book"), false).unwrap();
|
||||
book.render_all().unwrap();
|
||||
}
|
Loading…
Reference in New Issue
Block a user