diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Cargo.lock | 25 | ||||
-rw-r--r-- | Cargo.toml | 18 | ||||
-rw-r--r-- | build/about.html | 23 | ||||
-rw-r--r-- | build/articles/testart.html | 23 | ||||
-rw-r--r-- | build/articles/unbricking_idrac.html | 104 | ||||
-rw-r--r-- | build/test.html | 22 | ||||
-rw-r--r-- | src/lib.rs | 2 | ||||
-rw-r--r-- | src/macro_processor/macro_processor.rs | 260 | ||||
-rw-r--r-- | src/macro_processor/main.rs | 12 | ||||
-rw-r--r-- | src/macro_processor/mod.rs | 2 | ||||
-rw-r--r-- | src/skaldpress/main.rs | 153 | ||||
-rw-r--r-- | src/skaldpress/mod.rs | 0 | ||||
-rw-r--r-- | tests/example_include.smp | 1 | ||||
-rw-r--r-- | tests/macro_processor.rs | 244 |
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 <code>firmimg.d6</code> onto a FAT formatted SD card and put it int the vFlash. +But no reaction whatsoever. +It looks to me like everyone ("on the internet") 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 <code>f1</code> +to continue boot <code>f2</code> to open BIOS settings. +If you disconnect the boot drive and press <code>f1</code> +it will attempt to boot using PXE, when that fails. +You will get the same prompt, +with the additional option of pressing <code>f11</code> to choose the boot device.</p> +<section id="bios-settings"> +<h2><a href="#bios-settings">#</a> BIOS Settings</h2> +<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</code></pre> +</code></pre> +</section> +<section id="boot-an-os"> +<h2><a href="#boot-an-os">#</a> Boot an OS</h2> +<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> +</section> +<section id="setup-your-serial-console"> +<h2><a href="#setup-your-serial-console">#</a> Setup your serial console</h2> +<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> +</section> +<section id="setup-a-tftp-server"> +<h2><a href="#setup-a-tftp-server">#</a> Setup a TFTP server</h2> +<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> +</section> +<section id="flash-the-firmware"> +<h2><a href="#flash-the-firmware">#</a> Flash the firmware</h2> +<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> +</section> + + </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(¯o_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(¯o_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(¯o_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(¯o_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", + ); +} |