fully ported library
This commit is contained in:
parent
3385594089
commit
7c2812be4f
|
@ -0,0 +1,76 @@
|
|||
/** the transform module defines generic transformer and
|
||||
* transformer registry logic
|
||||
*/
|
||||
|
||||
pub mod transform;
|
||||
|
||||
/** the syntax module implements several transformers */
|
||||
pub mod syntax;
|
||||
|
||||
// protect escaped tokens
|
||||
const DOLLAR_TOKEN: &str = "e4d097183ab04e49f25cb7b0956fb9eb25b90c0316a32cb5afcbcdd9a6692e8d2974919035789d5632b10d799db5b3e5bf8539592c904497f5c356f117ef37382";
|
||||
const ASTERISK_TOKEN: &str = "253c3cd0a904d28abc3e601e3557d59ea69da2616079ceef4987d58d55c9820c83026be92a917ee19a298e613ea0b393cc70d4e55dc614a9afc6a020d8f08f37";
|
||||
const UNDERSCORE_TOKEN: &str = "2b08e24b7833e90c74ed8e6c27b7b3cd5fe949e0f18b28af813d5f2df863d55f97b0ed7f8fbb26a152eda55ac073331ce11ac10702caca5b3ea4a29f722840b9";
|
||||
const SQUARE_BRACKET_TOKEN: &str = "51b06edd58f36003844941916cd3b313979fece55824d89ba02af052a229b2673aafffa541b703472c1a21d8e6a1bb3e844d236fb0e8bf5d62902b24042f4fb5";
|
||||
|
||||
pub fn _format(args: std::fmt::Arguments) -> String {
|
||||
// 1. pre-process with format
|
||||
let pre_formatted = std::fmt::format(args);
|
||||
|
||||
// 2. protect escaped characters with tokens
|
||||
let escaped = pre_formatted.replace("\\$", DOLLAR_TOKEN);
|
||||
let escaped = escaped.replace("\\*", ASTERISK_TOKEN);
|
||||
let escaped = escaped.replace("\\_", UNDERSCORE_TOKEN);
|
||||
let escaped = escaped.replace("\\[", SQUARE_BRACKET_TOKEN);
|
||||
|
||||
// 3. create transform registry
|
||||
let mut registry = transform::registry::new();
|
||||
registry.add( syntax::bold::get_transformer() );
|
||||
registry.add( syntax::italic::get_transformer() );
|
||||
registry.add( syntax::underline::get_transformer() );
|
||||
registry.add( syntax::hyperlink::get_transformer() );
|
||||
registry.add( syntax::color::get_transformer() );
|
||||
|
||||
// 4. apply transformations
|
||||
let transformed = registry.transform(&escaped).unwrap();
|
||||
|
||||
// 5. restore token-protected escapes
|
||||
let unescaped = transformed.replace(DOLLAR_TOKEN, "\\$");
|
||||
let unescaped = unescaped.replace(ASTERISK_TOKEN, "\\*");
|
||||
let unescaped = unescaped.replace(UNDERSCORE_TOKEN, "\\_");
|
||||
let unescaped = unescaped.replace(SQUARE_BRACKET_TOKEN, "\\[");
|
||||
|
||||
unescaped
|
||||
}
|
||||
|
||||
pub fn _print(args: std::fmt::Arguments) {
|
||||
// 1. process with format
|
||||
let processed = _format(args);
|
||||
|
||||
std::print!("{}", processed);
|
||||
}
|
||||
pub fn _println(args: std::fmt::Arguments) {
|
||||
// 1. process with format
|
||||
let processed = _format(args);
|
||||
|
||||
std::print!("{}\n", processed);
|
||||
}
|
||||
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! format {
|
||||
($($arg:tt)*) => ($crate::_format(format_args!($($arg)*)))
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($($arg:tt)*) => ($crate::_print(format_args!($($arg)*)));
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
() => ($crate::_print!("\n"));
|
||||
($($arg:tt)*) => ({
|
||||
$crate::_println(format_args!($($arg)*));
|
||||
})
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
use regex;
|
||||
use crate::transform::Transformer;
|
||||
|
||||
pub struct BoldTransformer {
|
||||
regex: regex::Regex,
|
||||
}
|
||||
|
||||
pub fn get_transformer() -> BoldTransformer {
|
||||
BoldTransformer {
|
||||
regex: regex::Regex::new(r"(?m)\*\*((?:[^\*]+\*?)+)\*\*").unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Transformer for BoldTransformer {
|
||||
|
||||
fn get_regex(&self) -> ®ex::Regex { &self.regex }
|
||||
|
||||
fn transform(&self, captures: ®ex::Captures) -> Option<String> {
|
||||
// don't mutate empty catpures
|
||||
if captures[0].len() < 1 || captures.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some( format!("\x1b[1m{}\x1b[22m", captures[1].replace("\x1b[0m", "\x1b[0m\x1b[1m")) )
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
use regex;
|
||||
use crate::transform::Transformer;
|
||||
|
||||
pub struct ColorTransformer {
|
||||
regex: regex::Regex
|
||||
}
|
||||
|
||||
pub fn get_transformer() -> ColorTransformer {
|
||||
ColorTransformer {
|
||||
regex: regex::Regex::new(r"(?m)\$\{([^$]+)\}\(((?:[a-z]+|#(?:[0-9a-f]{3}|[0-9a-f]{6})))?(?::(#(?:[0-9a-f]{3}|[0-9a-f]{6})))?\)").unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Transformer for ColorTransformer {
|
||||
|
||||
fn get_regex(&self) -> ®ex::Regex { &self.regex }
|
||||
|
||||
fn transform(&self, captures: ®ex::Captures) -> Option<String> {
|
||||
// don't mutate empty catpures
|
||||
if captures[0].len() < 1 || captures.len() < 4 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let text = captures[1].to_owned();
|
||||
let foreground = match captures.get(2) {
|
||||
Some(fg_text) => hex_to_rgb(&fg_text.as_str()[1..]),
|
||||
None => None,
|
||||
};
|
||||
let background = match captures.get(3) {
|
||||
Some(bg_text) => hex_to_rgb(&bg_text.as_str()[1..]),
|
||||
None => None,
|
||||
};
|
||||
|
||||
if let Some(fg) = foreground {
|
||||
|
||||
// both
|
||||
if let Some(bg) = background {
|
||||
return Some( format!("\x1b[38;2;{};{};{};48;2;{};{};{}m{}\x1b[0m", fg.0, fg.1, fg.2, bg.0, bg.1, bg.2, text) );
|
||||
}
|
||||
|
||||
// only foreground
|
||||
return Some( format!("\x1b[38;2;{};{};{}m{}\x1b[0m", fg.0, fg.1, fg.2, text) );
|
||||
}
|
||||
|
||||
// only bg
|
||||
if let Some(bg) = background {
|
||||
return Some( format!("\x1b[48;2;{};{};{}m{}\x1b[0m", bg.0, bg.1, bg.2, text) );
|
||||
}
|
||||
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
fn hex_to_rgb(hex: &str) -> Option<(u8, u8, u8)> {
|
||||
if hex.len() != 3 && hex.len() != 6 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let fullhex;
|
||||
|
||||
// 3 char version
|
||||
if hex.len() == 3 {
|
||||
fullhex = format!("{0}{0}{1}{1}{2}{2}", hex.chars().nth(0).unwrap(), hex.chars().nth(1).unwrap(), hex.chars().nth(2).unwrap());
|
||||
} else {
|
||||
fullhex = hex.to_owned();
|
||||
}
|
||||
|
||||
// ignore on error
|
||||
if let Ok(r) = u8::from_str_radix(&fullhex[0..2], 16) {
|
||||
if let Ok(g) = u8::from_str_radix(&fullhex[2..4], 16) {
|
||||
if let Ok(b) = u8::from_str_radix(&fullhex[4..6], 16) {
|
||||
return Some( (r,g,b) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return None;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
use regex;
|
||||
use crate::transform::Transformer;
|
||||
|
||||
pub struct HyperlinkTransformer {
|
||||
regex: regex::Regex
|
||||
}
|
||||
|
||||
pub fn get_transformer() -> HyperlinkTransformer {
|
||||
HyperlinkTransformer {
|
||||
regex: regex::Regex::new(r"(?m)(^|[^\x1b])\[([^(?:\]()]+)\]\(([^\)]+)\)").unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
impl Transformer for HyperlinkTransformer {
|
||||
|
||||
fn get_regex(&self) -> ®ex::Regex { &self.regex }
|
||||
|
||||
fn transform(&self, captures: ®ex::Captures) -> Option<String> {
|
||||
// don't mutate empty catpures
|
||||
if captures[0].len() < 1 || captures.len() < 3 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let previous_char = captures[1].to_owned();
|
||||
let label = captures[2].to_owned();
|
||||
let target = captures[3].to_owned();
|
||||
|
||||
Some( format!("{}\x1b]8;;{}\x1b\\{}\x1b]8;;\x1b\\", previous_char, target, label) )
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
use regex;
|
||||
use crate::transform::Transformer;
|
||||
|
||||
pub struct ItalicTransformer {
|
||||
regex: regex::Regex,
|
||||
}
|
||||
|
||||
pub fn get_transformer() -> ItalicTransformer{
|
||||
ItalicTransformer {
|
||||
regex: regex::Regex::new(r"(?m)\*([^\*]+)\*").unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
impl Transformer for ItalicTransformer {
|
||||
|
||||
fn get_regex(&self) -> ®ex::Regex { &self.regex }
|
||||
|
||||
fn transform(&self, captures: ®ex::Captures) -> Option<String> {
|
||||
// don't mutate empty catpures
|
||||
if captures[0].len() < 1 || captures.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some( format!("\x1b[3m{}\x1b[23m", captures[1].replace("\x1b[0m", "\x1b[0m\x1b[3m")) )
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
pub mod bold;
|
||||
pub mod italic;
|
||||
pub mod underline;
|
||||
pub mod hyperlink;
|
||||
pub mod color;
|
|
@ -0,0 +1,26 @@
|
|||
use regex;
|
||||
use crate::transform::Transformer;
|
||||
|
||||
pub struct UnderlineTransformer {
|
||||
regex: regex::Regex
|
||||
}
|
||||
pub fn get_transformer() -> UnderlineTransformer {
|
||||
UnderlineTransformer {
|
||||
regex: regex::Regex::new(r"(?m)_([^_]+)_").unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Transformer for UnderlineTransformer {
|
||||
|
||||
fn get_regex(&self) -> ®ex::Regex { &self.regex }
|
||||
|
||||
fn transform(&self, captures: ®ex::Captures) -> Option<String> {
|
||||
// don't mutate empty catpures
|
||||
if captures[0].len() < 1 || captures.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some( format!("\x1b[4m{}\x1b[24m", captures[1].replace("\x1b[0m", "\x1b[0m\x1b[4m")) )
|
||||
}
|
||||
}
|
|
@ -3,18 +3,16 @@ use regex;
|
|||
pub mod errors;
|
||||
pub mod registry;
|
||||
|
||||
pub struct Transformer {
|
||||
pub trait Transformer {
|
||||
|
||||
// the regex matching text to replace
|
||||
pub matcher: regex::Regex,
|
||||
fn get_regex<'t>(&'t self) -> &'t regex::Regex;
|
||||
|
||||
// transform is called to replace a match by its transformation
|
||||
// it takes as arguments the matched string chunks from the regex
|
||||
pub transform: fn(®ex::Captures) -> Option<String>
|
||||
}
|
||||
fn transform(&self, captures: ®ex::Captures) -> Option<String>;
|
||||
|
||||
impl Transformer {
|
||||
// execute 1 given transformer @t with its @input string and returns the output,
|
||||
// and the error if one.
|
||||
// transforms the input and returns an optional error.
|
||||
fn execute(&self, input: &mut String) -> Option<errors::Error> {
|
||||
let mut cursor = 0;
|
||||
let original = input.to_owned();
|
||||
|
@ -23,16 +21,16 @@ impl Transformer {
|
|||
input.clear();
|
||||
|
||||
// apply transformatione for each match
|
||||
for re_match in self.matcher.find_iter(&original) {
|
||||
for re_match in self.get_regex().find_iter(&original) {
|
||||
|
||||
// 1. append gap between input start OR previous match
|
||||
input.push_str( &original[cursor..re_match.start()] );
|
||||
cursor = re_match.end();
|
||||
|
||||
// 2. execute transformation on captures
|
||||
for re_captures in self.matcher.captures(re_match.as_str()) {
|
||||
for re_captures in self.get_regex().captures(re_match.as_str()) {
|
||||
|
||||
if let Some(transformed) = (self.transform)(&re_captures) {
|
||||
if let Some(transformed) = self.transform(&re_captures) {
|
||||
// 3. apply transformation
|
||||
input.push_str(&transformed);
|
||||
}
|
||||
|
|
|
@ -12,14 +12,14 @@ pub fn new() -> Registry {
|
|||
pub struct Registry {
|
||||
// Transformers represents the transformer stack
|
||||
// ; each one will be executed in ascending order
|
||||
transformers: vec::Vec<Transformer>
|
||||
transformers: vec::Vec<Box<dyn Transformer>>
|
||||
}
|
||||
|
||||
|
||||
impl Registry {
|
||||
|
||||
pub fn add(&mut self, transformer: Transformer) {
|
||||
self.transformers.push(transformer);
|
||||
pub fn add(&mut self, transformer: impl Transformer + 'static) {
|
||||
self.transformers.push( Box::new(transformer) );
|
||||
}
|
||||
|
||||
// transform executes each transformer of the stack in ascending order feeding
|
||||
|
|
Loading…
Reference in New Issue