Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[valor-plugin] Add support for structs #9

Merged
merged 1 commit into from
Nov 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions examples/hello-plugin/src/functions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//! Simple macro example for functions

use valor::*;

#[vlugin]
fn hello_plugin(_req: Request) -> Response {
"Hello Plugin!".into()
}

8 changes: 2 additions & 6 deletions examples/hello-plugin/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,2 @@
use valor::*;

#[vlugin]
fn hello_plugin(_req: Request) -> Response {
"Hello Plugin!".into()
}
pub mod functions;
pub mod structures;
14 changes: 14 additions & 0 deletions examples/hello-plugin/src/structures.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//! Simple macro example for structures

use valor::*;

#[vlugin]
#[derive(Default)]
struct MyHandler;

impl RequestHandler for MyHandler {
fn handle_request(&self, _: Request) -> HandlerResponse {
unimplemented!()
}
}

42 changes: 39 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
//! Valor

use http_types::Body;
pub use http_types::{Method, Request, Response, StatusCode, Url};
use registry::PluginRegistry;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
Expand All @@ -24,6 +27,7 @@ type Result = std::result::Result<Response, Response>;
pub struct Handler(Arc<PluginRegistry>);

impl Handler {
/// Creates a new `Handler` instance
pub fn new(loader: Arc<impl Loader>) -> Self {
let registry = PluginRegistry::new();
let (plugin, handler) = registry.clone().as_handler(loader);
Expand Down Expand Up @@ -60,7 +64,18 @@ impl Clone for Handler {
}
}

impl fmt::Debug for Handler
where
for<'a> dyn RequestHandler + 'a: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Handler").field(&self.0).finish()
}
}

/// Loader
pub trait Loader: Send + Sync + 'static {
/// Loads the given `plugin`
fn load(&self, plugin: &Plugin) -> std::result::Result<Box<dyn RequestHandler>, ()>;
}

Expand All @@ -71,9 +86,12 @@ pub(crate) fn res(status: StatusCode, msg: impl Into<Body>) -> Response {
res
}

/// Handler response
pub type HandlerResponse = Pin<Box<dyn Future<Output = Response> + Send>>;

/// Request handler
pub trait RequestHandler: Send + Sync {
/// Handles the request
fn handle_request(&self, request: Request) -> HandlerResponse;
}

Expand All @@ -86,13 +104,31 @@ where
}
}

/// Plugin
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Plugin {
BuiltIn { name: String },
/// Built in
BuiltIn {
/// Name
name: String,
},
/// Dummy
Dummy,
Native { name: String, path: Option<String> },
WebWorker { name: String, url: Url },
/// Native
Native {
/// Name
name: String,
/// Path
path: Option<String>,
},
/// Web worker
WebWorker {
/// Name
name: String,
/// Url
url: Url,
},
}

impl Plugin {
Expand Down
13 changes: 13 additions & 0 deletions src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::{res, HandlerResponse, Loader, Method, Plugin, Request, RequestHandle
use path_tree::PathTree;
use serde_json as json;
use std::collections::HashMap;
use std::fmt;
use std::iter::Iterator;
use std::sync::{Arc, Mutex};

Expand Down Expand Up @@ -82,3 +83,15 @@ impl PluginRegistry {
)
}
}

