session-crate
Ondřej Hruška 4 years ago
parent d24af144fa
commit fdbc7947f6
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 21
      src/main.rs
  2. 75
      src/session.rs
  3. 3
      src/store/form.rs
  4. 43
      src/store/mod.rs

@ -12,18 +12,21 @@ use serde_json::Value;
use rocket_contrib::serve::StaticFiles; use rocket_contrib::serve::StaticFiles;
use rocket_contrib::templates::Template; use rocket_contrib::templates::Template;
mod store;
mod session; mod session;
mod store;
use crate::store::form::{render_card_fields, render_empty_fields, RenderedCard, RenderedField, MapFromForm, collect_card_form}; use crate::store::form::{
collect_card_form, render_card_fields, render_empty_fields, MapFromForm, RenderedCard,
RenderedField,
};
use crate::store::Store; use crate::store::Store;
use parking_lot::RwLock; use parking_lot::RwLock;
use crate::session::{Session, SessionID, SessionStore};
use rocket::request::Form; use rocket::request::Form;
use rocket::response::Redirect; use rocket::response::Redirect;
use rocket::{State, Request}; use rocket::{Request, State};
use std::env; use std::env;
use crate::session::{SessionID, SessionStore, Session};
#[derive(Serialize, Debug)] #[derive(Serialize, Debug)]
pub struct ListContext<'a> { pub struct ListContext<'a> {
@ -31,7 +34,7 @@ pub struct ListContext<'a> {
pub cards: Vec<RenderedCard<'a>>, pub cards: Vec<RenderedCard<'a>>,
pub page: usize, pub page: usize,
pub pages: usize, pub pages: usize,
pub count : usize, pub count: usize,
} }
const PER_PAGE: usize = 20; // TODO configurable const PER_PAGE: usize = 20; // TODO configurable
@ -51,14 +54,10 @@ fn find_page_with_card(store: &Store, card_id: usize) -> Option<usize> {
} }
#[get("/?<page>")] #[get("/?<page>")]
fn route_index( fn route_index(store: State<RwLock<Store>>, session: Session, page: Option<usize>) -> Template {
store: State<RwLock<Store>>,
session : Session,
page: Option<usize>
) -> Template {
let rg = store.read(); let rg = store.read();
let mut count : usize = session.get_or_default("foo.bar.count"); let mut count: usize = session.get_or_default("foo.bar.count");
count += 1; count += 1;
session.set("foo.bar.count", count); session.set("foo.bar.count", count);

@ -1,18 +1,21 @@
use rocket::request::FromRequest; use json_dotpath::DotPaths;
use rocket::{Outcome, Request, State, http::{Status, Cookies, Cookie}, Response, Data, Rocket};
use std::ops::{Deref, DerefMut};
use std::collections::HashMap;
use parking_lot::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard}; use parking_lot::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard};
use serde_json::{Value, Map};
use rocket::fairing::{self, Fairing, Info};
use rand::Rng; use rand::Rng;
use std::borrow::Cow; use rocket::fairing::{self, Fairing, Info};
use serde::{Deserialize, Serialize}; use rocket::request::FromRequest;
use serde::de::DeserializeOwned;
use json_dotpath::DotPaths;
use rocket::response::ResponseBuilder; use rocket::response::ResponseBuilder;
use rocket::{
http::{Cookie, Cookies, Status},
Data, Outcome, Request, Response, Rocket, State,
};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use std::borrow::Cow;
use std::collections::HashMap;
use std::ops::{Deref, DerefMut};
const SESSION_ID : &'static str = "SESSID"; const SESSION_ID: &'static str = "SESSID";
type SessionsMap = HashMap<String, SessionInstance>; type SessionsMap = HashMap<String, SessionInstance>;
@ -41,10 +44,12 @@ impl<'a, 'r> FromRequest<'a, 'r> for &'a SessionID {
SessionID(cookie.value().to_string()) // FIXME avoid cloning SessionID(cookie.value().to_string()) // FIXME avoid cloning
} else { } else {
println!("new id"); println!("new id");
SessionID(rand::thread_rng() SessionID(
.sample_iter(&rand::distributions::Alphanumeric) rand::thread_rng()
.take(16) .sample_iter(&rand::distributions::Alphanumeric)
.collect()) .take(16)
.collect(),
)
} }
})) }))
} }
@ -53,7 +58,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for &'a SessionID {
#[derive(Debug)] #[derive(Debug)]
pub struct Session<'a> { pub struct Session<'a> {
store: State<'a, SessionStore>, store: State<'a, SessionStore>,
id : &'a SessionID, id: &'a SessionID,
} }
impl<'a, 'r> FromRequest<'a, 'r> for Session<'a> { impl<'a, 'r> FromRequest<'a, 'r> for Session<'a> {
@ -65,13 +70,15 @@ impl<'a, 'r> FromRequest<'a, 'r> for Session<'a> {
if let Some(cookie) = request.cookies().get(SESSION_ID) { if let Some(cookie) = request.cookies().get(SESSION_ID) {
SessionID(cookie.value().to_string()) SessionID(cookie.value().to_string())
} else { } else {
SessionID(rand::thread_rng() SessionID(
.sample_iter(&rand::distributions::Alphanumeric) rand::thread_rng()
.take(16) .sample_iter(&rand::distributions::Alphanumeric)
.collect()) .take(16)
.collect(),
)
} }
}), }),
store: request.guard().unwrap() store: request.guard().unwrap(),
}) })
} }
} }
@ -81,7 +88,7 @@ impl<'a> Session<'a> {
SessionFairing SessionFairing
} }
pub fn get<T : DeserializeOwned>(&self, path : &str) -> Option<T> { pub fn get<T: DeserializeOwned>(&self, path: &str) -> Option<T> {
let rg = self.store.inner.read(); let rg = self.store.inner.read();
if let Some(ses) = rg.get(&self.id.0) { if let Some(ses) = rg.get(&self.id.0) {
ses.data.dot_get(path) ses.data.dot_get(path)
@ -90,19 +97,19 @@ impl<'a> Session<'a> {
} }
} }
pub fn get_or<T : DeserializeOwned>(&self, path : &str, def : T) -> T { pub fn get_or<T: DeserializeOwned>(&self, path: &str, def: T) -> T {
self.get(path).unwrap_or(def) self.get(path).unwrap_or(def)
} }
pub fn get_or_else<T : DeserializeOwned, F : FnOnce() -> T>(&self, path : &str, def : F) -> T { pub fn get_or_else<T: DeserializeOwned, F: FnOnce() -> T>(&self, path: &str, def: F) -> T {
self.get(path).unwrap_or_else(def) self.get(path).unwrap_or_else(def)
} }
pub fn get_or_default<T : DeserializeOwned + Default>(&self, path : &str) -> T { pub fn get_or_default<T: DeserializeOwned + Default>(&self, path: &str) -> T {
self.get(path).unwrap_or_default() self.get(path).unwrap_or_default()
} }
pub fn take<T : DeserializeOwned>(&self, path : &str) -> Option<T> { pub fn take<T: DeserializeOwned>(&self, path: &str) -> Option<T> {
let mut wg = self.store.inner.write(); let mut wg = self.store.inner.write();
if let Some(ses) = wg.get_mut(&self.id.0) { if let Some(ses) = wg.get_mut(&self.id.0) {
ses.data.dot_take(path) ses.data.dot_take(path)
@ -111,7 +118,7 @@ impl<'a> Session<'a> {
} }
} }
pub fn replace<O: DeserializeOwned, N: Serialize>(&self, path : &str, new : N) -> Option<O> { pub fn replace<O: DeserializeOwned, N: Serialize>(&self, path: &str, new: N) -> Option<O> {
let mut wg = self.store.inner.write(); let mut wg = self.store.inner.write();
if let Some(ses) = wg.get_mut(&self.id.0) { if let Some(ses) = wg.get_mut(&self.id.0) {
ses.data.dot_replace(path, new) ses.data.dot_replace(path, new)
@ -120,20 +127,18 @@ impl<'a> Session<'a> {
} }
} }
pub fn set<T : Serialize>(&self, path : &str, value : T) { pub fn set<T: Serialize>(&self, path: &str, value: T) {
let mut wg = self.store.inner.write(); let mut wg = self.store.inner.write();
if let Some(ses) = wg.get_mut(&self.id.0) { if let Some(ses) = wg.get_mut(&self.id.0) {
ses.data.dot_set(path, value); ses.data.dot_set(path, value);
} else { } else {
let mut map = Map::new(); let mut map = Map::new();
map.dot_set(path, value); map.dot_set(path, value);
wg.insert(self.id.0.clone(), SessionInstance { wg.insert(self.id.0.clone(), SessionInstance { data: map });
data : map,
});
} }
} }
pub fn remove(&self, path : &str) { pub fn remove(&self, path: &str) {
let mut wg = self.store.inner.write(); let mut wg = self.store.inner.write();
if let Some(ses) = wg.get_mut(&self.id.0) { if let Some(ses) = wg.get_mut(&self.id.0) {
ses.data.dot_remove(path); ses.data.dot_remove(path);
@ -148,7 +153,7 @@ impl Fairing for SessionFairing {
fn info(&self) -> Info { fn info(&self) -> Info {
Info { Info {
name: "Session Fairing", name: "Session Fairing",
kind: fairing::Kind::Attach | fairing::Kind::Response kind: fairing::Kind::Attach | fairing::Kind::Response,
} }
} }
@ -157,9 +162,7 @@ impl Fairing for SessionFairing {
} }
fn on_response<'r>(&self, request: &'r Request, response: &mut Response) { fn on_response<'r>(&self, request: &'r Request, response: &mut Response) {
let session = request.local_cache(|| { let session = request.local_cache(|| SessionID("".to_string()));
SessionID("".to_string())
});
if !session.0.is_empty() { if !session.0.is_empty() {
response.adjoin_header(Cookie::build(SESSION_ID, session.0.clone()).finish()); response.adjoin_header(Cookie::build(SESSION_ID, session.0.clone()).finish());

@ -4,8 +4,8 @@ use serde_json::Value;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::BTreeSet; use std::collections::BTreeSet;
use lazy_static::lazy_static;
use indexmap::map::IndexMap; use indexmap::map::IndexMap;
use lazy_static::lazy_static;
use rocket::request::{FormItems, FromForm}; use rocket::request::{FormItems, FromForm};
lazy_static! { lazy_static! {
@ -194,7 +194,6 @@ pub fn render_card_fields<'a>(
.collect() .collect()
} }
#[derive(Default)] #[derive(Default)]
pub struct MapFromForm { pub struct MapFromForm {
pub data: IndexMap<String, String>, pub data: IndexMap<String, String>,

@ -1,13 +1,13 @@
use crate::store::model::{Model, FieldKind}; use crate::store::model::{FieldKind, Model};
use indexmap::map::IndexMap; use indexmap::map::IndexMap;
use json_dotpath::DotPaths;
use serde::Serialize; use serde::Serialize;
use serde_json::Value; use serde_json::Value;
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::{BTreeMap, BTreeSet};
use std::fs::{File, OpenOptions}; use std::fs::{File, OpenOptions};
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::collections::{BTreeMap, BTreeSet};
use json_dotpath::DotPaths;
pub mod form; pub mod form;
pub mod model; pub mod model;
@ -17,7 +17,7 @@ pub mod model;
pub struct Store { pub struct Store {
path: PathBuf, path: PathBuf,
pub model: Model, pub model: Model,
freeform_fields : FreeformFieldsOfInterest, freeform_fields: FreeformFieldsOfInterest,
pub data: Cards, pub data: Cards,
pub index: Indexes, pub index: Indexes,
} }
@ -68,10 +68,14 @@ impl Store {
/// Handle a data change. /// Handle a data change.
/// If a card was modified, selectively update the tags index /// If a card was modified, selectively update the tags index
fn on_change(&mut self, changed_card : Option<usize>) { fn on_change(&mut self, changed_card: Option<usize>) {
if let Some(id) = changed_card { if let Some(id) = changed_card {
// this needs to be so ugly because of lifetimes - we need a mutable reference to the index // this needs to be so ugly because of lifetimes - we need a mutable reference to the index
Self::index_card(&mut self.index, &self.freeform_fields, self.data.cards.get(&id).unwrap()); Self::index_card(
&mut self.index,
&self.freeform_fields,
self.data.cards.get(&id).unwrap(),
);
} }
self.persist(); self.persist();
@ -123,22 +127,22 @@ impl Store {
/// Get a list of free_tags and free_enum fields /// Get a list of free_tags and free_enum fields
/// ///
/// Returns (free_tags, free_enums), where both members are vecs of (field_key, index_group) /// Returns (free_tags, free_enums), where both members are vecs of (field_key, index_group)
fn get_fields_for_freeform_indexes(model : &Model) -> FreeformFieldsOfInterest { fn get_fields_for_freeform_indexes(model: &Model) -> FreeformFieldsOfInterest {
// tuples (key, group) // tuples (key, group)
let mut free_enum_fields : Vec<KeyAndGroup> = vec![]; let mut free_enum_fields: Vec<KeyAndGroup> = vec![];
let mut free_tag_fields : Vec<KeyAndGroup> = vec![]; let mut free_tag_fields: Vec<KeyAndGroup> = vec![];
for (key, field) in &model.fields { for (key, field) in &model.fields {
match &field.kind { match &field.kind {
FieldKind::FreeEnum { enum_group } => { FieldKind::FreeEnum { enum_group } => {
let enum_group = enum_group.as_ref().unwrap_or(key); let enum_group = enum_group.as_ref().unwrap_or(key);
free_enum_fields.push(KeyAndGroup(key.to_string(), enum_group.to_string())); free_enum_fields.push(KeyAndGroup(key.to_string(), enum_group.to_string()));
}, }
FieldKind::FreeTags { tag_group } => { FieldKind::FreeTags { tag_group } => {
let tag_group = tag_group.as_ref().unwrap_or(key); let tag_group = tag_group.as_ref().unwrap_or(key);
free_tag_fields.push(KeyAndGroup(key.to_string(), tag_group.to_string())); free_tag_fields.push(KeyAndGroup(key.to_string(), tag_group.to_string()));
}, }
_ => {}, _ => {}
} }
} }
@ -149,7 +153,11 @@ impl Store {
} }
/// This is an associated function to split the lifetimes /// This is an associated function to split the lifetimes
fn index_card<'a>(index : &mut Indexes, freeform_fields : &FreeformFieldsOfInterest, card : &'a Value) { fn index_card<'a>(
index: &mut Indexes,
freeform_fields: &FreeformFieldsOfInterest,
card: &'a Value,
) {
for KeyAndGroup(key, group) in &freeform_fields.free_enum_fields { for KeyAndGroup(key, group) in &freeform_fields.free_enum_fields {
if !index.free_enums.contains_key(key.as_str()) { if !index.free_enums.contains_key(key.as_str()) {
index.free_enums.insert(key.to_string(), Default::default()); index.free_enums.insert(key.to_string(), Default::default());
@ -194,14 +202,15 @@ struct FreeformFieldsOfInterest {
pub free_enum_fields: Vec<KeyAndGroup>, pub free_enum_fields: Vec<KeyAndGroup>,
} }
fn write_file(path : impl AsRef<Path>, bytes : &[u8]) { fn write_file(path: impl AsRef<Path>, bytes: &[u8]) {
let mut file = OpenOptions::new() let mut file = OpenOptions::new()
.write(true).create(true).truncate(true) .write(true)
.create(true)
.truncate(true)
.open(path) .open(path)
.expect("Error opening data file for writing."); .expect("Error opening data file for writing.");
file.write(bytes) file.write(bytes).expect("Error write data file");
.expect("Error write data file");
} }
fn load_file(path: impl AsRef<Path>) -> String { fn load_file(path: impl AsRef<Path>) -> String {

Loading…
Cancel
Save