Processor

Struct Processor 

Source
pub struct Processor<'processor> { /* private fields */ }
Expand description

Central macro logic processor

  • Processor parses given input and expands detected macros
  • Processor substitutes all macros only when the macros were already defined. The fallback behaviour can be configured.
  • Processor can handle various types of inputs (string|stdin|file)

§Detailed usage

use r4d::{
    RadResult, Processor, AuthType, CommentType,
    WarningType, MacroType, Hygiene
};
#[cfg(feature = "debug")]
use r4d::DiffOption;
#[cfg(feature = "hook")]
use r4d::HookType; // This is behind hook feature
use std::path::Path;

fn main() -> RadResult<()> {
    // Builder
    let mut processor = Processor::new()
        .set_comment_type(CommentType::Start)                // Use comment
        .custom_macro_char('~')?                             // use custom macro character
        .custom_comment_char('#')?                           // use custom comment character
        .purge(true)                                         // Purge undefined macro
        .silent(WarningType::Security)                       // Silents all warnings
        .assert(true)                                        // Enable assertion mode
        .lenient(true)                                       // Disable strict mode
        .hygiene(Hygiene::Macro)                             // Enable hygiene mode
        .pipe_truncate(false)                                // Disable pipe truncate
        .write_to_file(Path::new("out.txt"))?                // default is stdout
        .error_to_file(Path::new("err.txt"))?                // default is stderr
        .unix_new_line(true)                                 // use unix new line for formatting
        .discard(true)                                       // discard all output
        .melt_files(&[Path::new("source.r4d")])?             // Read runtime macros from frozen
        // Permission
        .allow(&[AuthType::ENV])                             // Grant permission of authtypes
        .allow_with_warning(&[AuthType::CMD]);               // Grant permission of authypes with warning enabled


        // Debugging options
        #[cfg(feature = "debug")]
        {
            processor = processor
                .diff(DiffOption::All)?                      // Print diff in final result
                .debug(true)                                 // Turn on debug mode
                .interactive(true)                           // Use interactive mode
                .log(true);                                  // Log all macro invocations
        }

    // Comment char and macro char cannot be same
    // Unallowed pattern for the characters are [a-zA-Z1-9\\_\*\^\|\(\)=,]

    // Use Processor::empty() instead of Processor::new()
    // if you don't want any default macros

    // Print information about current processor permissions
    // This is an warning and can be suppressed with silent option
    processor.print_permission()?;

    // Register a hook macro
    // Trigger and execution macro should be defined elsewhere
    #[cfg(feature = "hook")]
    processor.register_hook(
        HookType::Macro,            // Macro type
        "trigger_macro",            // Macro that triggers
        "hook_div",                 // Macro to be executed
        1,                          // target count
        false                       // Resetable
    )?;

    // Add runtime rules(in order of "name, args, body")
    processor.add_runtime_rules(&[("test","a_src a_link","$a_src() -> $a_link()")])?;

    // Add custom rules without any arguments
    processor.add_static_rules(&[("test","TEST"),("lul","kekw")])?;

    // Undefine only macro
    processor.undefine_macro("name1", MacroType::Any);

    // Process with inputs
    // This prints to desginated write destinations
    processor.process_string(r#"$define(test=Test)"#)?;
    processor.process_stdin()?;
    processor.process_file(Path::new("from.txt"))?;

    processor.freeze_to_file(Path::new("out.r4f"))?; // Create frozen file

    // Print out result
    // This will print counts of warning and errors.
    // It will also print diff between source and processed if diff option was
    // given as builder pattern.
    processor.print_result()?;                       
    Ok(())
}

Implementations§

Source§

impl<'processor> Processor<'processor>

Source

pub fn new() -> Self

Creates default processor with built-in macros

You can chain builder methods to further configure processor

use std::path::Path;
let proc = r4d::Processor::new()
    .lenient(true)
    .pipe_truncate(false)
    .unix_new_line(true)
    .write_to_file(Path::new("cache.txt"))
    .expect("Failed to open a file");
Source

pub fn empty() -> Self

Creates default processor without built-in macros

You can chain builder methods to further configure processor

use std::path::Path;
let proc = r4d::Processor::empty()
    .lenient(true)
    .pipe_truncate(false)
    .unix_new_line(true)
    .write_to_file(Path::new("cache.txt"))
    .expect("Failed to open a file");
Source

pub fn write_to_file<P: AsRef<Path>>(self, target_file: P) -> RadResult<Self>

Set write option to yield output to the file

use std::path::Path;
let proc = r4d::Processor::empty()
    .write_to_file(Path::new("cache.txt"))
    .expect("Failed to open a file");
Source

pub fn write_to_variable(self, value: &'processor mut String) -> Self

Write to variable

let mut acc = String::new();
let proc = r4d::Processor::empty()
    .write_to_variable(&mut acc);
Source

pub fn error_to_file<F: AsRef<Path>>(self, target_file: F) -> RadResult<Self>

Yield error to the file

use std::path::Path;
let proc = r4d::Processor::empty()
    .error_to_file(Path::new("err.txt"))
    .expect("Failed to open a file");
Source

pub fn error_to_variable(self, value: &'processor mut String) -> Self

Yield error to the file

let mut acc = String::new();
let proc = r4d::Processor::empty()
    .error_to_variable(&mut acc);
Source

pub fn custom_comment_char(self, character: char) -> RadResult<Self>

Custom comment character

Every character that consists of valid macro name cannot be a custom comment character. Unallowed characters are [a-zA-Z1-9\\_\*\^\|\(\)=,]

let proc = r4d::Processor::empty()
    .custom_comment_char('&');
Source

pub fn custom_chars( self, macro_character: char, comment_char: char, ) -> RadResult<Self>

Set custom characters

Every character that consists of valid macro name cannot be a custom macro character. Unallowed characters are [a-zA-Z1-9\\_\*\^\|\(\)=,]

let proc = r4d::Processor::empty()
    .custom_chars('&', '%');
Source

pub fn custom_macro_char(self, character: char) -> RadResult<Self>

Custom macro character

Every character that consists of valid macro name cannot be a custom macro character. Unallowed characters are [a-zA-Z1-9\\_\*\^\|\(\)=,]

let proc = r4d::Processor::empty()
    .custom_macro_char('&');
Source

pub fn unix_new_line(self, use_unix_new_line: bool) -> Self

Use unix line ending instead of operating system’s default one

let proc = r4d::Processor::empty()
    .unix_new_line(true);
Source

pub fn purge(self, purge: bool) -> Self

Set purge option

Purge mode removed failed macro expression.

This overrides lenient option

let proc = r4d::Processor::empty()
    .purge(true);
Source

pub fn lenient(self, lenient: bool) -> Self

Set lenient

Lenient mode left macro expression in place.

This overrides purge option

let proc = r4d::Processor::empty()
    .lenient(true);
Source

pub fn hygiene(self, hygiene: Hygiene) -> Self

Set hygiene variant

Hygiene decides the processor’s behaviour toward runtime macros

let proc = r4d::Processor::empty()
    .hygiene(r4d::Hygiene::Macro);
Source

pub fn pipe_truncate(self, truncate: bool) -> Self

Set pipe truncate option

By default, pipe truncates original value and leave empty value in place.

If pipe_truncate is set to false, it will retain the original value.

let proc = r4d::Processor::empty()
    .pipe_truncate(false);
Source

pub fn set_comment_type(self, comment_type: CommentType) -> Self

Set comment type

By default, comment is disabled for better compatibility.

There are two types of comments other than none

  • Start : Treats a line comment only if a comment character was placed in the first index.
  • Any : Treats any text chunk followed as a comment whenever a comment character is detected.
let proc = r4d::Processor::empty()
    .set_comment_type(r4d::CommentType::Start);
Source

pub fn silent(self, silent_type: WarningType) -> Self

Set silent option

By default, every warning types are enabled>

There are three types of warnings

  • Sanity : Rrelated to possibly unintended behaviours.
  • Security : Related to possibly dangerous behaviour related to a file system.
  • Any : Both of the warnings
let proc = r4d::Processor::empty()
    .silent(r4d::WarningType::Sanity);
Source

pub fn assert(self, assert: bool) -> Self

Set assertion mode

Assert mode will not print the output by default and treat assertion fallable not panicking.

let proc = r4d::Processor::empty()
    .assert(true);
Source

pub fn melt_files(self, paths: &[impl AsRef<Path>]) -> RadResult<Self>

Melt rule file

This always melt file into non-volatile form, which means hygiene doesn’t affect melted macros.

use std::path::Path;
let proc = r4d::Processor::empty()
    .melt_files(&[Path::new("a.r4f"), Path::new("b.r4f")]);
Source

pub fn allow(self, auth_types: &[AuthType]) -> Self

Open authorization of processor

Some macros require authorization be granted by user. There are several authorization types as follows.

  • ENV : Get or set environment variable
  • CMD : System command
  • FIN : File read
  • FOUT : File write
let proc = r4d::Processor::empty()
    .allow(&[r4d::AuthType::CMD]);
Source

pub fn allow_with_warning(self, auth_types: &[AuthType]) -> Self

Open authorization of processor but yield warning

Some macros require authorization be granted by user. This grants authorization to processor but yields warning on every authorized macro invocation.

There are several authorization types as follows.

  • ENV : Get or set environment variable
  • CMD : System command
  • FIN : File read
  • FOUT : File write
let proc = r4d::Processor::empty()
    .allow_with_warning(&[r4d::AuthType::CMD]);
Source

pub fn discard(self, discard: bool) -> Self

Discard output

Set write option to discard. Nothing will be printed or redirected.

let proc = r4d::Processor::empty()
    .discard(true);
Source

pub fn storage(self, storage: Box<dyn RadStorage>) -> Self

Build with storage

Append Storage to a processor. Storage should implment RadStorage trait.

Storage interaction is accessed with update and extract macro.

let proc = r4d::Processor::empty()
    .storage(Box::new(CustomStorage));
Source

pub fn melt_literal(&mut self, literal: &[u8]) -> RadResult<()>

Melt rule as literal input source, or say from byte array

This always melt file into non-volatile form, which means hygiene doesn’t affect melted macros.

let source = b"Some frozen macro definition";
let proc = r4d::Processor::empty();
proc.melt_literal(source);
Source

pub fn package_sources<T: AsRef<Path>>( &self, sources: &[T], out_file: Option<T>, ) -> RadResult<()>

Packages sources into a single file

let source = ["a.r4f","b.r4f"];
let proc = r4d::Processor::empty();
proc.package_sources(&source, Some(Path::new("OUT_FILE.r4c")));
Source

pub fn import_frozen_file(&mut self, path: &Path) -> RadResult<()>

Import(melt) a frozen file

This always melt file into non-volatile form

use std::path::Path;
let mut proc = r4d::Processor::empty();
proc.import_frozen_file(Path::new("file.r4f")).expect("Failed to import a frozen file");
Source

pub fn insert_queue(&mut self, item: &str)

Set queue object

This is intended for macro logics not processor logics.

Queued objects are not executed immediately but executed only after currently aggregated macro fragments are fully expanded.

let mut proc = r4d::Processor::empty();
proc.insert_queue("$halt()");
Source

pub fn set_hygiene(&mut self, hygiene: Hygiene)

Set hygiene type

Set hygiene type and also clears volatile runtime macro.

Queued objects are not executed immediately but executed only after currently aggregated macro fragments are fully expanded.

let mut proc = r4d::Processor::empty();
proc.set_hygiene(r4d::Hygiene::Macro);
Source

pub fn set_dry_mode(&mut self)

Set to dry run mode

Source

pub fn set_freeze_mode(&mut self)

Set to freeze mode

Source

pub fn clear_volatile(&mut self)

Clear volatile macros

This removes runtime macros which are not melted from.

let mut proc = r4d::Processor::empty();
proc.clear_volatile();
Source

pub fn set_write_option(&mut self, write_option: WriteOption<'processor>)

Set write option in the process

let mut proc = r4d::Processor::empty();
proc.set_write_option(r4d::WriteOption::Discard);
Source

pub fn reset_flow_control(&mut self)

Reset flow control

let mut proc = r4d::Processor::empty();
proc.reset_flow_control();
Source

pub fn print_permission(&mut self) -> RadResult<()>

Print current permission status

let mut proc = r4d::Processor::empty();
proc.print_permission().expect("Failed to print permission data");
Source

pub fn print_result(&mut self) -> RadResult<()>

Print the result of a processing

This will also print diff file if debug and diff feature is enabled.

let mut proc = r4d::Processor::empty();
proc.print_result().expect("Failed to print result");
Source

pub fn set_storage(&mut self, storage: Box<dyn RadStorage>)

Set storage

Storage should implment RadStorage trait.

let mut proc = r4d::Processor::empty();
proc.set_storage(Box::new(some_storage_struct));
Source

pub fn update_storage( &mut self, data: &[String], ) -> RadResult<Option<StorageOutput>>

Update storage

let mut proc = r4d::Processor::empty();
proc.update_storage(&[String::new("Hello world")]).expect("Failed to update a storage");
Source

pub fn extract_storage( &mut self, serialize: bool, ) -> RadResult<Option<StorageOutput>>

Extract from storage

let mut proc = r4d::Processor::empty();
let serialize = false;
proc.extract_storage(serialize).expect("Failed to extract storage information");
Source

pub fn freeze_to_file(&mut self, path: impl AsRef<Path>) -> RadResult<()>

Freeze to a single file

Frozen file is a bincode encoded binary format file.

use std::path::Path;
let mut proc = r4d::Processor::empty();
proc.freeze_to_file(Path::new("file.r4f")).expect("Failed to freeze to a file");
Source

pub fn serialize_rules(&self) -> RadResult<Vec<u8>>

Serialize rule files into a bincode

Source

pub fn add_ext_macro(&mut self, ext: ExtMacroBuilder)

Add a new macro as an extension

Register a function as macro. To make a register process eaiser, use template feature if possible.

Refer doc for detailed usage.

let mut processor = r4d::Processor::empty();
#[cfg(feature = "template")]
processor.add_ext_macro(r4d::ExtMacroBuilder::new("macro_name")
    .args(&["a1","b2"])
    .function(r4d::function_template!(
        let args = r4d::split_args!(2)?;
        let result = format!("{} + {}", args[0], args[1]);
        Ok(Some(result))
)));
Source

pub fn add_anon_macro(&mut self, body: &str) -> RadResult<()>

Add new anonymous macro

Source

pub fn add_runtime_rules( &mut self, rules: &[(impl AsRef<str>, &str, &str)], ) -> RadResult<()>

Add runtime rules without builder pattern

§Args

The order of argument is “name, args, body”

§Example
let mut processor = r4d::Processor::empty();
processor.add_runtime_rules(&[("macro_name","macro_arg1 macro_arg2","macro_body=$macro_arg1()")]);
Source

pub fn add_static_rules( &mut self, rules: &[(impl AsRef<str>, impl AsRef<str>)], ) -> RadResult<()>

Add static rules without builder pattern

NOTE that this method doesn’t expand body, but needs to be handled before invoking this method

§Args

The order of argument is “name, body”

§Example
let mut processor = r4d::Processor::new();
processor.add_static_rules(&[("macro_name","Macro body without arguments")]);
Source

pub fn execute_macro( &mut self, level: usize, caller: &str, macro_name: &str, arguments: &str, ) -> RadResult<Option<String>>

Execute a macro

let mut proc = r4d::Processor::empty();
proc.execute_macro(0, "MAIN", "name", "args")
    .expect("Failed to execute a macro");
Source

pub fn process_string(&mut self, content: &str) -> RadResult<Option<String>>

Read from string

let mut proc = r4d::Processor::empty();
proc.process_string("$define(new=NEW)")
    .expect("Failed to process a string");
Source

pub fn process_stdin(&mut self) -> RadResult<Option<String>>

Read from standard input

If debug mode is enabled this, doesn’t read stdin line by line but by chunk because user input is also a standard input and processor cannot distinguish the two

let mut proc = r4d::Processor::empty();
proc.process_stdin()
    .expect("Failed to process a standard input");
Source

pub fn process_file( &mut self, path: impl AsRef<Path>, ) -> RadResult<Option<String>>

Process contents from a file

use std::path::Path;
let mut proc = r4d::Processor::empty();
proc.process_file(Path::new("source.txt"))
    .expect("Failed to process a file");
Source

pub fn process_static_script( &mut self, path: impl AsRef<Path>, ) -> RadResult<Option<String>>

Process contents from a static script

use std::path::Path;
let mut proc = r4d::Processor::empty();
proc.process_static_script(Path::new("source.r4c"))
    .expect("Failed to process a script");
Source

pub fn set_documentation(&mut self, macro_name: &str, content: &str) -> bool

Set documentation for a macro

This sets a description for a macro. This will fail silent if macro doesn’t exist

§Return
  • Boolean value represents a success of an operation
§Example
let mut proc = r4d::Processor::new();
proc.set_documentation("macro_name", "this is a new macro");
Source

pub fn try_get_or_insert_regex(&mut self, expression: &str) -> RadResult<&Regex>

Try getting a regex or newly compile

§Return

this returns reference to a existing or compiled regex

Source

pub fn expand( &mut self, level: usize, src: &str, strip: bool, ) -> RadResult<String>

Expand chunk and strip quotes

This is intended for end user

Source

pub fn print_error_no_line(&mut self, error: &str) -> RadResult<()>

Print error using a processor’s error logger wihtout line information

let mut proc = r4d::Processor::new();
proc.print_error_no_line("Error occured").expect("Failed to write error");
Source

pub fn print_error(&mut self, error: &str) -> RadResult<()>

Print error using a processor’s error logger

This utilizes logger’s line number tracking and colored prompt

let mut proc = r4d::Processor::new();
proc.print_error("Error occured right now").expect("Failed to write error");
Source

pub fn split_arguments( &self, source: &str, target_length: usize, no_strip: bool, ) -> RadResult<Vec<String>>

Split arguments as vector

This is designed for end user

This doesn’t strip literal quotes from vector

let mut proc = r4d::Processor::new();
proc.split_arguments("a,b", 2,false).expect("Failed to split arguments");
Source

pub fn check_auth(&mut self, auth_type: AuthType) -> RadResult<bool>

Check auth information

This exits for internal macro logic.

This will print log_error if auth was enabled with warning

§Return

Whether auth is enabled or not

§Example
let mut proc = r4d::Processor::new();
proc.check_auth(r4d::AuthType::CMD).expect("Failed to get auth information");
Source

pub fn contains_macro(&self, macro_name: &str, macro_type: MacroType) -> bool

Check if given macro exists

This exits for internal macro logic.

let mut proc = r4d::Processor::new();
if !proc.contains_macro("name", r4d::MacroType::Runtime) {
    proc.print_error("Error").expect("Failed to write error");
}
Source

pub fn contains_local_macro( &self, level: usize, macro_name: &str, ) -> Option<String>

Check if given local macro exists

This exits for internal macro logic.

§Return
  • Return a local_name if exists
let mut proc = r4d::Processor::new();
let level = 2;
if !proc.contains_local_macro("name", 2) {
    proc.print_error("Error").expect("Failed to write error");
}
Source

pub fn undefine_macro(&mut self, macro_name: &str, macro_type: MacroType)

Try undefine a macro

This exits for internal macro logic.

let mut proc = r4d::Processor::new();
if proc.contains_macro("name", r4d::MacroType::Runtime) {
    proc.undefine_macro("name", r4d::MacroType::Runtime);
}
Source

pub fn rename_macro( &mut self, macro_name: &str, target_name: &str, macro_type: MacroType, )

Rename macro

This exits for internal macro logic.

let mut proc = r4d::Processor::new();
if proc.contains_macro("name", r4d::MacroType::Runtime) {
    proc.rename_macro("name", "new_name",r4d::MacroType::Runtime);
}
Source

pub fn append_macro(&mut self, macro_name: &str, target: &str)

Append content into a macro

This exits for internal macro logic.

This will do nothing if macro doesn’t exist

let mut proc = r4d::Processor::new();
if proc.contains_macro("name", r4d::MacroType::Runtime) {
    proc.append_macro("name", "added text");
}
Source

pub fn append_local_macro(&mut self, macro_name: &str, target: &str)

Append content into a local macro

This exits for internal macro logic.

This will do nothing if macro doesn’t exist

let mut proc = r4d::Processor::new();
if proc.contains_macro("name", r4d::MacroType::Runtime) {
    proc.append_local_macro("name", "added text");
}
Source

pub fn add_pass_through(&mut self, macro_name: &str)

Add new macro name as pass through

Source

pub fn clear_pass_through(&mut self)

Clear all macro names from pass through

Source

pub fn replace_macro(&mut self, macro_name: &str, target: &str) -> bool

Replace macro’s content

This exits for internal macro logic.

This will do nothing if macro doesn’t exist

let mut proc = r4d::Processor::new();
let level = 2
if proc.contains_local_macro("name", level) {
    proc.replace_macro("name", "new macro content");
}
Source

pub fn add_new_local_macro( &mut self, level: usize, macro_name: &str, body: &str, )

Add new local macro

This exits for internal macro logic.

let mut proc = r4d::Processor::new();
proc.add_new_local_macro(0,"a_macro", "macro body");
Source

pub fn remove_local_macro(&mut self, level: usize, macro_name: &str)

Remove local macro

This exits for internal macro logic.

This will do nothing if macro doesn’t exist

let mut proc = r4d::Processor::new();
proc.remove_local_macro(0,"a_macro");
Source

pub fn is_true(&self, src: &str) -> RadResult<bool>

Check if given text is boolean-able

This exits for internal macro logic.

This panics when the value is neither true nor false

let mut proc = r4d::Processor::new();
assert_eq!(false,proc.is_true("0").expect("Failed to convert"));
assert_eq!(true,proc.is_true("true").expect("Failed to convert"));

Trait Implementations§

Source§

impl<'processor> Default for Processor<'processor>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl<'processor> Freeze for Processor<'processor>

§

impl<'processor> !RefUnwindSafe for Processor<'processor>

§

impl<'processor> !Send for Processor<'processor>

§

impl<'processor> !Sync for Processor<'processor>

§

impl<'processor> Unpin for Processor<'processor>

§

impl<'processor> !UnwindSafe for Processor<'processor>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.