diff --git a/Cargo.lock b/Cargo.lock index 87f98a4..8ca8fa1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -836,7 +836,6 @@ dependencies = [ "rsasl", "rust-argon2", "rustls", - "sdk", "serde", "serde_dhall", "serde_json", @@ -878,6 +877,13 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "dummy" +version = "0.1.0" +dependencies = [ + "sdk", +] + [[package]] name = "either" version = "1.6.1" @@ -2104,8 +2110,19 @@ dependencies = [ name = "sdk" version = "0.1.0" dependencies = [ - "async-trait", + "diflouroborane", "futures-util", + "sdk-proc", +] + +[[package]] +name = "sdk-proc" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "trybuild", ] [[package]] @@ -2410,6 +2427,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + [[package]] name = "tracing" version = "0.1.29" @@ -2499,6 +2525,20 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "trybuild" +version = "1.0.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d664de8ea7e531ad4c0f5a834f20b8cb2b8e6dfe88d05796ee7887518ed67b9" +dependencies = [ + "glob", + "lazy_static", + "serde", + "serde_json", + "termcolor", + "toml", +] + [[package]] name = "typed-arena" version = "1.7.0" diff --git a/Cargo.toml b/Cargo.toml index 5666a4c..23bc316 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,8 +25,6 @@ path = "bin/bffhd/main.rs" [dependencies] libc = "0.2.101" - -sdk = { path = "modules/sdk", default-features = false } lazy_static = "1.4.0" uuid = { version = "0.8.2", features = ["serde", "v4"] } async-trait = "0.1.51" diff --git a/bffhd/initiators/mod.rs b/bffhd/initiators/mod.rs index 1996985..06ffaf9 100644 --- a/bffhd/initiators/mod.rs +++ b/bffhd/initiators/mod.rs @@ -6,18 +6,48 @@ use async_oneshot as oneshot; use futures_signals::signal::Signal; use futures_util::future::BoxFuture; use smol::future::FutureExt; -use sdk::initiators::{Initiator, InitiatorError, UpdateError, UpdateSink, UserID, ResourceID}; use crate::resource::{Error, Update}; +use crate::resource::claim::{ResourceID, UserID}; +use crate::resource::state::State; + +pub enum UpdateError { + /// We're not connected to anything anymore. You can't do anything about this error and the + /// only reason why you even get it is because your future was called a last time before + /// being shelved so best way to handle this error is to just return from your loop entirely, + /// cleaning up any state that doesn't survive a freeze. + Closed, + + Denied, + + Other(Box), +} + +pub trait InitiatorError: std::error::Error + Send { +} + +pub trait Initiator { + fn start_for(&mut self, machine: ResourceID) + -> BoxFuture<'static, Result<(), Box>>; + + fn run(&mut self, request: &mut UpdateSink) + -> BoxFuture<'static, Result<(), Box>>; +} #[derive(Clone)] -pub struct BffhUpdateSink { - tx: channel::Sender<(Option, sdk::initiators::State)>, +pub struct UpdateSink { + tx: channel::Sender<(Option, State)>, rx: channel::Receiver>, } -#[async_trait::async_trait] -impl UpdateSink for BffhUpdateSink { - async fn send(&mut self, userid: Option, state: sdk::initiators::State) +impl UpdateSink { + fn new(tx: channel::Sender<(Option, State)>, + rx: channel::Receiver>) + -> Self + { + Self { tx, rx } + } + + async fn send(&mut self, userid: Option, state: State) -> Result<(), UpdateError> { if let Err(_e) = self.tx.send((userid, state)).await { @@ -34,15 +64,6 @@ impl UpdateSink for BffhUpdateSink { } } -impl BffhUpdateSink { - fn new(tx: channel::Sender<(Option, sdk::initiators::State)>, - rx: channel::Receiver>) - -> Self - { - Self { tx, rx } - } -} - struct Resource; pub struct InitiatorDriver { resource_signal: S, @@ -51,8 +72,8 @@ pub struct InitiatorDriver { initiator: I, initiator_future: Option>>>, - update_sink: BffhUpdateSink, - initiator_req_rx: channel::Receiver<(Option, sdk::initiators::State)>, + update_sink: UpdateSink, + initiator_req_rx: channel::Receiver<(Option, State)>, initiator_reply_tx: channel::Sender>, } @@ -65,7 +86,7 @@ impl, I: Initiator> InitiatorDriver { pub fn new(resource_signal: S, initiator: I) -> Self { let (initiator_reply_tx, initiator_reply_rx) = channel::bounded(1); let (initiator_req_tx, initiator_req_rx) = async_channel::bounded(1); - let update_sink = BffhUpdateSink::new(initiator_req_tx, initiator_reply_rx); + let update_sink = UpdateSink::new(initiator_req_tx, initiator_reply_rx); Self { resource: None, resource_signal, diff --git a/bffhd/users/db.rs b/bffhd/users/db.rs index 72920dc..f28e275 100644 --- a/bffhd/users/db.rs +++ b/bffhd/users/db.rs @@ -1,4 +1,6 @@ +use std::collections::HashSet; use std::sync::Arc; +use lmdb::{RwTransaction, Transaction}; use crate::db::{RawDB, DB, AllocAdapter, Environment, Result}; use crate::db::{DatabaseFlags, LMDBorrow, RoTransaction, WriteFlags, }; use super::User; @@ -61,4 +63,60 @@ impl UserDB { Ok(out) } +} + +pub struct UserIndex { + env: Arc, + usernames: RawDB, + roles: RawDB, +} + +impl UserIndex { + pub fn update(&self, old: &User, new: &User) -> Result<()> { + assert_eq!(old.id, new.id); + let mut txn = self.env.begin_rw_txn()?; + if old.username != new.username { + self.update_username(&mut txn, new.id, &old.username, &new.username)?; + } + + let mut to_remove: HashSet<&String> = old.roles.iter().collect(); + let mut to_add: HashSet<&String> = HashSet::new(); + for role in new.roles.iter() { + // If a role wasn't found in the old ones it's a new one that's being added + if !to_remove.remove(role) { + to_add.insert(role); + } + // Otherwise it's in both sets so we just ignore it. + } + + self.update_roles(&mut txn, new.id, to_remove, to_add)?; + txn.commit()?; + Ok(()) + } + + fn update_username(&self, txn: &mut RwTransaction, uid: u128, old: &String, new: &String) + -> Result<()> + { + let flags = WriteFlags::empty(); + self.usernames.del(txn, &old.as_bytes(), Some(&uid.to_ne_bytes()))?; + self.usernames.put(txn, &new.as_bytes(), &uid.to_ne_bytes(), flags)?; + Ok(()) + } + + fn update_roles(&self, + txn: &mut RwTransaction, + uid: u128, + remove: HashSet<&String>, + add: HashSet<&String> + ) -> Result<()> + { + let flags = WriteFlags::empty(); + for role in remove.iter() { + self.roles.del(txn, &role.as_bytes(), Some(&uid.to_ne_bytes()))?; + } + for role in add.iter() { + self.roles.put(txn, &role.as_bytes(), &uid.to_ne_bytes(), flags)?; + } + Ok(()) + } } \ No newline at end of file diff --git a/bffhd/users/mod.rs b/bffhd/users/mod.rs index 8e67674..6e7e4fa 100644 --- a/bffhd/users/mod.rs +++ b/bffhd/users/mod.rs @@ -1,3 +1,11 @@ +/* + * Copyright (c) 2021. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + use rkyv::{Archive, Serialize, Deserialize}; use capnp::capability::Promise; @@ -26,7 +34,9 @@ pub struct User { } impl User { - + pub fn new(id: u128, username: String, roles: Vec) -> Self { + User { id, username, roles } + } } impl info::Server for User { diff --git a/modules/sdk/Cargo.toml b/modules/sdk/Cargo.toml index 9cb2c61..33c6d31 100644 --- a/modules/sdk/Cargo.toml +++ b/modules/sdk/Cargo.toml @@ -6,5 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -async-trait = "0.1.51" +sdk-proc = { path = "sdk_proc" } futures-util = "0.3" +diflouroborane = { path = "../.." } \ No newline at end of file diff --git a/modules/sdk/sdk_proc/src/lib.rs b/modules/sdk/sdk_proc/src/lib.rs new file mode 100644 index 0000000..2c78d5d --- /dev/null +++ b/modules/sdk/sdk_proc/src/lib.rs @@ -0,0 +1,87 @@ +use proc_macro::TokenStream; +use std::sync::Mutex; +use quote::{format_ident, quote}; +use syn::{braced, parse_macro_input, Field, Ident, Token, Visibility, Type}; +use syn::parse::{Parse, ParseStream}; +use syn::punctuated::Punctuated; +use syn::token::Brace; + +mod keywords { + syn::custom_keyword!(initiator); + syn::custom_keyword!(actor); + syn::custom_keyword!(sensor); +} + +enum ModuleAttrs { + Nothing, + Initiator, + Actor, + Sensor, +} + +impl Parse for ModuleAttrs { + fn parse(input: ParseStream) -> syn::Result { + if input.is_empty() { + Ok(ModuleAttrs::Nothing) + } else { + let lookahead = input.lookahead1(); + if lookahead.peek(keywords::initiator) { + Ok(ModuleAttrs::Initiator) + } else if lookahead.peek(keywords::actor) { + Ok(ModuleAttrs::Actor) + } else if lookahead.peek(keywords::sensor) { + Ok(ModuleAttrs::Sensor) + } else { + Err(input.error("Module type must be empty or one of \"initiator\", \"actor\", or \ + \"sensor\"")) + } + } + } +} + +struct ModuleInput { + pub ident: Ident, + pub fields: Punctuated, +} + +impl Parse for ModuleInput { + fn parse(input: ParseStream) -> syn::Result { + let lookahead = input.lookahead1(); + if lookahead.peek(Token![pub]) { + let _vis: Visibility = input.parse()?; + } + if input.parse::().is_err() { + return Err(input.error("Modules must be structs")); + } + let ident = input.parse::()?; + + let lookahead = input.lookahead1(); + if !lookahead.peek(Brace) { + return Err(input.error("Modules can't be unit structs")); + } + + let content; + braced!(content in input); + Ok(Self { + ident, + fields: content.parse_terminated(Field::parse_named)?, + }) + } +} + +#[proc_macro_attribute] +pub fn module(attr: TokenStream, tokens: TokenStream) -> TokenStream { + let attrs = parse_macro_input!(attr as ModuleAttrs); + let item = parse_macro_input!(tokens as ModuleInput); + + let output = { + let ident = item.ident; + let fields = item.fields.iter(); + quote! { + pub struct #ident { + #(#fields),* + } + } + }; + output.into() +} \ No newline at end of file diff --git a/modules/sdk/sdk_proc/tests/01-parse-struct.rs b/modules/sdk/sdk_proc/tests/01-parse-struct.rs new file mode 100644 index 0000000..994136e --- /dev/null +++ b/modules/sdk/sdk_proc/tests/01-parse-struct.rs @@ -0,0 +1,10 @@ +// Check if the proc macro for modules exists and is correctly imported from top level + +use sdk_proc::module; + +#[module] +pub struct Module { +} + + +fn main() {} \ No newline at end of file diff --git a/modules/sdk/sdk_proc/tests/02-not-or-bad-struct.rs b/modules/sdk/sdk_proc/tests/02-not-or-bad-struct.rs new file mode 100644 index 0000000..9e995d5 --- /dev/null +++ b/modules/sdk/sdk_proc/tests/02-not-or-bad-struct.rs @@ -0,0 +1,11 @@ + + +#[sdk_proc::module] +enum EnumModule { + +} + +#[sdk_proc::module] +struct UnitStructModule; + +fn main() {} \ No newline at end of file diff --git a/modules/sdk/src/initiators/mod.rs b/modules/sdk/src/initiators/mod.rs index ac9dfca..0dbd5ea 100644 --- a/modules/sdk/src/initiators/mod.rs +++ b/modules/sdk/src/initiators/mod.rs @@ -1,35 +1,10 @@ -use async_trait::async_trait; -use futures_util::future::BoxFuture; +pub use diflouroborane::{ + initiators::{ + UpdateSink, + UpdateError, -pub struct State; -pub struct UserID; -pub struct ResourceID; -pub struct Error; - -pub enum UpdateError { - /// We're not connected to anything anymore. You can't do anything about this error and the - /// only reason why you even get it is because your future was called a last time before - /// being shelved so best way to handle this error is to just return from your loop entirely, - /// cleaning up any state that doesn't survive a freeze. - Closed, - - Denied, - - Other(Box), -} - -#[async_trait] -pub trait UpdateSink: Send { - async fn send(&mut self, userid: Option, state: State) -> Result<(), UpdateError>; -} - -pub trait InitiatorError: std::error::Error + Send { -} - -pub trait Initiator { - fn start_for(&mut self, machine: ResourceID) - -> BoxFuture<'static, Result<(), Box>>; - - fn run(&mut self, request: &mut impl UpdateSink) - -> BoxFuture<'static, Result<(), Box>>; -} + Initiator, + InitiatorError, + }, + resource::claim::ResourceID, +}; diff --git a/modules/sdk/src/lib.rs b/modules/sdk/src/lib.rs index e750ce4..18e1564 100644 --- a/modules/sdk/src/lib.rs +++ b/modules/sdk/src/lib.rs @@ -1,7 +1,20 @@ #[forbid(private_in_public)] +pub use sdk_proc::module; + +pub use futures_util::future::BoxFuture; pub mod initiators; +pub const VERSION_STRING: &'static str = env!("CARGO_PKG_VERSION"); +pub const VERSION_STRING_PARTS: (&'static str, &'static str, &'static str, &'static str) = ( + env!("CARGO_PKG_VERSION_MAJOR"), + env!("CARGO_PKG_VERSION_MINOR"), + env!("CARGO_PKG_VERSION_PATCH"), + env!("CARGO_PKG_VERSION_PRE"), +); + +pub static VERSION_MAJOR: u32 = 0; + #[cfg(test)] mod tests { #[test]