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::templates::Template;
mod store;
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 parking_lot::RwLock;
use crate::session::{Session, SessionID, SessionStore};
use rocket::request::Form;
use rocket::response::Redirect;
use rocket::{State, Request};
use rocket::{Request, State};
use std::env;
use crate::session::{SessionID, SessionStore, Session};
#[derive(Serialize, Debug)]
pub struct ListContext<'a> {
@ -31,7 +34,7 @@ pub struct ListContext<'a> {
pub cards: Vec<RenderedCard<'a>>,
pub page: usize,
pub pages: usize,
pub count : usize,
pub count: usize,
}
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>")]
fn route_index(
store: State<RwLock<Store>>,
session : Session,
page: Option<usize>
) -> Template {
fn route_index(store: State<RwLock<Store>>, session: Session, page: Option<usize>) -> Template {
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;
session.set("foo.bar.count", count);

@ -1,18 +1,21 @@
use rocket::request::FromRequest;
use rocket::{Outcome, Request, State, http::{Status, Cookies, Cookie}, Response, Data, Rocket};
use std::ops::{Deref, DerefMut};
use std::collections::HashMap;
use json_dotpath::DotPaths;
use parking_lot::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard};
use serde_json::{Value, Map};
use rocket::fairing::{self, Fairing, Info};
use rand::Rng;
use std::borrow::Cow;
use serde::{Deserialize, Serialize};
use serde::de::DeserializeOwned;
use json_dotpath::DotPaths;
use rocket::fairing::{self, Fairing, Info};
use rocket::request::FromRequest;
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>;
@ -41,10 +44,12 @@ impl<'a, 'r> FromRequest<'a, 'r> for &'a SessionID {
SessionID(cookie.value().to_string()) // FIXME avoid cloning
} else {
println!("new id");
SessionID(rand::thread_rng()
.sample_iter(&rand::distributions::Alphanumeric)
.take(16)
.collect())
SessionID(
rand::thread_rng()
.sample_iter(&rand::distributions::Alphanumeric)
.take(16)
.collect(),
)
}
}))
}
@ -53,7 +58,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for &'a SessionID {
#[derive(Debug)]
pub struct Session<'a> {
store: State<'a, SessionStore>,
id : &'a SessionID,
id: &'a SessionID,
}
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) {
SessionID(cookie.value().to_string())
} else {
SessionID(rand::thread_rng()
.sample_iter(&rand::distributions::Alphanumeric)
.take(16)
.collect())
SessionID(
rand::thread_rng()
.sample_iter(&rand::distributions::Alphanumeric)
.take(16)
.collect(),
)
}
}),
store: request.guard().unwrap()
store: request.guard().unwrap(),
})
}
}
@ -81,7 +88,7 @@ impl<'a> Session<'a> {
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();
if let Some(ses) = rg.get(&self.id.0) {
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)
}
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)
}
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()
}
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();
if let Some(ses) = wg.get_mut(&self.id.0) {
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();
if let Some(ses) = wg.get_mut(&self.id.0) {
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();
if let Some(ses) = wg.get_mut(&self.id.0) {
ses.data.dot_set(path, value);
} else {
let mut map = Map::new();
map.dot_set(path, value);
wg.insert(self.id.0.clone(), SessionInstance {
data : map,
});
wg.insert(self.id.0.clone(), SessionInstance { data: map });
}
}
pub fn remove(&self, path : &str) {
pub fn remove(&self, path: &str) {
let mut wg = self.store.inner.write();
if let Some(ses) = wg.get_mut(&self.id.0) {
ses.data.dot_remove(path);
@ -148,7 +153,7 @@ impl Fairing for SessionFairing {
fn info(&self) -> Info {
Info {
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) {
let session = request.local_cache(|| {
SessionID("".to_string())
});
let session = request.local_cache(|| SessionID("".to_string()));
if !session.0.is_empty() {
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::collections::BTreeSet;
use lazy_static::lazy_static;
use indexmap::map::IndexMap;
use lazy_static::lazy_static;
use rocket::request::{FormItems, FromForm};
lazy_static! {
@ -194,7 +194,6 @@ pub fn render_card_fields<'a>(
.collect()
}
#[derive(Default)]
pub struct MapFromForm {
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 json_dotpath::DotPaths;
use serde::Serialize;
use serde_json::Value;
use std::collections::HashMap;
use std::collections::{BTreeMap, BTreeSet};
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::collections::{BTreeMap, BTreeSet};
use json_dotpath::DotPaths;
pub mod form;
pub mod model;
@ -17,7 +17,7 @@ pub mod model;
pub struct Store {
path: PathBuf,
pub model: Model,
freeform_fields : FreeformFieldsOfInterest,
freeform_fields: FreeformFieldsOfInterest,
pub data: Cards,
pub index: Indexes,
}
@ -68,10 +68,14 @@ impl Store {
/// Handle a data change.
/// 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 {
// 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();
@ -123,22 +127,22 @@ impl Store {
/// 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)
fn get_fields_for_freeform_indexes(model : &Model) -> FreeformFieldsOfInterest {
fn get_fields_for_freeform_indexes(model: &Model) -> FreeformFieldsOfInterest {
// tuples (key, group)
let mut free_enum_fields : Vec<KeyAndGroup> = vec![];
let mut free_tag_fields : Vec<KeyAndGroup> = vec![];
let mut free_enum_fields: Vec<KeyAndGroup> = vec![];
let mut free_tag_fields: Vec<KeyAndGroup> = vec![];
for (key, field) in &model.fields {
match &field.kind {
FieldKind::FreeEnum { enum_group } => {
let enum_group = enum_group.as_ref().unwrap_or(key);
free_enum_fields.push(KeyAndGroup(key.to_string(), enum_group.to_string()));
},
}
FieldKind::FreeTags { tag_group } => {
let tag_group = tag_group.as_ref().unwrap_or(key);
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
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 {
if !index.free_enums.contains_key(key.as_str()) {
index.free_enums.insert(key.to_string(), Default::default());
@ -194,14 +202,15 @@ struct FreeformFieldsOfInterest {
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()
.write(true).create(true).truncate(true)
.write(true)
.create(true)
.truncate(true)
.open(path)
.expect("Error opening data file for writing.");
file.write(bytes)
.expect("Error write data file");
file.write(bytes).expect("Error write data file");
}
fn load_file(path: impl AsRef<Path>) -> String {

Loading…
Cancel
Save