diff --git a/DEVELOP.md b/DEVELOP.md index 3ff5706..f070063 100644 --- a/DEVELOP.md +++ b/DEVELOP.md @@ -53,14 +53,14 @@ - [ ] Linkify (convert URL-like strings in links) - [x] Fast draft (prevent to parse time consuming parts) - [ ] Dynamics addons (e.g. katex iff math is used) -- [x] Watcher mode +- [ ] Watcher mode for single file compilation - [ ] Split CLI lib from compiler - [ ] Compile only modified chapters in watcher mode - [x] Compile only a subset of documents - [ ] Paper format support (A3, A5, ...) - [x] MD to NMD converter - [x] Include all .nmd file in running directory as default option in dossier configuration -- [ ] Compile single files +- [x] Compile single files - [ ] Table of contents with page numbers - [ ] Cover page - [ ] VS Code extension (https://dev.to/salesforceeng/how-to-build-a-vs-code-extension-for-markdown-preview-using-remark-processor-1169) diff --git a/README.md b/README.md index 65ccb30..a929ecb 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,7 @@ nmd dossier -p dossier/input/path reset [ -p ] Compile a dossier in `html`: ```shell -nmd compile dossier -f html -i dossier/input/path -o artifact/output/path +nmd compile -f html dossier -i dossier/input/path -o artifact/output/path ``` If you watch dossier files and compile them if anything changes, you should use watcher mode (`-w` option). diff --git a/src/cli.rs b/src/cli.rs index fb51d86..2722611 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -82,6 +82,12 @@ impl NmdCli { .num_args(1) .default_value("html") ) + .arg( + Arg::new("force-output") + .long("force") + .help("force output if destination not exists") + .action(ArgAction::SetTrue) + ) .arg( Arg::new("theme") .short('m') @@ -110,6 +116,12 @@ impl NmdCli { .help("fast draft instead of complete compilation") .action(ArgAction::SetTrue) ) + .arg( + Arg::new("style-file") + .long("style-file") + .help("add style file") + .action(ArgAction::Append) + ) .subcommand( Command::new("dossier") .about("Compile NMD dossier") @@ -333,6 +345,12 @@ impl NmdCli { compilation_configuration.set_fast_draft(fast_draft); + compilation_configuration.set_force_output(matches.get_flag("force-output")); + + if let Some(styles) = matches.get_many::("style-file") { + compilation_configuration.set_styles_raw_path(styles.map(|s| s.clone()).collect()); + } + match matches.subcommand() { Some(("dossier", compile_dossier_matches)) => { @@ -393,7 +411,7 @@ impl NmdCli { } else { - compilation_configuration.set_output_location(compilation_configuration.input_location().clone()); + compilation_configuration.set_output_location(compilation_configuration.input_location().parent().unwrap().to_path_buf()); } if watch { diff --git a/src/compiler.rs b/src/compiler.rs index 023ae74..a4475a5 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -16,11 +16,12 @@ pub mod bibliography; use std::{sync::{mpsc::{channel, RecvError}, Arc, RwLock}, thread, time::{Instant, SystemTime}}; use dossier::{dossier_configuration::DossierConfiguration, Document, Dossier}; +use dumpable::DumpConfiguration; use notify::{RecursiveMode, Watcher}; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use theme::Theme; use thiserror::Error; -use crate::{compiler::{dumpable::{DumpError, Dumpable}, loader::Loader, parsable::Parsable}, constants::{DOSSIER_CONFIGURATION_JSON_FILE_NAME, DOSSIER_CONFIGURATION_YAML_FILE_NAME}}; +use crate::{compiler::{dumpable::{DumpError, Dumpable}, loader::Loader, parsable::Parsable}, constants::{DOSSIER_CONFIGURATION_JSON_FILE_NAME, DOSSIER_CONFIGURATION_YAML_FILE_NAME}, utility::file_utility}; use self::{assembler::{assembler_configuration::AssemblerConfiguration, AssemblerError}, compilation_configuration::CompilationConfiguration, loader::LoadError, parsing::parsing_error::ParsingError}; @@ -110,7 +111,6 @@ impl Compiler { dossier.parse(compilation_configuration.format(), Arc::clone(&codex), Arc::new(RwLock::new(parsing_configuration)), Arc::new(None))?; - assembler_configuration.set_output_location(compilation_configuration.output_location().clone()); assembler_configuration.set_theme(dossier_theme); log::info!("assembling..."); @@ -123,7 +123,18 @@ impl Compiler { log::info!("end to assembly (assembly time {} ms)", assembly_time.elapsed().as_millis()); - artifact.dump()?; + let mut output_location = compilation_configuration.output_location().clone(); + + if output_location.is_dir() { + output_location = output_location.join(file_utility::build_output_file_name( + &dossier.name(), + Some(&compilation_configuration.format().get_extension()) + )); + } + + let dump_configuration = DumpConfiguration::new(output_location, compilation_configuration.force_output()); + + artifact.dump(&dump_configuration)?; log::info!("end to compile dossier (compile time: {} ms)", compile_start.elapsed().as_millis()); @@ -285,20 +296,31 @@ impl Compiler { document.parse(compilation_configuration.format(), Arc::clone(&codex), Arc::new(RwLock::new(parsing_configuration)), Arc::new(None))?; - assembler_configuration.set_output_location(compilation_configuration.output_location().clone()); assembler_configuration.set_theme(compilation_configuration.theme().clone().unwrap_or(Theme::default())); log::info!("assembling..."); + + let mut output_location = compilation_configuration.output_location().clone(); + + if output_location.is_dir() { + output_location = output_location.join(file_utility::build_output_file_name( + compilation_configuration.input_location().file_stem().unwrap().to_string_lossy().to_string().as_str(), + Some(&compilation_configuration.format().get_extension()) + )); + } + let assembly_time = Instant::now(); let assembler = assembler::from(compilation_configuration.format().clone(), assembler_configuration); - let mut artifact = assembler.assemble_document(compilation_configuration.output_location().clone(), &document)?; + let mut artifact = assembler.assemble_document_standalone(&output_location.file_name().unwrap().to_string_lossy().to_string(), Some(compilation_configuration.styles_raw_path()), None, None, &document)?; log::info!("end to assembly (assembly time {} ms)", assembly_time.elapsed().as_millis()); - artifact.dump()?; + let dump_configuration = DumpConfiguration::new(output_location, compilation_configuration.force_output()); + + artifact.dump(&dump_configuration)?; log::info!("end to compile dossier (compile time: {} ms)", compile_start.elapsed().as_millis()); @@ -306,7 +328,7 @@ impl Compiler { } pub fn watch_compile_file(compilation_configuration: CompilationConfiguration, min_elapsed_time_between_events_in_secs: u64) -> Result<(), CompilationError> { - todo!() + unimplemented!("watch compile file will be added in a next version") } } diff --git a/src/compiler/artifact.rs b/src/compiler/artifact.rs index d4deee0..d785213 100644 --- a/src/compiler/artifact.rs +++ b/src/compiler/artifact.rs @@ -1,18 +1,15 @@ pub mod artifact_assets; pub mod artifacts_collection; -use std::path::PathBuf; +use std::fmt::Display; -use getset::{Getters, MutGetters, Setters}; -use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator}; +use getset::{CopyGetters, Getters, MutGetters, Setters}; use thiserror::Error; -use crate::resource::{cached_disk_resource::CachedDiskResource, Resource, ResourceError}; +use crate::resource::{disk_resource::DiskResource, Resource, ResourceError}; -use self::artifact_assets::ArtifactAssets; - -use super::dumpable::{Dumpable, DumpError}; +use super::dumpable::{DumpConfiguration, DumpError, Dumpable}; #[derive(Error, Debug)] @@ -25,35 +22,49 @@ pub enum ArtifactError { ResourceError(#[from] ResourceError) } +pub type ArtifactContent = String; - -#[derive(Debug, Clone, Getters, MutGetters, Setters)] +#[derive(Debug, Clone, Getters, MutGetters, CopyGetters, Setters)] pub struct Artifact { - #[getset(get = "pub", set = "pub")] - output_path: PathBuf, - #[getset(get = "pub", get_mut = "pub", set = "pub")] - content: CachedDiskResource, + content: ArtifactContent, } impl Artifact { - pub fn new(output_path: PathBuf) -> Result { + pub fn new(content: ArtifactContent) -> Self { + + Self { + content + } + } +} - Ok(Self { - content: CachedDiskResource::try_from(output_path.clone())?, - output_path - }) +impl Display for Artifact { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.content) } } impl Dumpable for Artifact { - fn dump(&mut self) -> Result<(), DumpError> { + fn dump(&mut self, configuration: &DumpConfiguration) -> Result<(), DumpError> { - log::info!("dump artifact...",); + log::info!("dump artifact in {:?}", configuration.output_path()); - self.content.dump_cached_content(); + let mut disk_resource = DiskResource::try_from(configuration.output_path().clone())?; + + if configuration.force_dump() { + disk_resource.create_parents_dir()?; + } + + disk_resource.write(&self.content)?; Ok(()) } +} + +impl Into for Artifact { + fn into(self) -> String { + self.content + } } \ No newline at end of file diff --git a/src/compiler/artifact/artifacts_collection.rs b/src/compiler/artifact/artifacts_collection.rs index 86e7ffd..72fbaa6 100644 --- a/src/compiler/artifact/artifacts_collection.rs +++ b/src/compiler/artifact/artifacts_collection.rs @@ -54,23 +54,23 @@ impl ArtifactsCollection { // } } -impl Dumpable for ArtifactsCollection { - fn dump(&mut self) -> Result<(), DumpError> { +// impl Dumpable for ArtifactsCollection { +// fn dump(&mut self) -> Result<(), DumpError> { - log::info!("dump artifacts collection...",); +// log::info!("dump artifacts collection...",); - let error = self.artifacts.par_iter_mut().map(|artifact| { +// let error = self.artifacts.par_iter_mut().map(|artifact| { - log::info!("dumping artifact in {:?}", artifact.output_path()); +// log::info!("dumping artifact in {:?}", artifact.output_path()); - artifact.dump() - }) - .find_any(|result| result.is_err()); +// artifact.dump() +// }) +// .find_any(|result| result.is_err()); - if let Some(error) = error { - return Err(error.err().unwrap()); - } +// if let Some(error) = error { +// return Err(error.err().unwrap()); +// } - Ok(()) - } -} \ No newline at end of file +// Ok(()) +// } +// } \ No newline at end of file diff --git a/src/compiler/assembler.rs b/src/compiler/assembler.rs index dbeef2d..51c995a 100644 --- a/src/compiler/assembler.rs +++ b/src/compiler/assembler.rs @@ -6,7 +6,7 @@ use crate::resource::{Resource, ResourceError}; use self::{html_assembler::HtmlAssembler, assembler_configuration::AssemblerConfiguration}; -use super::{artifact::{Artifact, ArtifactError}, dossier::{Document, Dossier}, output_format::OutputFormat, parsing::parsing_error::ParsingError}; +use super::{artifact::{Artifact, ArtifactError}, bibliography::Bibliography, dossier::{Document, Dossier}, output_format::OutputFormat, parsing::parsing_error::ParsingError, table_of_contents::TableOfContents}; pub mod html_assembler; pub mod assembler_configuration; @@ -36,10 +36,9 @@ pub trait Assembler { fn set_configuration(&mut self, configuration: AssemblerConfiguration); - fn assemble_dossier(&self, /*codex: &Codex,*/ dossier: &Dossier) -> Result; + fn assemble_dossier(&self, dossier: &Dossier) -> Result; - /// Assemble document in only one `String` - fn assemble_document_into_string(&self, document: &Document) -> Result { + fn assemble_document(&self, document: &Document) -> Result { let mut result = String::new(); @@ -78,17 +77,12 @@ pub trait Assembler { } } - Ok(result) + Ok(Artifact::new(result)) } - fn assemble_document(&self, output_path: PathBuf, document: &Document) -> Result { - - let mut artifact = Artifact::new(output_path)?; - - artifact.content_mut().write(&self.assemble_document_into_string(document)?)?; - - Ok(artifact) + fn assemble_document_standalone(&self, page_title: &String, styles_references: Option<&Vec>, toc: Option<&TableOfContents>, bibliography: Option<&Bibliography>, document: &Document) -> Result { + self.assemble_document(document) } } diff --git a/src/compiler/assembler/assembler_configuration.rs b/src/compiler/assembler/assembler_configuration.rs index 936cc87..33c7916 100644 --- a/src/compiler/assembler/assembler_configuration.rs +++ b/src/compiler/assembler/assembler_configuration.rs @@ -1,15 +1,10 @@ -use std::{ffi::OsStr, path::PathBuf}; - use getset::{CopyGetters, Getters, Setters}; -use crate::compiler::{dossier::dossier_configuration::{self, DossierConfiguration}, theme::Theme}; +use crate::compiler::{dossier::dossier_configuration::DossierConfiguration, theme::Theme}; #[derive(Debug, Getters, CopyGetters, Setters)] pub struct AssemblerConfiguration { - #[getset(get = "pub", set = "pub")] - output_location: PathBuf, - #[getset(get = "pub", set = "pub")] theme: Theme, @@ -19,16 +14,18 @@ pub struct AssemblerConfiguration { #[getset(get_copy = "pub", set = "pub")] parallelization: bool, + #[getset(get = "pub", set = "pub")] + styles_raw_path: Vec, } impl AssemblerConfiguration { - pub fn new(output_location: PathBuf, theme: Theme, use_remote_addons: bool, parallelization: bool) -> Self { + pub fn new(theme: Theme, use_remote_addons: bool, parallelization: bool) -> Self { Self { - output_location, theme, use_remote_addons, - parallelization + parallelization, + styles_raw_path: Vec::new(), } } } @@ -36,10 +33,10 @@ impl AssemblerConfiguration { impl Default for AssemblerConfiguration { fn default() -> Self { Self { - output_location: Default::default(), theme: Theme::default(), use_remote_addons: false, - parallelization: false + parallelization: false, + styles_raw_path: Vec::new(), } } } diff --git a/src/compiler/assembler/html_assembler.rs b/src/compiler/assembler/html_assembler.rs index a7e4401..e89b86a 100644 --- a/src/compiler/assembler/html_assembler.rs +++ b/src/compiler/assembler/html_assembler.rs @@ -1,9 +1,9 @@ -use std::{path::PathBuf, str::FromStr, sync::{Arc, Mutex}}; +use std::str::FromStr; use build_html::{HtmlPage, HtmlContainer, Html, Container}; use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; -use crate::{compiler::{artifact::Artifact, dossier::{document::chapter::chapter_tag::ChapterTagKey, Document, Dossier}, parsable::parsed_content_accessor::ParsedContentAccessor, theme::Theme}, resource::{disk_resource, dynamic_resource::DynamicResource, remote_resource, Resource, ResourceError}, utility::file_utility}; +use crate::{compiler::{artifact::Artifact, bibliography::Bibliography, dossier::{document::chapter::chapter_tag::ChapterTagKey, Document, Dossier}, table_of_contents::TableOfContents, theme::Theme}, resource::{dynamic_resource::DynamicResource, Resource, ResourceError}}; use super::{Assembler, AssemblerError, assembler_configuration::AssemblerConfiguration}; @@ -19,7 +19,7 @@ impl HtmlAssembler { } - fn apply_remote_addons(&self, mut page: HtmlPage) -> HtmlPage { + fn apply_standard_remote_addons(&self, mut page: HtmlPage) -> HtmlPage { // add code block js/css match self.configuration.theme() { Theme::Light => { @@ -83,7 +83,7 @@ impl HtmlAssembler { page } - fn apply_local_addons(&self, mut page: HtmlPage) -> HtmlPage { + fn apply_standard_local_addons(&self, mut page: HtmlPage) -> HtmlPage { page.add_style(include_str!("html_assembler/emoji/emoji.min.css")); @@ -118,41 +118,9 @@ impl HtmlAssembler { page } -} - -impl Assembler for HtmlAssembler { - - fn set_configuration(&mut self, configuration: AssemblerConfiguration) { - self.configuration = configuration - } - - fn assemble_dossier(&self, dossier: &Dossier) -> Result { - - if dossier.documents().is_empty() { - return Err(AssemblerError::TooFewElements("there are no documents".to_string())) - } - - let dossier_name = file_utility::build_output_file_name(dossier.name(), Some("html")); - let mut output = self.configuration.output_location().clone(); - if output.is_dir() { - output = output.join(dossier_name); - } - - let mut artifact = Artifact::new(output)?; - - let mut page = HtmlPage::new() - .with_title(dossier.name()) - .with_meta(vec![("charset", "utf-8")]); + fn apply_theme_style(&self, mut page: HtmlPage) -> HtmlPage { - if self.configuration.use_remote_addons() { - page = self.apply_remote_addons(page); - - } else { - page = self.apply_local_addons(page); - } - - // apply embedded styles match self.configuration.theme() { Theme::Light => page.add_style(include_str!("html_assembler/default_style/light_theme.css")), Theme::Dark => page.add_style(include_str!("html_assembler/default_style/dark_theme.css")), @@ -161,9 +129,10 @@ impl Assembler for HtmlAssembler { Theme::None => todo!(), } - let styles_references = dossier.configuration().style().styles_references(); - log::info!("appending {} custom styles", styles_references.len()); + page + } + fn apply_styles(&self, mut page: HtmlPage, styles_references: &Vec) -> Result { for ref style_ref in styles_references { log::info!("appending style (reference): {:?}", style_ref); @@ -181,6 +150,50 @@ impl Assembler for HtmlAssembler { } } + Ok(page) + } + + fn create_default_html_page(&self, page_title: &String, styles_references: &Vec) -> Result { + + let mut page = HtmlPage::new() + .with_title(page_title) + .with_meta(vec![("charset", "utf-8")]); + + if self.configuration.use_remote_addons() { + page = self.apply_standard_remote_addons(page); + + } else { + page = self.apply_standard_local_addons(page); + } + + page = self.apply_theme_style(page); + + page = self.apply_styles(page, &styles_references)?; + + Ok(page) + } +} + +impl Assembler for HtmlAssembler { + + fn set_configuration(&mut self, configuration: AssemblerConfiguration) { + self.configuration = configuration + } + + fn assemble_dossier(&self, dossier: &Dossier) -> Result { + + if dossier.documents().is_empty() { + return Err(AssemblerError::TooFewElements("there are no documents".to_string())) + } + + let mut styles_references = dossier.configuration().style().styles_references(); + log::info!("appending {} custom styles", styles_references.len()); + + let mut other_styles = self.configuration.styles_raw_path().clone(); + styles_references.append(&mut other_styles); + + let mut page = self.create_default_html_page(dossier.name(), &styles_references)?; + if let Some(toc) = dossier.table_of_contents() { if let Some(parsed_toc) = toc.parsed_content() { page.add_raw(parsed_toc.parsed_content()); @@ -189,10 +202,10 @@ impl Assembler for HtmlAssembler { if self.configuration.parallelization() { - let mut assembled_documents: Vec> = Vec::new(); + let mut assembled_documents: Vec> = Vec::new(); dossier.documents().par_iter().map(|document| { - self.assemble_document_into_string(document) + self.assemble_document(document) }).collect_into_vec(&mut assembled_documents); for assembled_document in assembled_documents { @@ -212,7 +225,7 @@ impl Assembler for HtmlAssembler { .with_attributes(vec![ ("class", "document") ]) - .with_raw(self.assemble_document_into_string(document)?); + .with_raw(self.assemble_document(document)?); page.add_container(section); } @@ -224,12 +237,12 @@ impl Assembler for HtmlAssembler { } } - artifact.content_mut().write(&page.to_html_string())?; + let artifact = Artifact::new(page.to_html_string()); Ok(artifact) } - fn assemble_document_into_string(&self, document: &Document) -> Result { + fn assemble_document(&self, document: &Document) -> Result { let mut result = String::new(); for paragraph in document.preamble() { @@ -297,7 +310,27 @@ impl Assembler for HtmlAssembler { result.push_str(div_chapter.with_raw(div_chapter_content).to_html_string().as_str()); } - Ok(result) + Ok(Artifact::new(result)) + } + + fn assemble_document_standalone(&self, page_title: &String, styles_references: Option<&Vec>, toc: Option<&TableOfContents>, bibliography: Option<&Bibliography>, document: &Document) -> Result { + let mut page = self.create_default_html_page(page_title, styles_references.unwrap_or(&Vec::new()))?; + + if let Some(toc) = toc { + if let Some(parsed_toc) = toc.parsed_content() { + page.add_raw(parsed_toc.parsed_content()); + } + } + + page.add_raw(Into::::into(self.assemble_document(document)?)); + + if let Some(bib) = bibliography { + if let Some(parsed_bib) = bib.parsed_content() { + page.add_raw(parsed_bib.parsed_content()); + } + } + + Ok(Artifact::new(page.to_html_string())) } fn configuration(&self) -> &AssemblerConfiguration { diff --git a/src/compiler/compilation_configuration.rs b/src/compiler/compilation_configuration.rs index 7b307bc..64e5afb 100644 --- a/src/compiler/compilation_configuration.rs +++ b/src/compiler/compilation_configuration.rs @@ -21,6 +21,9 @@ pub struct CompilationConfiguration { #[getset(get = "pub", set = "pub")] output_location: PathBuf, + #[getset(get_copy = "pub", set = "pub")] + force_output: bool, + #[getset(get_copy = "pub", set = "pub")] fast_draft: bool, @@ -53,6 +56,9 @@ pub struct CompilationConfiguration { #[getset(get = "pub", set = "pub")] theme: Option, + + #[getset(get = "pub", set = "pub")] + styles_raw_path: Vec, } impl CompilationConfiguration { @@ -161,6 +167,7 @@ impl Default for CompilationConfiguration { input_location: PathBuf::from("."), output_location: PathBuf::from("."), fast_draft: false, + force_output: false, embed_local_image: None, embed_remote_image: None, compress_embed_image: None, @@ -171,6 +178,7 @@ impl Default for CompilationConfiguration { documents_subset_to_compile: None, bibliography: None, theme: None, + styles_raw_path: Vec::new() } } } \ No newline at end of file diff --git a/src/compiler/dumpable.rs b/src/compiler/dumpable.rs index 106c5de..d1b1781 100644 --- a/src/compiler/dumpable.rs +++ b/src/compiler/dumpable.rs @@ -1,3 +1,6 @@ +use std::path::PathBuf; + +use getset::{CopyGetters, Getters, Setters}; use thiserror::Error; use crate::resource::ResourceError; @@ -10,8 +13,28 @@ pub enum DumpError { } -/// Dump trait. Dump is the operation which permits to save save a resource +#[derive(Debug, Clone, Getters, CopyGetters, Setters)] +pub struct DumpConfiguration { + + #[getset(get = "pub", set = "pub")] + output_path: PathBuf, + + #[getset(get_copy = "pub", set = "pub")] + force_dump: bool, +} + +impl DumpConfiguration { + pub fn new(output_path: PathBuf, force_dump: bool,) -> Self { + Self { + output_path, + force_dump + } + } +} + + +/// Dump trait. Dump is the operation which permits to save a resource pub trait Dumpable { - fn dump(&mut self) -> Result<(), DumpError>; + fn dump(&mut self, configuration: &DumpConfiguration) -> Result<(), DumpError>; } \ No newline at end of file diff --git a/src/compiler/output_format.rs b/src/compiler/output_format.rs index 046fe3e..1ba4e28 100644 --- a/src/compiler/output_format.rs +++ b/src/compiler/output_format.rs @@ -9,12 +9,20 @@ pub enum OutputFormatError { } /// Set of supported formats -#[derive(PartialEq, Debug, Default)] +#[derive(PartialEq, Debug, Default, Clone)] pub enum OutputFormat { #[default] Html } +impl OutputFormat { + pub fn get_extension(&self) -> String { + match self { + OutputFormat::Html => String::from("html"), + } + } +} + impl FromStr for OutputFormat { type Err = OutputFormatError; @@ -28,14 +36,6 @@ impl FromStr for OutputFormat { } } -impl Clone for OutputFormat { - fn clone(&self) -> Self { - match self { - Self::Html => Self::Html, - } - } -} - #[cfg(test)] mod tests { diff --git a/src/compiler/parsing/parsing_rule/html_extended_block_quote_rule.rs b/src/compiler/parsing/parsing_rule/html_extended_block_quote_rule.rs index 2cac16c..e45ecd9 100644 --- a/src/compiler/parsing/parsing_rule/html_extended_block_quote_rule.rs +++ b/src/compiler/parsing/parsing_rule/html_extended_block_quote_rule.rs @@ -67,14 +67,15 @@ impl ParsingRule for HtmlExtendedBlockQuoteRule { tag_body.push_str(c.as_str()); } - tag_body = DOUBLE_NEW_LINE_REGEX.replace_all(&tag_body, "
").to_string(); + let tag_body = text_utility::replace(&tag_body, &ESCAPE_HTML); + let tag_body = DOUBLE_NEW_LINE_REGEX.replace_all(&tag_body, "
").to_string(); let outcome = ParsingOutcome::new(vec![ ParsingOutcomePart::Fixed { content: format!(r#"
"#, quote_type, quote_type, quote_type) }, - ParsingOutcomePart::Mutable { content: text_utility::replace(&tag_body, &ESCAPE_HTML) }, + ParsingOutcomePart::Mutable { content: tag_body }, ParsingOutcomePart::Fixed { content: String::from("
") } ]); diff --git a/src/compiler/parsing/parsing_rule/html_image_rule.rs b/src/compiler/parsing/parsing_rule/html_image_rule.rs index 073a06f..8a70a01 100644 --- a/src/compiler/parsing/parsing_rule/html_image_rule.rs +++ b/src/compiler/parsing/parsing_rule/html_image_rule.rs @@ -121,7 +121,7 @@ impl HtmlImageRule { } fn build_embed_remote_img(image: &mut ImageResource, id: Option, img_classes: Vec<&str>, figure_style: Option, parsing_configuration: &RwLockReadGuard) -> Result { - todo!() + unimplemented!("embed remote image will be added in a next version") } fn build_embed_local_img(image: &mut ImageResource, id: Option, img_classes: Vec<&str>, figure_style: Option, parsing_configuration: &RwLockReadGuard) -> Result { diff --git a/src/resource/disk_resource.rs b/src/resource/disk_resource.rs index 06fd0f8..df3600b 100644 --- a/src/resource/disk_resource.rs +++ b/src/resource/disk_resource.rs @@ -12,6 +12,21 @@ pub struct DiskResource { location: PathBuf } +impl DiskResource { + pub fn new(location: PathBuf) -> Result { + + Self::try_from(location) + } + + pub fn create_parents_dir(&self) -> Result<(), ResourceError> { + + let prefix = self.location.parent().unwrap(); + std::fs::create_dir_all(prefix)?; + + Ok(()) + } +} + impl FromStr for DiskResource { type Err = ResourceError; @@ -51,15 +66,6 @@ impl TryFrom for DiskResource { } } - -#[allow(dead_code)] -impl DiskResource { - fn new(location: PathBuf) -> Result { - - Self::try_from(location) - } -} - impl Resource for DiskResource { type LocationType = PathBuf; diff --git a/test-resources/nmd-test-dossier-1/assets/styles/custom.css b/test-resources/nmd-test-dossier-1/assets/styles/custom.css index e69de29..ba4e657 100644 --- a/test-resources/nmd-test-dossier-1/assets/styles/custom.css +++ b/test-resources/nmd-test-dossier-1/assets/styles/custom.css @@ -0,0 +1,3 @@ +* { + background-color: red; +}