Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Umi restructure #123

Merged
merged 14 commits into from
Jan 29, 2024
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()) },
SeppFS marked this conversation as resolved.
Show resolved Hide resolved
_ => 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
Loading