Skip to content

Commit

Permalink
Merge pull request #123 from unimarkup/umi_restructure
Browse files Browse the repository at this point in the history
Restructure Intermediate Representation .umi
  • Loading branch information
SeppFS authored Jan 29, 2024
2 parents 53ec8c6 + 6098791 commit 7a46853
Show file tree
Hide file tree
Showing 7 changed files with 427 additions and 161 deletions.
18 changes: 11 additions & 7 deletions cli/src/compiler.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Entry module for unimarkup-rs.
use std::{
ffi::OsStr,
fs,
path::{Path, PathBuf},
};
Expand All @@ -23,13 +24,16 @@ use crate::log_id::{GeneralError, GeneralInfo};
///
/// Returns a [`GeneralError`] if error occurs during compilation.
pub fn compile(config: Config) -> Result<(), GeneralError> {
let source = fs::read_to_string(&config.input).map_err(|error| {
pipe!(
GeneralError::FileRead,
format!("Could not read file: '{:?}'", &config.input),
add: AddonKind::Info(format!("Cause: {}", error))
)
})?;
let source: String = match config.input.extension().and_then(OsStr::to_str) {
Some("umi") => unsafe { String::from_utf8_unchecked(fs::read(&config.input).unwrap()) },
_ => fs::read_to_string(&config.input).map_err(|error| {
pipe!(
GeneralError::FileRead,
format!("Could not read file: '{:?}'", &config.input),
add: AddonKind::Info(format!("Cause: {}", error))
)
})?,
};

let out_path = {
if let Some(ref out_file) = config.output.file {
Expand Down
124 changes: 117 additions & 7 deletions cli/tests/umi.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,125 @@
use std::iter::zip;
use std::path::PathBuf;

use unimarkup_core::{commons::config::Config, Unimarkup};
use unimarkup_core::{
commons::config::Config,
inline::element::{Inline, InlineElement},
parser::{document::Document, elements::blocks::Block},
render::umi::Umi,
Unimarkup,
};

fn compile_um(config: Config) -> Option<Unimarkup> {
let source = std::fs::read_to_string(&config.input).ok()?;

Some(Unimarkup::parse(&source, config))
}

fn equals_inlines_output(input: &Vec<Inline>, output: &Vec<Inline>) -> bool {
assert_eq!(
input.len(),
output.len(),
"Parsed Inlines does not have the same number of elements"
);

for (in_elem, out_elem) in zip(input.iter(), output.iter()) {
assert_eq!(
in_elem.as_unimarkup(),
out_elem.as_unimarkup(),
"Inline contains wrong content"
)
}
true
}

fn equals_blocks_output(input: &Vec<Block>, output: &Vec<Block>) -> bool {
assert_eq!(
input.len(),
output.len(),
"Parsed Blocks does not have the same length as the input"
);

for (in_elem, out_elem) in zip(input.iter(), output.iter()) {
assert_eq!(
in_elem.variant_str(),
out_elem.variant_str(),
"Blocks did not match up at Index"
);
let block_in = in_elem.clone();
let block_out = out_elem.clone();
match (block_in, block_out) {
(Block::Heading(block_in), Block::Heading(block_out)) => {
assert_eq!(block_in.id, block_out.id, "Heading ids do not match!");
assert_eq!(
block_in.level, block_out.level,
"Heading Levels do not match!"
);
assert!(equals_inlines_output(&block_in.content, &block_out.content));
assert_eq!(
block_in.attributes, block_out.attributes,
"Heading Attributes do not match!"
);
}
(Block::Paragraph(block_in), Block::Paragraph(block_out)) => {
assert!(equals_inlines_output(&block_in.content, &block_out.content));
}
(Block::VerbatimBlock(block_in), Block::VerbatimBlock(block_out)) => {
assert_eq!(
block_in.content, block_out.content,
"Verbatim Content does not match"
);
assert_eq!(
block_in.data_lang, block_out.data_lang,
"Verbatim Data_Lang does not match"
);
assert_eq!(
block_in.attributes, block_out.attributes,
"Verbatim Attributes do not match"
);
assert_eq!(
block_in.implicit_closed, block_out.implicit_closed,
"Verbatim Implicit_Closed does not match"
);
assert_eq!(
block_in.tick_len, block_out.tick_len,
"Verbatim Tick-Len does not match"
);
}
(Block::BulletList(block_in), Block::BulletList(block_out)) => {
assert_eq!(
block_in.entries.len(),
block_out.entries.len(),
"Bullet List entry count does not match"
);

for (in_entry, out_entry) in zip(block_in.entries.iter(), block_out.entries.iter())
{
assert_eq!(
in_entry.keyword, out_entry.keyword,
"Bullet List Entry Keyword does not match"
);
assert!(equals_inlines_output(&in_entry.heading, &out_entry.heading));
assert!(equals_blocks_output(&in_entry.body, &out_entry.body));
}
}
_ => return false,
}
}

true
}

fn equals_umi_output(input: &Document, output: &Document) -> bool {
assert_eq!(
input.config, output.config,
"Parsed UMI Config differs from original Config"
);

equals_blocks_output(&input.blocks, &output.blocks)
}

#[test]
fn umi_loop() {
fn umi_supported() {
let mut config = Config::default();
let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.canonicalize()
Expand All @@ -21,11 +131,11 @@ fn umi_loop() {
let mut umi = um.render_umi().unwrap();
let workbook = umi.create_workbook();

let looped_doc = workbook.create_um();
let looped_doc = &Umi::create_um(workbook.to_string().as_str(), &mut workbook.config).unwrap();
let input = um.get_document();

assert_eq!(
looped_doc.blocks.len(),
um.get_document().blocks.len(),
"Parsed UMI file differs from original UM."
assert!(
equals_umi_output(input, looped_doc),
"Output does not equal the Input"
);
}
14 changes: 14 additions & 0 deletions commons/src/config/preamble.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ pub struct I18n {

#[arg(long, value_parser = parse_to_hashset::<Locale>, required = false, default_value = "")]
#[serde(with = "locale::serde::multiple", default)]
#[serde(skip_serializing_if = "HashSet::is_empty")]
pub output_langs: HashSet<Locale>,
}

Expand All @@ -72,9 +73,11 @@ impl ConfigFns for I18n {
#[derive(Args, Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RenderConfig {
#[arg(long = "ignore-file", value_parser = parse_ignore_file, required = false, default_value = "")]
#[serde(skip_serializing_if = "HashSet::is_empty")]
#[serde(default)]
pub ignore: HashSet<String>,
#[arg(long, value_parser = parse_parameter, required = false, default_value = "")]
#[serde(skip_serializing_if = "HashMap::is_empty")]
#[serde(default)]
pub parameter: HashMap<String, String>,
#[arg(long)]
Expand Down Expand Up @@ -103,8 +106,11 @@ impl ConfigFns for RenderConfig {
#[derive(Args, Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Citedata {
#[arg(long = "citation-style")]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub style: Option<PathBuf>,
#[arg(long, value_parser = parse_to_hashset::<PathBuf>, required = false, default_value = "")]
#[serde(skip_serializing_if = "HashSet::is_empty")]
#[serde(default)]
pub references: HashSet<PathBuf>,
}
Expand Down Expand Up @@ -141,15 +147,23 @@ impl ConfigFns for Citedata {
#[derive(Args, Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Metadata {
#[arg(long)]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub title: Option<String>,
#[arg(long, value_parser = parse_to_hashset::<String>, required = false, default_value = "")]
#[serde(skip_serializing_if = "HashSet::is_empty")]
#[serde(default)]
pub authors: HashSet<String>,
#[arg(long)]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub description: Option<String>,
#[arg(long)]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub base: Option<PathBuf>,
#[arg(long, value_parser = parse_to_hashset::<PathBuf>, required = false, default_value = "")]
#[serde(skip_serializing_if = "HashSet::is_empty")]
#[serde(default)]
pub fonts: HashSet<PathBuf>,
}
Expand Down
11 changes: 9 additions & 2 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::ffi::OsStr;

pub use unimarkup_commons as commons;
pub use unimarkup_inline as inline;
pub use unimarkup_parser as parser;
Expand Down Expand Up @@ -25,8 +27,13 @@ impl Unimarkup {
/// * `um_content` - String containing Unimarkup elements.
/// * `config` - Unimarkup configuration to be used on top of preambles.
pub fn parse(um_content: &str, mut config: Config) -> Self {
Unimarkup {
doc: parser::parse_unimarkup(um_content, &mut config),
match config.input.extension().and_then(OsStr::to_str) {
Some("umi") => Unimarkup {
doc: Umi::create_um(um_content, &mut config).unwrap(),
},
_ => Unimarkup {
doc: parser::parse_unimarkup(um_content, &mut config),
},
}
}

Expand Down
18 changes: 18 additions & 0 deletions render/src/log_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,21 @@ pub enum RenderError {
#[error("Output format `append()` failed. See log: '{}: {}'", .0.event_id, .0.entry_id)]
BadAppend(FinalizedEvent<LogId>),
}

#[derive(Debug, Clone, ErrLogId, Error, PartialEq, Eq)]
pub enum UmiParserError {
#[error("The UMI Parser failed to parse the Element Kind at Position {}.", .0)]
UnknownKind(u8),

#[error("The UMI Parser failed to parse Property {} from Element at Position {}.", (.0).0, (.0).1)]
MissingProperty((String, u8)),

#[error("The UMI Parser failed to parse Property Value {} from Element at Position {}.", (.0).0, (.0).1)]
InvalidPropertyValue((String, u8)),

#[error("The UMI Parser failed to parse the corresponding Document.")]
NoUnimarkupDetected,

#[error("The UMI Parser failed to parse the Element at Position {}, because it has not Parent Element wwith Depth 0.", .0)]
MissingParentElement(u8),
}
Loading

0 comments on commit 7a46853

Please sign in to comment.