1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
//! This module keeps track of al the Memory related abstraction
//! Memory is considered the layer that keeps track of the exercise, submission, user...
//! It's not important how it is implemented, or how the data are structured. It has to respond to some simple queries in the Memory trait.
//!
//! The Memory trait is actualy formed by two traits: StatelessMemory and StateMemory.
//! This is needed to expose StateMemory only when it is actualy possible to specify the state.
//!
use std::{any::TypeId, error::Error, fmt::Debug, marker::PhantomData};
pub use async_trait::async_trait;
use chrono::{DateTime, Utc};
use crate::prelude::*;
use private::Privatizer;
mod private {
/// should remain private, it's needed to privatize what we doesn't want to implement outside this module
pub trait Privatizer {}
}
/// A valid UserVariant
pub trait UserState: Debug + Privatizer + Clone {}
#[derive(Debug, Clone)]
/// Admin variant, used as a type-state-machine
pub struct Admin;
impl Privatizer for Admin {}
impl UserState for Admin {}
#[derive(Debug, Clone)]
/// Authenticated variant, used as a type-state-machine
pub struct Authenticated;
impl Privatizer for Authenticated {}
impl UserState for Authenticated {}
#[derive(Debug, Clone)]
/// Unauthenticated variant, used as a type-state-machine
pub struct Unauthenticated;
impl Privatizer for Unauthenticated {}
impl UserState for Unauthenticated {}
#[derive(Debug, Clone)]
/// An user, the variant S represent the type of user (if it is Authenticated, Admin, or Unauthenticated).
pub struct User<S: UserState> {
/// Phantom data used to save the variant
pub ph: PhantomData<S>,
/// Univoque identification of a user
pub user_id: i64,
/// Univoque Username of a user
pub username: String,
/// Hashed password
pub password_hash: String,
/// When did it log-in last time?
pub logged_in_time: Option<DateTime<Utc>>,
/// Which token should use to authenticate
pub logged_in_token: Option<String>,
/// is an admin? Aka can connect to Admin-only parts?
pub is_admin: bool,
}
impl<S: UserState> User<S> {
/// WARNING: THIS FUNCTION DOESN'T CHECK IF IS POSSIBLE TO CONVERT, incorrect usage will lead to insecurities
pub fn transmute<S2: UserState>(self) -> User<S2> {
User {
ph: PhantomData,
user_id: self.user_id,
username: self.username,
password_hash: self.password_hash,
logged_in_time: self.logged_in_time,
logged_in_token: self.logged_in_token,
is_admin: self.is_admin,
}
}
}
#[async_trait]
/// This is the trait that contains all method of the memory that does not require knowing the state
pub trait StatelessMemory: Sync + Send {
//USERS
/// register User, it should not authenticate it
async fn register(
&self,
username: &str,
password: &str,
) -> Result<User<Unauthenticated>, Box<dyn Error>>;
/// try to log in the relative user
async fn login(
&self,
username: &str,
password: &str,
) -> Result<User<Authenticated>, Box<dyn Error>>;
/// search an user from his username
async fn get_by_username(
&self,
username: &str,
) -> Result<User<Unauthenticated>, Box<dyn Error>>;
/// get the authenticated user from his token
async fn get_authenticate(&self, token: &str) -> Result<User<Authenticated>, Box<dyn Error>>;
/// get an authenticated admin from his token
async fn get_admin(&self, token: &str) -> Result<User<Admin>, Box<dyn Error>>;
/// prints out all users
async fn get_all_users(&self) -> Result<Vec<User<Unauthenticated>>, Box<dyn Error>>;
///list exercises names
async fn list_exercise_names(&self) -> Result<Vec<String>, Box<dyn Error>>;
///add submission (on success returns submission id)
async fn add_submission(
&self,
exercise_name: String,
source: String,
user: User<Authenticated>,
) -> Result<i64, Box<dyn Error + Send + Sync>>;
///add exercise result
async fn add_exercise_result(
&self,
submission_id: i64,
user: User<Authenticated>,
result: ExerciseResult,
) -> Result<(), Box<dyn Error + Send + Sync>>;
}
#[async_trait]
/// This is the trait that contains all method of the memory that does require knowing the state.
/// Is not always available
pub trait StateMemory<S: ExecutorGlobalState> {
/// used to enable a particular executor
async fn enable_executor(
&self,
input: &S,
output: &S,
data: String,
) -> Result<(), Box<dyn Error + Send + Sync + 'static>>;
/// from a particular state, which executor will be triggered? in which order?
async fn get_execution_plan(
&self,
input: &S,
) -> Result<Vec<(TypeId, TypeId, String)>, Box<dyn Error + Send + Sync + 'static>>;
/// add an exercise to memory
async fn add_exercise(
&self,
name: String,
exercise_type: S,
source: String,
) -> Result<(), Box<dyn Error + Send + Sync + 'static>>;
/// get an exercise from memory
/// type, source
async fn get_exercise(
&self,
name: String,
) -> Result<(TypeId, String), Box<dyn Error + Send + Sync + 'static>>;
}
/// auto trait that rapresent the union of stateless and state Memory
pub trait Memory<S: ExecutorGlobalState>: StateMemory<S> + StatelessMemory {
/// conversion into a StatelessMemory
fn as_stateless(&self) -> &dyn StatelessMemory;
}
impl<S: ExecutorGlobalState, Cur: StateMemory<S> + StatelessMemory> Memory<S> for Cur {
fn as_stateless(&self) -> &dyn StatelessMemory {
self
}
}
#[test]
fn test_typesafeness() {
let _t: Option<Box<dyn StatelessMemory>> = None;
}