summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Cargo.lock25
-rw-r--r--Cargo.toml18
-rw-r--r--build/about.html23
-rw-r--r--build/articles/testart.html23
-rw-r--r--build/articles/unbricking_idrac.html104
-rw-r--r--build/test.html22
-rw-r--r--src/lib.rs2
-rw-r--r--src/macro_processor/macro_processor.rs260
-rw-r--r--src/macro_processor/main.rs12
-rw-r--r--src/macro_processor/mod.rs2
-rw-r--r--src/skaldpress/main.rs153
-rw-r--r--src/skaldpress/mod.rs0
-rw-r--r--tests/example_include.smp1
-rw-r--r--tests/macro_processor.rs244
15 files changed, 892 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..123b206
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+target/
+templates/
+content/
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..7d54bd4
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,25 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "markdown"
+version = "1.0.0-alpha.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "911a8325e6fb87b89890cd4529a2ab34c2669c026279e61c26b7140a3d821ccb"
+dependencies = [
+ "unicode-id",
+]
+
+[[package]]
+name = "skaldpress"
+version = "0.1.0"
+dependencies = [
+ "markdown",
+]
+
+[[package]]
+name = "unicode-id"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1b6def86329695390197b82c1e244a54a131ceb66c996f2088a3876e2ae083f"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..ebaf72c
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "skaldpress"
+version = "0.1.0"
+edition = "2021"
+default-run = "skaldpress"
+
+[dependencies]
+markdown = "1.0.0-alpha.20"
+
+[[bin]]
+name = "skaldpress"
+path = "src/skaldpress/main.rs"
+
+# Skaldpress Macro Processor
+# Skaldpress Macro Processor (SMP) is a powerful tool in your arsenal, capable of handling all your text processing and macro needs with precision and strength.
+[[bin]]
+name = "smp"
+path = "src/macro_processor/main.rs"
diff --git a/build/about.html b/build/about.html
new file mode 100644
index 0000000..e28dae0
--- /dev/null
+++ b/build/about.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Quirio</title>
+ </head>
+ <body>
+ <article aria-label="Article">
+ <div class="article-meta">
+ <span>Published at
+ <time datetime="01.09.2024">
+ 01.09.2024
+ </time>
+ </span>
+ </div>
+
+ <h1>About</h1>
+<p>this is some about <em>text</em>.</p>
+
+ </article>
+ </body>
+</html>
+
+
diff --git a/build/articles/testart.html b/build/articles/testart.html
new file mode 100644
index 0000000..3a7854e
--- /dev/null
+++ b/build/articles/testart.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Quirio</title>
+ </head>
+ <body>
+ <article aria-label="Article">
+ <div class="article-meta">
+ <span>Published at
+ <time datetime="01.09.2024">
+ 01.09.2024
+ </time>
+ </span>
+ </div>
+
+ <h1>Test article</h1>
+<p>this is some about <em>text</em>.</p>
+
+ </article>
+ </body>
+</html>
+
+
diff --git a/build/articles/unbricking_idrac.html b/build/articles/unbricking_idrac.html
new file mode 100644
index 0000000..dd404bb
--- /dev/null
+++ b/build/articles/unbricking_idrac.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Quirio</title>
+ </head>
+ <body>
+ <article aria-label="Article">
+ <div class="article-meta">
+ <span>Published at
+ <time datetime="04.04.2024">
+ 04.04.2024
+ </time>
+ </span>
+ </div>
+
+ <p>So, the iDRAC6 on my Dell R710 got bricked when attempting to update the lifecycle controller to 1.6.0.73.
+The front lcd was dead, and also the rear service led was unlit.
+I attempted to drain the power, that made no difference.
+I attempted to load &lt;code&gt;firmimg.d6&lt;/code&gt; onto a FAT formatted SD card and put it int the vFlash.
+But no reaction whatsoever.
+It looks to me like everyone (&quot;on the internet&quot;) is saying this means a new motherboard,
+but I was able to reflash the chip using the BIOS's serial console.</p>
+<p>First I needed to get into the BIOS settings, to change some settings there:
+When the iDRAC is in this state, the BIOS will complain that it cannot contact the RAC,
+and this will make it attempt a reboot. If you wait and let it reboot,
+the second time it figures out it cannot contact the RAC it will let you press &lt;code&gt;f1&lt;/code&gt;
+to continue boot &lt;code&gt;f2&lt;/code&gt; to open BIOS settings.
+If you disconnect the boot drive and press &lt;code&gt;f1&lt;/code&gt;
+it will attempt to boot using PXE, when that fails.
+You will get the same prompt,
+with the additional option of pressing &lt;code&gt;f11&lt;/code&gt; to choose the boot device.</p>
+&lt;section id=&quot;bios-settings&quot;&gt;
+&lt;h2&gt;&lt;a href=&quot;#bios-settings&quot;&gt;#&lt;/a&gt; BIOS Settings&lt;/h2&gt;
+<p>In the “Serial Communication” menu, make sure everything is set like this:</p>
+<pre><code>Serial Communication …………………… On with Console Redirection via COM2
+Serial Port Address ……………………… Serial Device1=COM1,Serial Device2=COM2
+External Serial Connector ……… Serial Device 1
+Failsafe Baud Rate ………………………… 115200
+Remote Terminal Type …………………… VT100/VTZZ0
+Redirection After Boot ……………… Enabled&lt;/code&gt;&lt;/pre&gt;
+</code></pre>
+&lt;/section&gt;
+&lt;section id=&quot;boot-an-os&quot;&gt;
+&lt;h2&gt;&lt;a href=&quot;#boot-an-os&quot;&gt;#&lt;/a&gt; Boot an OS&lt;/h2&gt;
+<p>Now you should boot an operating system, I booted Linux MX from a USB stick.
+But you will probably be fine with the OS you have installed as well.</p>
+<p>When you have an OS running, you will need some kind of serial console
+(could use just screen and ttys or something) I used Putty.</p>
+&lt;/section&gt;
+&lt;section id=&quot;setup-your-serial-console&quot;&gt;
+&lt;h2&gt;&lt;a href=&quot;#setup-your-serial-console&quot;&gt;#&lt;/a&gt; Setup your serial console&lt;/h2&gt;
+<p>Serial port has to be configured like this:</p>
+<pre><code>Port/address/device: /dev/ttyS1 (For windows it should be COM2)
+Baud rate (speed in Putty): 115200
+Data bits: 8
+Pairity: None
+Stop Bits: 1
+Flow Control: Hardware
+</code></pre>
+<p>Your terminal should be configured like this:</p>
+<pre><code>Function, arrow and ctrl keys should be set to Terminal Keys or VT100+
+Backspace key sends should be set as Ctrl+H
+Emulation should be VT100/220 or auto, NOT ANSI.
+</code></pre>
+&lt;/section&gt;
+&lt;section id=&quot;setup-a-tftp-server&quot;&gt;
+&lt;h2&gt;&lt;a href=&quot;#setup-a-tftp-server&quot;&gt;#&lt;/a&gt; Setup a TFTP server&lt;/h2&gt;
+<p>Now you need to put the <code>firming.d6</code> on a tftp server.
+I just downloaded the newest firmware from downloads.dell.com
+(in my case, the iDRAC was running the latest version last time it worked) and extracted the firmimg.d6
+from the EXE file.
+I started a TFTP server on my laptop on the same network as the dedicated iDRAC Enterprise NIC port.
+and put the firmware image on the root there.
+You could in theory also start a TFTP server on the OS you are running on the server.</p>
+&lt;/section&gt;
+&lt;section id=&quot;flash-the-firmware&quot;&gt;
+&lt;h2&gt;&lt;a href=&quot;#flash-the-firmware&quot;&gt;#&lt;/a&gt; Flash the firmware&lt;/h2&gt;
+<p>At this point you should connect to the serial console using the terminal/console you set up earlier.
+Now, if you press Enter in the console, you should get a Firmware recovery menu.</p>
+<ul>
+<li>Setup the iDRAC Enterprise IP (so it can connect to the TFTP server).
+If there is a DHCP server on that network, press the number of the action mentioning DHCP (9),
+and this should happen automatically.</li>
+<li>Select the TFTP server by pressing 7.
+If you have the firmimg.d6 on the root of the TFTP server,
+you don't have to configure anything else.</li>
+<li>Hit the option that mentions Firmware Upgrade.
+This will download the firmware image and flash it.
+This will take a few minutes, and then the RAC (NOT THE OS YOU ARE RUNNING) will reboot,
+and you should hear the fans change speed and the LCD should turn on.</li>
+<li>When the RAC is updated with this new firmware, the serial console should become unresponsive.</li>
+<li>When everything seems to be done “doing things”,
+and you have waited a few minutes extra just to be sure.
+You can now reboot the server, at this point you should see the “System services” again.</li>
+<li>Time to reconfigure the iDRAC and the BIOS again, as everything probably has been wiped :)</li>
+</ul>
+<p>Good luck</p>
+&lt;/section&gt;
+
+ </article>
+ </body>
+</html>
+
+
diff --git a/build/test.html b/build/test.html
new file mode 100644
index 0000000..fdc3647
--- /dev/null
+++ b/build/test.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Quirio</title>
+ </head>
+ <body>
+ <article aria-label="Article">
+ <div class="article-meta">
+ <span>Published at
+ <time datetime="METADATA_publish_date">
+ METADATA_publish_date
+ </time>
+ </span>
+ </div>
+
+ <p>TEST</p>
+
+ </article>
+ </body>
+</html>
+
+
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..d81aca7
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,2 @@
+pub mod skaldpress;
+pub mod macro_processor;
diff --git a/src/macro_processor/macro_processor.rs b/src/macro_processor/macro_processor.rs
new file mode 100644
index 0000000..306aa04
--- /dev/null
+++ b/src/macro_processor/macro_processor.rs
@@ -0,0 +1,260 @@
+use std::collections::HashMap;
+use std::fs;
+
+// print only with debug_assertions
+macro_rules! dprint {
+ ($($x:tt)*) => {
+ #[cfg(debug_assertions)]
+ print!($($x)*)
+ }
+}
+
+// // println only with debug_assertions
+// macro_rules! dprintln {
+// ($($x:tt)*) => {
+// #[cfg(debug_assertions)]
+// println!($($x)*)
+// }
+// }
+
+// Point to one or more ranges of a string, useful for highlighting parts of string
+macro_rules! highlight_debug {
+ ($hi_col:expr, $str:expr $(, ($pos:tt -> $endpos:tt))*) => {
+ for (i, _c) in $str.char_indices() {
+ if false $(|| (i >= $pos) && (i < $endpos))* {
+ dprint!("{}{}\x1b[0m", $hi_col, _c);
+ } else {
+ dprint!("{}", _c);
+ }
+ }
+ dprint!("\n");
+ };
+ ($str:expr, $pos:expr, $endpos:expr) => {
+ highlight_debug!("\x1b[7m", $str, ($pos -> $endpos))
+ };
+ ($str:expr, $pos:expr) => {
+ highlight_debug!($str, $pos, $pos+1)
+ };
+}
+
+#[derive(Debug)]
+pub struct MacroProcessor {
+ macros: HashMap<String, String>,
+}
+
+impl MacroProcessor {
+ pub fn new() -> Self {
+ Self {
+ macros: HashMap::new(),
+ }
+ }
+
+ pub fn define_macro(&mut self, name: String, body: String) {
+ self.macros.insert(name, body);
+ }
+
+ /// This expands a macro definition, and it executes builtin functions, like define
+ /// Currently, you cannot overwrite builtin's.
+ /// This is partly by design, and I don't currently see why I would want that.
+ /// In the future, this may change
+ ///
+ /// (The HashMap might become a HashMap<String, Box<dyn FnMut>> or something similar.
+ /// Then, all builtins would also be functions, and "normal" macros, would simply
+ /// be closures returning the string)
+ fn expand_macro(&mut self, macro_name: &str, args: &mut [String]) -> String {
+ if macro_name == "define" {
+ if args.len() < 1 {
+ println!("Missing argument(s) to `define`, found {} but expected 1 or 2", args.len());
+ return String::new();
+ }
+ let arg0 = self.process_input(&args[0]);
+ if args.len() > 1 {
+ let arg1 = self.process_input(&args[1]);
+ self.define_macro(arg0, arg1);
+ } else {
+ self.define_macro(arg0, String::new());
+ }
+ return String::new();
+ }
+
+ if macro_name == "ifdef" {
+ if args.len() < 2 {
+ println!("Missing argument(s) to `ifdef`, found {} but expected 2 or 3", args.len());
+ return String::new();
+ }
+ // We need to expand the first argument here as well, but we need to make the parser
+ // support literal, and phrase strings
+ if self.macros.contains_key(&args[0]) {
+ return self.process_input(&args[1]);
+ }
+ if args.len() > 2 {
+ return self.process_input(&args[2]);
+ }
+ return String::new();
+ }
+
+ if macro_name == "ifndef" {
+ if args.len() < 2 {
+ println!("Missing argument(s) to `ifndef`, found {} but expected 2 or 3", args.len());
+ return String::new();
+ }
+ // We need to expand the first argument here as well, but we need to make the parser
+ // support literal, and phrase strings
+ if !self.macros.contains_key(&args[0]) {
+ return self.process_input(&args[1]);
+ }
+ if args.len() > 2 {
+ return self.process_input(&args[2]);
+ }
+ return String::new();
+ }
+
+ if macro_name == "ifeq" {
+ if args.len() < 3 {
+ println!("Missing argument(s) to `ifeq`, found {} but expected 3 or 4", args.len());
+ return String::new();
+ }
+ let arg0 = self.process_input(&args[0]);
+ let arg1 = self.process_input(&args[1]);
+ if arg0 == arg1 {
+ return self.process_input(&args[2]);
+ }
+ if args.len() > 3 {
+ return self.process_input(&args[3]);
+ }
+ return String::new();
+ }
+
+ if macro_name == "ifneq" {
+ if args.len() < 3 {
+ println!("Missing argument(s) to `ifneq`, found {} but expected 3 or 4", args.len());
+ return String::new();
+ }
+ let arg0 = self.process_input(&args[0]);
+ let arg1 = self.process_input(&args[1]);
+ if arg0 != arg1 {
+ return self.process_input(&args[2]);
+ }
+ if args.len() > 3 {
+ return self.process_input(&args[3]);
+ }
+ return String::new();
+ }
+
+ if macro_name == "include" {
+ if args.len() < 1 {
+ println!("Missing argument(s) to `include`, found {} but expected 1", args.len());
+ return String::new();
+ }
+ let arg0 = self.process_input(&args[0]);
+ let input_file = fs::read_to_string(&arg0).expect("Failed to read input file");
+ return self.process_input(&input_file);
+ }
+
+ //Expand macro name? somwhat unsure on how to do this safely
+ //let macro_name = self.process_input(macro_name);
+
+ let Some(macro_body) = self.macros.get(macro_name) else {
+ return format!("{}", macro_name);
+ };
+
+ let mut expanded = macro_body.clone();
+ for (i, arg) in args.iter().enumerate() {
+ let placeholder = format!("${}", i + 1);
+ expanded = expanded.replace(&placeholder, arg);
+ }
+ expanded
+ }
+
+ pub fn process_input(&mut self, input: &str) -> String {
+ let mut output = String::new();
+ let mut state = ParserState::Normal;
+ let mut state_previous = ParserState::Normal;
+ let mut macro_name = String::new();
+ let mut macro_args = Vec::new();
+ let mut argument = String::new();
+ let mut macro_name_start = 0;
+ let mut skip_next_line_ending = false;
+
+ let mut in_quote_single = false;
+ let mut in_quote_double = false;
+
+ for (i, c) in input.char_indices() {
+ highlight_debug!(input, macro_name_start, i);
+
+ match state {
+ ParserState::Normal => {
+ macro_name_start = i;
+
+ if skip_next_line_ending && (c == '\n') {
+ skip_next_line_ending = false;
+ continue;
+ }
+
+ if c.is_alphanumeric() {
+ state = ParserState::InMacro;
+ state_previous = ParserState::Normal;
+ macro_name.push(c);
+ } else {
+ output.push(c);
+ }
+ }
+ ParserState::InMacro => {
+ if c.is_alphanumeric() || c == '_' {
+ macro_name.push(c);
+ } else if c == '(' {
+ state = ParserState::InMacroArgs;
+ state_previous = ParserState::InMacro;
+ } else {
+ if self.macros.contains_key(&macro_name) {
+ highlight_debug!("\x1b[32m\x1b[7m", input, (macro_name_start -> i));
+ }
+ if macro_name == "DNL" {
+ skip_next_line_ending = c != '\n';
+ } else {
+ let expanded = self.expand_macro(&macro_name, &mut []);
+ output.push_str(&expanded);
+ output.push(c);
+ }
+ macro_name.clear();
+ state = ParserState::Normal;
+ state_previous = ParserState::InMacro;
+ }
+ }
+ ParserState::InMacroArgs => {
+ if c == ')' {
+ highlight_debug!("\x1b[32m\x1b[7m", input, (macro_name_start -> i));
+
+ macro_args.push(argument.trim().to_string());
+ let expanded = self.expand_macro(&macro_name, &mut macro_args);
+ output.push_str(&expanded);
+ state = ParserState::Normal;
+ state_previous = ParserState::InMacroArgs;
+ macro_name.clear();
+ macro_args.clear();
+ argument.clear();
+ } else if c == ',' {
+ macro_args.push(argument.trim().to_string());
+ argument.clear();
+ } else {
+ argument.push(c);
+ }
+ }
+ }
+ }
+
+ // Handle cases where the text ends with a macro without arguments
+ if !macro_name.is_empty() {
+ output.push_str(&self.expand_macro(&macro_name, &mut []));
+ }
+
+ output
+ }
+}
+
+#[derive(Debug, PartialEq)]
+enum ParserState {
+ Normal,
+ InMacro,
+ InMacroArgs,
+}
diff --git a/src/macro_processor/main.rs b/src/macro_processor/main.rs
new file mode 100644
index 0000000..5575aa6
--- /dev/null
+++ b/src/macro_processor/main.rs
@@ -0,0 +1,12 @@
+use std::env;
+use std::fs;
+use skaldpress::macro_processor::MacroProcessor;
+
+fn main() {
+ let args: Vec<String> = env::args().collect();
+ let input_file = fs::read_to_string(&args[1]).expect("Failed to read input file");
+
+ let mut macro_processor = MacroProcessor::new();
+ let final_output = macro_processor.process_input(&input_file);
+ println!("{}", final_output);
+}
diff --git a/src/macro_processor/mod.rs b/src/macro_processor/mod.rs
new file mode 100644
index 0000000..2fccbda
--- /dev/null
+++ b/src/macro_processor/mod.rs
@@ -0,0 +1,2 @@
+pub mod macro_processor;
+pub use macro_processor::MacroProcessor;
diff --git a/src/skaldpress/main.rs b/src/skaldpress/main.rs
new file mode 100644
index 0000000..de6088f
--- /dev/null
+++ b/src/skaldpress/main.rs
@@ -0,0 +1,153 @@
+use std::fmt;
+use std::fs;
+use std::path::Path;
+
+use skaldpress::macro_processor::MacroProcessor;
+use std::collections::HashMap;
+
+const TEMPLATES_DIR: &str = "templates/";
+const CONTENT_DIR: &str = "content/";
+const BUILD_DIR: &str = "build/";
+
+#[derive(Debug)]
+enum YamlValue {
+ Scalar(String),
+ List(Vec<String>),
+}
+
+impl fmt::Display for YamlValue {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ YamlValue::Scalar(x) => write!(f, "{}", x),
+ YamlValue::List(_l) => write!(f, "List<String>"),
+ }
+ }
+}
+
+/// Currently, all files MUST have a metadata block, or this will fail completely
+fn extract_parse_yaml_metadata<'a>(markdown: &'a str) -> (HashMap<String, YamlValue>, &'a str) {
+ let mut yaml_map = HashMap::new();
+ let lines = markdown.lines();
+ let mut yaml_started = false;
+ let mut yaml_ended = false;
+ let mut end_index = 0;
+ let mut current_key: Option<String> = None;
+ let mut current_list: Vec<String> = Vec::new();
+
+ for (i, line) in lines.enumerate() {
+ if line.trim() == "---" {
+ if yaml_started {
+ yaml_ended = true;
+ end_index = markdown.lines().take(i + 1).map(|l| l.len() + 1).sum();
+ break;
+ } else {
+ yaml_started = true;
+ }
+ } else if yaml_started && !yaml_ended {
+ if line.trim().starts_with('-') && current_key.is_some() {
+ current_list.push(line.trim().trim_start_matches('-').trim().to_string());
+ } else if let Some((key, value)) = line.split_once(':') {
+ if let Some(key) = current_key.take() {
+ if !current_list.is_empty() {
+ yaml_map.insert(key, YamlValue::List(current_list.clone()));
+ current_list.clear();
+ }
+ }
+ current_key = Some(key.trim().to_string());
+ if !value.trim().is_empty() {
+ yaml_map.insert(
+ current_key.clone().unwrap(),
+ YamlValue::Scalar(value.trim().to_string()),
+ );
+ current_key = None;
+ }
+ }
+ }
+ }
+
+ if let Some(key) = current_key.take() {
+ if !current_list.is_empty() {
+ yaml_map.insert(key, YamlValue::List(current_list));
+ }
+ }
+
+ if !yaml_ended {
+ end_index = markdown.len();
+ }
+
+ (yaml_map, &markdown[end_index..])
+}
+
+fn compile_file(file_path: &Path) -> Result<String, Box<dyn std::error::Error>> {
+ let extension = file_path.extension().expect("SP14");
+
+ let file_content = fs::read_to_string(file_path).expect("Failed to read file");
+ let (map, file_content) = extract_parse_yaml_metadata(&file_content);
+ let file_content = match extension.to_str().expect("SP15") {
+ "md" => markdown::to_html(file_content),
+ _ => file_content.to_string(),
+ };
+
+ let Some(template) = map.get("template") else {
+ return Ok(file_content);
+ };
+
+ let template_file = format!(
+ "{}{}.html",
+ TEMPLATES_DIR,
+ template
+ );
+ //println!(
+ // "Processing template {} for content file {:?}",
+ // template_file, file_path
+ //);
+ let template = fs::read_to_string(template_file).expect("Failed to read template");
+
+ let mut macro_processor = MacroProcessor::new();
+ for (key, value) in map {
+ macro_processor.define_macro(format!("METADATA_{}", key), value.to_string());
+ }
+ macro_processor.define_macro(String::from("CONTENT"), file_content);
+ let final_output = macro_processor.process_input(&template);
+
+ Ok(final_output)
+}
+
+fn compile_file_and_write(source_file_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
+ let dest_file_path = Path::new(BUILD_DIR).join(source_file_path.strip_prefix(CONTENT_DIR).expect("SP14")).with_extension("html");
+
+ std::fs::create_dir_all(&dest_file_path.parent().expect("SP19")).expect("SP10");
+
+ let file_content = compile_file(&source_file_path)?;
+ fs::write(&dest_file_path, file_content)?;
+ Ok(())
+}
+
+fn compile_files_in_directory(directory: &Path) {
+ for entry in fs::read_dir(directory).expect("SP4") {
+ let entry = entry.expect("SP5");
+ let path = entry.path();
+
+ let metadata = fs::metadata(&path).expect("SP6");
+ if metadata.is_file() {
+ println!(
+ "Compiling {:#?}",
+ path.as_path()
+ );
+ compile_file_and_write(path.as_path()).expect("SP12");
+ } else if metadata.is_dir() {
+ compile_files_in_directory(path.as_path());
+ }
+ }
+
+}
+
+fn main() {
+ std::fs::remove_dir_all(Path::new(BUILD_DIR)).expect("SP21");
+ // Should run twice, or at least the files which uses macros which has to be generated during
+ // runtime
+ compile_files_in_directory(Path::new(CONTENT_DIR));
+
+ // Just for testing
+ //compile_file_and_write(Path::new("content/test.html")).expect("AYYYYO");
+}
diff --git a/src/skaldpress/mod.rs b/src/skaldpress/mod.rs
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/skaldpress/mod.rs
diff --git a/tests/example_include.smp b/tests/example_include.smp
new file mode 100644
index 0000000..ce45d39
--- /dev/null
+++ b/tests/example_include.smp
@@ -0,0 +1 @@
+define(SMP)DNL
diff --git a/tests/macro_processor.rs b/tests/macro_processor.rs
new file mode 100644
index 0000000..0c3651c
--- /dev/null
+++ b/tests/macro_processor.rs
@@ -0,0 +1,244 @@
+use skaldpress::macro_processor::MacroProcessor;
+
+fn run_macro_processor(input: &str) -> String {
+ let mut macro_processor = MacroProcessor::new();
+ macro_processor.process_input(&input)
+}
+
+#[test]
+fn test_smn_empty_string() {
+ assert_eq!(
+ run_macro_processor(""),
+ ""
+ );
+}
+
+#[test]
+fn test_smp_non_macro_html() {
+ assert_eq!(
+ run_macro_processor(
+"<html>
+ <p>This is a <em>test</em></p>
+</html>"),
+
+"<html>
+ <p>This is a <em>test</em></p>
+</html>",
+ );
+}
+
+#[test]
+fn test_smp_define_1() {
+ assert_eq!(
+ run_macro_processor(
+"define(TEMP, testvalue)
+TEMP"),
+
+"
+testvalue",
+ );
+}
+
+#[test]
+fn test_smp_define_2() {
+ assert_eq!(
+ run_macro_processor(
+"define(TEMP, TEMP2)define(TEMP)ifdef(TEMP2, TEMP2_ISDEF)"),
+
+"TEMP2_ISDEF",
+ );
+}
+
+#[test]
+fn test_smp_dnl_1() {
+ assert_eq!(
+ run_macro_processor(
+"DNL
+test"),
+
+"test",
+ );
+}
+
+#[test]
+fn test_smp_dnl_2() {
+ assert_eq!(
+ run_macro_processor(
+"DNL
+test"),
+
+"test",
+ );
+}
+
+#[test]
+fn test_smp_dnl_3() {
+ assert_eq!(
+ run_macro_processor(
+"define(MAC1, test)DNL
+MAC1 DNL
+test"),
+
+"test test",
+ );
+}
+
+#[test]
+fn test_smp_ifdef_0() {
+ assert_eq!(
+ run_macro_processor(
+"define(MAC1, test)ifdef(MAC1, MAC1_ISDEF)"),
+"MAC1_ISDEF",
+ );
+}
+
+#[test]
+fn test_smp_ifdef_1() {
+ assert_eq!(
+ run_macro_processor(
+"define(MAC1, test)DNL
+ifdef(MAC1, MAC1_ISDEF)
+ifdef(MAC2, MAC2_ISDEF, MAC2_ISNDEF)"),
+
+"MAC1_ISDEF
+MAC2_ISNDEF",
+ );
+}
+
+
+#[test]
+fn test_smp_ifdef_2() {
+ assert_eq!(
+ run_macro_processor(
+"ifdef(MAC, MAC_ISDEF)"),
+
+"",
+ );
+}
+
+#[test]
+fn test_smp_ifdef_3() {
+ assert_eq!(
+ run_macro_processor(
+"ifdef(MAC, MAC_ISDEF, MAC_ISNDEF)"),
+
+"MAC_ISNDEF",
+ );
+}
+
+#[test]
+fn test_smp_ifndef_1() {
+ assert_eq!(
+ run_macro_processor(
+"ifndef(MAC, MAC_ISNDEF)"),
+
+"MAC_ISNDEF",
+ );
+}
+
+#[test]
+fn test_smp_ifndef_2() {
+ assert_eq!(
+ run_macro_processor(
+"define(MAC, test)DNL
+ifndef(MAC, MAC_ISNDEF, MAC_ISDEF)"),
+
+"MAC_ISDEF",
+ );
+}
+
+#[test]
+fn test_smp_ifndef_3() {
+ assert_eq!(
+ run_macro_processor(
+"define(MAC, test)DNL
+ifndef(MAC, MAC_ISNDEF)"),
+
+"",
+ );
+}
+
+#[test]
+fn test_include_1() {
+ assert_eq!(
+ run_macro_processor(
+"include(tests/example_include.smp)"),
+
+"",
+ );
+}
+
+#[test]
+fn test_include_2() {
+ assert_eq!(
+ run_macro_processor(
+"include(tests/example_include.smp)DNL
+ifdef(SMP, SMP_ISDEF, SMP_ISNDEF)"),
+
+"SMP_ISDEF",
+ );
+}
+
+#[test]
+fn test_ifeq_1() {
+ assert_eq!(
+ run_macro_processor("ifeq(a, a, true, false)"),
+ "true",
+ );
+}
+
+#[test]
+fn test_ifeq_2() {
+ assert_eq!(
+ run_macro_processor("ifeq(a, b, true, false)"),
+ "false",
+ );
+}
+
+#[test]
+fn test_ifeq_3() {
+ assert_eq!(
+ run_macro_processor("ifeq(a, a, true)"),
+ "true",
+ );
+}
+
+#[test]
+fn test_ifeq_4() {
+ assert_eq!(
+ run_macro_processor("ifeq(a, b, true)"),
+ "",
+ );
+}
+
+#[test]
+fn test_ifneq_1() {
+ assert_eq!(
+ run_macro_processor("ifneq(a, a, true, false)"),
+ "false",
+ );
+}
+
+#[test]
+fn test_ifneq_2() {
+ assert_eq!(
+ run_macro_processor("ifneq(a, b, true, false)"),
+ "true",
+ );
+}
+
+#[test]
+fn test_ifneq_3() {
+ assert_eq!(
+ run_macro_processor("ifneq(a, a, true)"),
+ "",
+ );
+}
+
+#[test]
+fn test_ifneq_4() {
+ assert_eq!(
+ run_macro_processor("ifneq(a, b, true)"),
+ "true",
+ );
+}