impl fmt::Debug for PluginRegistry
where
for<'a> dyn RequestHandler + 'a: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PluginRegistry")
.field("plugins", &self.plugins)
.field("routes", &self.routes)
.finish()
}
}
2 changes: 1 addition & 1 deletion valor-native/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ impl Loader for DynLoader {
let lib = Library::new(path).map_err(|_| ())?;

debug!("loading native plugin {}", path);
let get_request_handler: Symbol<fn() -> _> =
let get_request_handler: Symbol<'_, fn() -> _> =
unsafe { lib.get(b"get_request_handler") }.map_err(|_| ())?;
debug!("symbol {:?}", plugin);

Expand Down
2 changes: 2 additions & 0 deletions valor-native/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Valor vlupin

use kv_log_macro::{error, info};
use loader::DynLoader;
use std::sync::Arc;
Expand Down
2 changes: 1 addition & 1 deletion valor-vlugin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ proc-macro = true
[dependencies]
proc-macro2 = "1.0.24"
quote = "1.0.7"
syn = { version = "1.0.48", features = ["full"] }
syn = { version = "1.0.48", features = ["full", "parsing"] }
1 change: 1 addition & 0 deletions valor-vlugin/README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# Vlugin

"pluging" was taken so now we have Vlugins!. This helper crate is a procedural macro that annotates a function to be the entry point of a plugin that handles requests.
56 changes: 41 additions & 15 deletions valor-vlugin/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,59 @@
//! Valor vlugin

use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{parse_macro_input, Ident, ItemFn};
use syn::{Error, ItemFn, ItemStruct};

/// vlugin
#[proc_macro_attribute]
pub fn vlugin(_attr: TokenStream, input: TokenStream) -> TokenStream {
let func = parse_macro_input!(input as ItemFn);
let name = func.sig.ident.clone();
pub fn vlugin(_attr: TokenStream, item: TokenStream) -> TokenStream {
let ts2: proc_macro2::TokenStream = item.into();

let handler = sync_req_handler(name);
if let Ok(rslt) = syn::parse2::<ItemFn>(ts2.clone()) {
return handle_item_fn(rslt).into();
}

if let Ok(rslt) = syn::parse2::<ItemStruct>(ts2) {
return handle_item_struct(rslt).into();
}

Error::new(
proc_macro2::Span::mixed_site(),
"Only functions and structures that implement `RequestHandler` are currently supported",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😄 👍🏽

)
.to_compile_error()
.into()
}

fn handle_item_fn(item: ItemFn) -> TokenStream2 {
let name = item.sig.ident.clone();

let plugin_def = quote! {
/// Handler
#[no_mangle]
pub extern "Rust" fn get_request_handler() -> Box<dyn valor::RequestHandler> {
sync_req_handler()
Box::new(|req| Box::pin(async { #name(req) }) as valor::HandlerResponse)
}

#handler

#func
#item
};

plugin_def.into()
}

fn sync_req_handler(handler: Ident) -> TokenStream2 {
quote! {
#[inline]
fn sync_req_handler() -> Box<dyn valor::RequestHandler> {
Box::new(|req| Box::pin(async { #handler(req) }) as valor::HandlerResponse)
fn handle_item_struct(item: ItemStruct) -> TokenStream2 {
let name = &item.ident;

let plugin_def = quote! {
/// Handler
#[no_mangle]
pub extern "Rust" fn get_request_handler() -> Box<dyn valor::RequestHandler> {
Box::new(#name::default())
}
}

#item
};

plugin_def.into()
}
7 changes: 5 additions & 2 deletions valor-web/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! Valor web

#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
static ALLOC: wee_alloc::WeeAlloc<'_> = wee_alloc::WeeAlloc::INIT;

use js_sys::{Array, ArrayBuffer, Object, Reflect, Uint8Array};
use std::rc::Rc;
Expand Down Expand Up @@ -39,6 +41,7 @@ impl From<JsRequest> for Request {
}
}

/// Run
#[wasm_bindgen(start)]
pub async fn run() -> Result<(), JsValue> {
init_log();
Expand Down Expand Up @@ -87,7 +90,7 @@ fn load_service_worker(url: &str) -> Result<(), JsValue> {
struct Loader;

impl valor::Loader for Loader {
fn load(&self, plugin: &Plugin) -> std::result::Result<Box<dyn RequestHandler>, ()> {
fn load(&self, plugin: &Plugin) -> Result<Box<dyn RequestHandler>, ()> {
match plugin {
Plugin::WebWorker { .. } => todo!(),
_ => unreachable!(),
Expand Down