Basic Plugin Support - Syntax Highlighter Added - WIP
This commit is contained in:
parent
1c873d23b5
commit
a6a56a11c7
8 changed files with 367 additions and 7 deletions
|
@ -20,4 +20,5 @@ sanitize-filename = "0.3.0"
|
|||
log = "0.4"
|
||||
env_logger = "0.9.0"
|
||||
actix-web-httpauth = "0.6.0"
|
||||
lazy_static = "1.4.0"
|
||||
lazy_static = "1.4.0"
|
||||
rutie = "0.8.4"
|
|
@ -22,5 +22,10 @@ COPY --from=builder /usr/src/microbin/target/release/microbin /usr/local/bin/mic
|
|||
# copy /static folder containing the stylesheets
|
||||
COPY --from=builder /usr/src/microbin/static /usr/local/bin/static
|
||||
|
||||
# Install Ruby (no need if you're disabling all plugins)
|
||||
RUN \
|
||||
apt-get update && \
|
||||
apt-get install -y ruby
|
||||
|
||||
# run the binary
|
||||
CMD ["microbin"]
|
78
plugins/BasicSyntaxHighlighter.rb
Normal file
78
plugins/BasicSyntaxHighlighter.rb
Normal file
|
@ -0,0 +1,78 @@
|
|||
module MBP
|
||||
module BasicSyntaxHighlighter
|
||||
|
||||
# Plugin Properties
|
||||
|
||||
def self.get_id()
|
||||
"BasicSyntaxHighlighter"
|
||||
end
|
||||
|
||||
def self.get_name()
|
||||
"Basic Syntax Highlighter Plugin"
|
||||
end
|
||||
|
||||
def self.get_version()
|
||||
"1.0.0"
|
||||
end
|
||||
|
||||
def self.get_author()
|
||||
"Daniel Szabo"
|
||||
end
|
||||
|
||||
def self.get_webpage()
|
||||
"https://github.com/szabodanika/microbin"
|
||||
end
|
||||
|
||||
def self.get_description()
|
||||
"This plugin will simply color keywords and special characters in four different colors based on some very basic RegEx - it is meant to univesally make code pastas more readable but is not a robust syntax highlighter solution."
|
||||
end
|
||||
|
||||
# Plugin Event Hooks
|
||||
|
||||
def self.init()
|
||||
# Ignore event
|
||||
"OK"
|
||||
end
|
||||
|
||||
def self.on_pasta_created(content)
|
||||
# We do not modify stored content
|
||||
return content
|
||||
end
|
||||
|
||||
def self.on_pasta_read(content)
|
||||
|
||||
tokens = {
|
||||
|
||||
"orchid" => [/([0-9])/, /([t|T][r|R][u|U][e|E]|[f|F][a|A][l|L][s|S][e|E])/],
|
||||
|
||||
"palevioletred" => ['(', ')', '{', '}', '[', ']'],
|
||||
|
||||
"royalblue" => [/(\s(for|while|do|select|async|await|mut|break|continue|in|as|switch|let|fn|async|if|else|elseif|new|switch|match|case|default|public|protected|private|return|class|interface|static|final|const|var|int|integer|boolean|float|double|module|def|end|void))(?![a-z])/],
|
||||
|
||||
"mediumorchid" => [/(:|\.|;|=|>|<|\?|!|#|%|@|\^|&|\*|\|)/],
|
||||
|
||||
"mediumseagreen" => [/(\".*\")/, /(\'.*\')/]
|
||||
|
||||
};
|
||||
|
||||
tokens.each { | color, tokens |
|
||||
for token in tokens do
|
||||
if(token.class == String)
|
||||
content.gsub!(token, "$$#{color}$$" + token + "$$/#{color}$$")
|
||||
elsif
|
||||
content.gsub!(token, "$$#{color}$$" + '\1' + "$$/#{color}$$")
|
||||
end
|
||||
end
|
||||
};
|
||||
|
||||
tokens.each { | color, tokens |
|
||||
content.gsub!("$$#{color}$$", "<span style='color:#{color}'>");
|
||||
content.gsub!("$$/#{color}$$", "</span>");
|
||||
};
|
||||
|
||||
return content
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
78
plugins/MBPlugin.rb
Normal file
78
plugins/MBPlugin.rb
Normal file
|
@ -0,0 +1,78 @@
|
|||
require 'rutie'
|
||||
|
||||
module MB
|
||||
|
||||
class MBPlugin
|
||||
# Plugin Properties
|
||||
|
||||
def self.get_id()
|
||||
"[Enter Plugin ID]"
|
||||
end
|
||||
|
||||
def self.get_name()
|
||||
"[Eenter Plugin Name]"
|
||||
end
|
||||
|
||||
def self.get_version()
|
||||
"1.0.0"
|
||||
end
|
||||
|
||||
def self.get_author()
|
||||
"[Enter Author name]"
|
||||
end
|
||||
|
||||
def self.get_webpage()
|
||||
"[Enter Web URL]"
|
||||
end
|
||||
|
||||
def self.get_description()
|
||||
"[Enter Description]"
|
||||
end
|
||||
|
||||
# Plugin Event Hooks
|
||||
|
||||
def self.init()
|
||||
raise "Operation not supported";
|
||||
end
|
||||
|
||||
def self.on_pasta_deleted(id, content, created, expiration, file)
|
||||
raise "Operation not supported";
|
||||
end
|
||||
|
||||
def self.on_pasta_expired(id, content, created, expiration, file)
|
||||
raise "Operation not supported";
|
||||
end
|
||||
|
||||
def self.on_pasta_created(id, content, created, expiration, file)
|
||||
raise "Operation not supported";
|
||||
end
|
||||
|
||||
def self.on_pasta_read(id, content, created, expiration, file)
|
||||
raise "Operation not supported";
|
||||
end
|
||||
|
||||
# Rust Function Calls
|
||||
|
||||
def self.init()
|
||||
raise "Operation not supported";
|
||||
end
|
||||
|
||||
def self.P=on_pasta_deleted(id, content, created, expiration, file)
|
||||
raise "Operation not supported";
|
||||
end
|
||||
|
||||
def self.on_pasta_expired(id, content, created, expiration, file)
|
||||
raise "Operation not supported";
|
||||
end
|
||||
|
||||
def self.on_pasta_created(id, content, created, expiration, file)
|
||||
raise "Operation not supported";
|
||||
end
|
||||
|
||||
def self.on_pasta_read(id, content, created, expiration, file)
|
||||
raise "Operation not supported";
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
42
plugins/helloWorld.rb
Normal file
42
plugins/helloWorld.rb
Normal file
|
@ -0,0 +1,42 @@
|
|||
module MBP
|
||||
class HelloWorld < MBPlugin
|
||||
|
||||
def self.get_id()
|
||||
"HelloWorld"
|
||||
end
|
||||
|
||||
def self.get_name()
|
||||
"Hello World Plugin"
|
||||
end
|
||||
|
||||
def self.get_version()
|
||||
"1.0.0"
|
||||
end
|
||||
|
||||
def self.get_description()
|
||||
"This is just a demo plugin. It does not do anything."
|
||||
end
|
||||
|
||||
def self.get_author()
|
||||
"Daniel Szabo"
|
||||
end
|
||||
|
||||
def self.get_webpage()
|
||||
"https://github.com/szabodanika/microbin"
|
||||
end
|
||||
|
||||
def self.init()
|
||||
# Ignore event
|
||||
"OK"
|
||||
end
|
||||
|
||||
def self.on_pasta_created(content)
|
||||
return content
|
||||
end
|
||||
|
||||
def self.on_pasta_read(content)
|
||||
return content
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -24,7 +24,7 @@ pub fn to_animal_names(mut number: u64) -> String {
|
|||
number -= digit * ANIMAL_NAMES.len().pow(power) as u64;
|
||||
if power > 0 {
|
||||
power -= 1;
|
||||
} else if power <= 0 || number == 0 {
|
||||
} else if power == 0 || number == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
32
src/main.rs
32
src/main.rs
|
@ -29,6 +29,7 @@ use crate::pasta::Pasta;
|
|||
mod animalnumbers;
|
||||
mod dbio;
|
||||
mod pasta;
|
||||
mod plugins;
|
||||
|
||||
lazy_static! {
|
||||
static ref ARGS: Args = Args::parse();
|
||||
|
@ -47,6 +48,12 @@ struct Args {
|
|||
#[clap(short, long, default_value_t = 1)]
|
||||
threads: u8,
|
||||
|
||||
#[clap(short, long)]
|
||||
wide: bool,
|
||||
|
||||
#[clap(short, long, default_value_t = 3)]
|
||||
animals: u8,
|
||||
|
||||
#[clap(long)]
|
||||
hide_header: bool,
|
||||
|
||||
|
@ -103,7 +110,7 @@ struct ErrorTemplate<'a> {
|
|||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "pasta.html")]
|
||||
#[template(path = "pasta.html", escape = "none")]
|
||||
struct PastaTemplate<'a> {
|
||||
pasta: &'a Pasta,
|
||||
args: &'a Args,
|
||||
|
@ -165,7 +172,8 @@ async fn create(data: web::Data<AppState>, mut payload: Multipart) -> Result<Htt
|
|||
}
|
||||
"content" => {
|
||||
while let Some(chunk) = field.try_next().await? {
|
||||
new_pasta.content = std::str::from_utf8(&chunk).unwrap().to_string();
|
||||
new_pasta.content =
|
||||
plugins::on_pasta_created(std::str::from_utf8(&chunk).unwrap());
|
||||
new_pasta.pasta_type = if is_valid_url(new_pasta.content.as_str()) {
|
||||
String::from("url")
|
||||
} else {
|
||||
|
@ -223,9 +231,23 @@ async fn getpasta(data: web::Data<AppState>, id: web::Path<String>) -> HttpRespo
|
|||
|
||||
for pasta in pastas.iter() {
|
||||
if pasta.id == id {
|
||||
return HttpResponse::Found()
|
||||
.content_type("text/html")
|
||||
.body(PastaTemplate { pasta, args: &ARGS }.render().unwrap());
|
||||
let pasta_copy = Pasta {
|
||||
id: pasta.id,
|
||||
content: plugins::on_pasta_read(&pasta.content),
|
||||
file: pasta.file.to_string(),
|
||||
created: pasta.created,
|
||||
pasta_type: pasta.pasta_type.to_string(),
|
||||
expiration: pasta.expiration,
|
||||
};
|
||||
|
||||
return HttpResponse::Found().content_type("text/html").body(
|
||||
PastaTemplate {
|
||||
pasta: &pasta_copy,
|
||||
args: &ARGS,
|
||||
}
|
||||
.render()
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
134
src/plugins.rs
Normal file
134
src/plugins.rs
Normal file
|
@ -0,0 +1,134 @@
|
|||
extern crate rutie;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use log::{error, log};
|
||||
use rutie::{AnyException, AnyObject, Object, RString, VM};
|
||||
use std::fs::File;
|
||||
use std::io::{BufReader, Read};
|
||||
use std::{fs, io};
|
||||
|
||||
const CACHE_PLUGINS: bool = false;
|
||||
|
||||
lazy_static! {
|
||||
static ref PLUGIN_IDENTIFIERS: Vec<String> = init();
|
||||
}
|
||||
|
||||
fn init() -> Vec<String> {
|
||||
VM::init();
|
||||
|
||||
let plugin_paths = load_plugin_paths();
|
||||
|
||||
let plugin_codes = read_plugins(plugin_paths.clone());
|
||||
|
||||
feed_plugins(plugin_codes);
|
||||
|
||||
let identifiers = get_plugin_identifiers(plugin_paths);
|
||||
|
||||
init_plugins(&identifiers);
|
||||
|
||||
identifiers
|
||||
}
|
||||
|
||||
pub fn pasta_filter(s: &str) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub fn on_pasta_read(s: &str) -> String {
|
||||
let mut processed_content: String = String::from(s);
|
||||
|
||||
for PLUGIN_IDENTIFIER in PLUGIN_IDENTIFIERS.iter() {
|
||||
processed_content = eval_for_string(PLUGIN_IDENTIFIER, "on_pasta_read", s);
|
||||
}
|
||||
|
||||
processed_content
|
||||
}
|
||||
|
||||
pub fn on_pasta_created(s: &str) -> String {
|
||||
let mut processed_content: String = String::from(s);
|
||||
|
||||
for PLUGIN_IDENTIFIER in PLUGIN_IDENTIFIERS.iter() {
|
||||
processed_content = eval_for_string(PLUGIN_IDENTIFIER, "on_pasta_created", s);
|
||||
}
|
||||
|
||||
processed_content
|
||||
}
|
||||
|
||||
pub fn init_plugins(plugin_identifiers: &Vec<String>) {
|
||||
for PLUGIN_IDENTIFIER in plugin_identifiers.iter() {
|
||||
eval_for_string(PLUGIN_IDENTIFIER, "init", "");
|
||||
|
||||
let init_result = eval_for_string(&PLUGIN_IDENTIFIER, "init", "");
|
||||
let id = eval_for_string(&PLUGIN_IDENTIFIER, "get_id", "");
|
||||
let name = eval_for_string(&id, "get_name", "");
|
||||
let version = eval_for_string(&id, "get_version", "");
|
||||
|
||||
log::info!("Initialised plugin {id} - {name} ({version})");
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_for_string(plugin_id: &str, function: &str, parameter: &str) -> String {
|
||||
match VM::eval(&*format!("MBP::{}::{}({})", plugin_id, function, parameter)) {
|
||||
Ok(result) => match result.try_convert_to::<RString>() {
|
||||
Ok(ruby_string) => ruby_string.to_string(),
|
||||
Err(err) => err.to_string(),
|
||||
},
|
||||
Err(err) => {
|
||||
log::error!(
|
||||
"Failed to run function '{}' on plugin {}: {}",
|
||||
function,
|
||||
plugin_id,
|
||||
err
|
||||
);
|
||||
err.to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_plugin_paths() -> Vec<String> {
|
||||
let paths = fs::read_dir("./plugins").expect("Failed to access ./plugins library.");
|
||||
|
||||
let mut plugin_paths: Vec<String> = Vec::new();
|
||||
|
||||
for path in paths {
|
||||
plugin_paths.push(path.unwrap().path().to_str().unwrap().parse().unwrap());
|
||||
}
|
||||
|
||||
plugin_paths
|
||||
}
|
||||
|
||||
fn read_plugins(plugin_paths: Vec<String>) -> Vec<String> {
|
||||
let mut plugin_codes: Vec<String> = Vec::new();
|
||||
|
||||
for plugin_path in plugin_paths {
|
||||
let plugin_code = match fs::read_to_string(&plugin_path) {
|
||||
Ok(result) => result,
|
||||
Err(err) => {
|
||||
log::error!("Failed to read plugin file {}: {}", plugin_path, err);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
plugin_codes.push(plugin_code);
|
||||
}
|
||||
|
||||
plugin_codes
|
||||
}
|
||||
|
||||
fn feed_plugins(plugin_codes: Vec<String>) {
|
||||
for plugin_code in plugin_codes {
|
||||
match VM::eval(plugin_code.as_str()) {
|
||||
Ok(result) => {}
|
||||
Err(error) => {
|
||||
log::error!("Failed to initialise plugin: {}", error);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_plugin_identifiers(plugin_paths: Vec<String>) -> Vec<String> {
|
||||
let mut plugin_ids: Vec<String> = Vec::new();
|
||||
for plugin_path in plugin_paths {
|
||||
plugin_ids.push(plugin_path.replace("./plugins/", "").replace(".rb", ""))
|
||||
}
|
||||
plugin_ids
|
||||
}
|
Loading…
Reference in a new issue