|
|
@ -5,7 +5,7 @@ extern crate rocket; |
|
|
|
#[macro_use] |
|
|
|
#[macro_use] |
|
|
|
extern crate serde; |
|
|
|
extern crate serde; |
|
|
|
|
|
|
|
|
|
|
|
use serde_json::{json, Value, Number}; |
|
|
|
use serde_json::{json, Number, Value}; |
|
|
|
|
|
|
|
|
|
|
|
//use rocket::request::FromSegments;
|
|
|
|
//use rocket::request::FromSegments;
|
|
|
|
//use rocket::http::uri::Segments;
|
|
|
|
//use rocket::http::uri::Segments;
|
|
|
@ -14,17 +14,17 @@ use rocket_contrib::templates::Template; |
|
|
|
|
|
|
|
|
|
|
|
mod store; |
|
|
|
mod store; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
use crate::store::form::{render_card_fields, render_empty_fields, RenderedCard, RenderedField}; |
|
|
|
|
|
|
|
use crate::store::model::FieldKind; |
|
|
|
use crate::store::Store; |
|
|
|
use crate::store::Store; |
|
|
|
use rocket::{State, Data}; |
|
|
|
use indexmap::map::IndexMap; |
|
|
|
use parking_lot::RwLock; |
|
|
|
use parking_lot::RwLock; |
|
|
|
use rocket::response::Redirect; |
|
|
|
|
|
|
|
use rocket::http::Status; |
|
|
|
use rocket::http::Status; |
|
|
|
use rocket::request::{Form, LenientForm, FromForm, FormItems}; |
|
|
|
use rocket::request::{Form, FormItems, FromForm, LenientForm}; |
|
|
|
|
|
|
|
use rocket::response::Redirect; |
|
|
|
|
|
|
|
use rocket::{Data, State}; |
|
|
|
use std::env; |
|
|
|
use std::env; |
|
|
|
use crate::store::form::{RenderedField, render_empty_fields, RenderedCard, render_card_fields}; |
|
|
|
|
|
|
|
use crate::store::model::FieldKind; |
|
|
|
|
|
|
|
use std::ops::Deref; |
|
|
|
use std::ops::Deref; |
|
|
|
use indexmap::map::IndexMap; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Serialize, Debug)] |
|
|
|
#[derive(Serialize, Debug)] |
|
|
|
pub struct ListContext<'a> { |
|
|
|
pub struct ListContext<'a> { |
|
|
@ -36,8 +36,14 @@ pub struct ListContext<'a> { |
|
|
|
|
|
|
|
|
|
|
|
const per_page: usize = 20; |
|
|
|
const per_page: usize = 20; |
|
|
|
|
|
|
|
|
|
|
|
fn find_page_with_card(store : &Store, card_id : usize) -> Option<usize> { |
|
|
|
fn find_page_with_card(store: &Store, card_id: usize) -> Option<usize> { |
|
|
|
if let Some((n, _)) = store.data.cards.iter().enumerate().find(|(_n, (id, _card))| **id == card_id) { |
|
|
|
if let Some((n, _)) = store |
|
|
|
|
|
|
|
.data |
|
|
|
|
|
|
|
.cards |
|
|
|
|
|
|
|
.iter() |
|
|
|
|
|
|
|
.enumerate() |
|
|
|
|
|
|
|
.find(|(_n, (id, _card))| **id == card_id) |
|
|
|
|
|
|
|
{ |
|
|
|
Some(n / per_page) |
|
|
|
Some(n / per_page) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
None |
|
|
|
None |
|
|
@ -48,7 +54,6 @@ fn find_page_with_card(store : &Store, card_id : usize) -> Option<usize> { |
|
|
|
fn route_index(store: State<RwLock<Store>>, page: Option<usize>, card: Option<usize>) -> Template { |
|
|
|
fn route_index(store: State<RwLock<Store>>, page: Option<usize>, card: Option<usize>) -> Template { |
|
|
|
let rg = store.read(); |
|
|
|
let rg = store.read(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let mut page = page.unwrap_or_default(); |
|
|
|
let mut page = page.unwrap_or_default(); |
|
|
|
let n_pages = (rg.data.cards.len() as f64 / per_page as f64).ceil() as usize; |
|
|
|
let n_pages = (rg.data.cards.len() as f64 / per_page as f64).ceil() as usize; |
|
|
|
|
|
|
|
|
|
|
@ -64,7 +69,10 @@ fn route_index(store: State<RwLock<Store>>, page: Option<usize>, card: Option<us |
|
|
|
fields: render_empty_fields(&rg), |
|
|
|
fields: render_empty_fields(&rg), |
|
|
|
pages: n_pages, |
|
|
|
pages: n_pages, |
|
|
|
page, |
|
|
|
page, |
|
|
|
cards: rg.data.cards.iter() |
|
|
|
cards: rg |
|
|
|
|
|
|
|
.data |
|
|
|
|
|
|
|
.cards |
|
|
|
|
|
|
|
.iter() |
|
|
|
.skip(page * per_page) |
|
|
|
.skip(page * per_page) |
|
|
|
.take(per_page) |
|
|
|
.take(per_page) |
|
|
|
.filter_map(|(id, card)| { |
|
|
|
.filter_map(|(id, card)| { |
|
|
@ -76,7 +84,8 @@ fn route_index(store: State<RwLock<Store>>, page: Option<usize>, card: Option<us |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
None |
|
|
|
None |
|
|
|
} |
|
|
|
} |
|
|
|
}).collect(), |
|
|
|
}) |
|
|
|
|
|
|
|
.collect(), |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Template::render("index", context) |
|
|
|
Template::render("index", context) |
|
|
@ -84,7 +93,7 @@ fn route_index(store: State<RwLock<Store>>, page: Option<usize>, card: Option<us |
|
|
|
|
|
|
|
|
|
|
|
#[derive(Default)] |
|
|
|
#[derive(Default)] |
|
|
|
struct MapFromForm { |
|
|
|
struct MapFromForm { |
|
|
|
pub data: IndexMap<String, String> |
|
|
|
pub data: IndexMap<String, String>, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl<'a> FromForm<'a> for MapFromForm { |
|
|
|
impl<'a> FromForm<'a> for MapFromForm { |
|
|
@ -100,7 +109,7 @@ impl<'a> FromForm<'a> for MapFromForm { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn collect_card_form(store: &Store, mut form: MapFromForm) -> IndexMap::<String, Value> { |
|
|
|
fn collect_card_form(store: &Store, mut form: MapFromForm) -> IndexMap<String, Value> { |
|
|
|
let mut card = IndexMap::new(); |
|
|
|
let mut card = IndexMap::new(); |
|
|
|
|
|
|
|
|
|
|
|
for (k, field) in &store.model.fields { |
|
|
|
for (k, field) in &store.model.fields { |
|
|
@ -110,9 +119,7 @@ fn collect_card_form(store: &Store, mut form: MapFromForm) -> IndexMap::<String, |
|
|
|
FieldKind::Text | FieldKind::String | FieldKind::FreeEnum { .. } => { |
|
|
|
FieldKind::Text | FieldKind::String | FieldKind::FreeEnum { .. } => { |
|
|
|
Value::String(input) |
|
|
|
Value::String(input) |
|
|
|
} |
|
|
|
} |
|
|
|
FieldKind::Bool { .. } => { |
|
|
|
FieldKind::Bool { .. } => serde_json::to_value(true).unwrap(), |
|
|
|
serde_json::to_value(true).unwrap() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
FieldKind::Int { min, max, default } => { |
|
|
|
FieldKind::Int { min, max, default } => { |
|
|
|
let mut val: i64 = if input.is_empty() { |
|
|
|
let mut val: i64 = if input.is_empty() { |
|
|
|
*default |
|
|
|
*default |
|
|
@ -151,20 +158,19 @@ fn collect_card_form(store: &Store, mut form: MapFromForm) -> IndexMap::<String, |
|
|
|
if options.contains(&input) { |
|
|
|
if options.contains(&input) { |
|
|
|
Value::String(input) |
|
|
|
Value::String(input) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
let val = default |
|
|
|
let val = default.as_ref().map(ToOwned::to_owned).unwrap_or_else(|| { |
|
|
|
.as_ref() |
|
|
|
options |
|
|
|
.map(ToOwned::to_owned) |
|
|
|
|
|
|
|
.unwrap_or_else(|| options |
|
|
|
|
|
|
|
.first() |
|
|
|
.first() |
|
|
|
.expect("fixed enum must have values") |
|
|
|
.expect("fixed enum must have values") |
|
|
|
.to_owned() |
|
|
|
.to_owned() |
|
|
|
); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
Value::String(val) |
|
|
|
Value::String(val) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
FieldKind::Tags { options } => { |
|
|
|
FieldKind::Tags { options } => { |
|
|
|
let tags: Vec<String> = input.split(' ') |
|
|
|
let tags: Vec<String> = input |
|
|
|
|
|
|
|
.split(' ') |
|
|
|
.map(ToOwned::to_owned) |
|
|
|
.map(ToOwned::to_owned) |
|
|
|
.filter_map(|tag| { |
|
|
|
.filter_map(|tag| { |
|
|
|
if options.contains(&tag) { |
|
|
|
if options.contains(&tag) { |
|
|
@ -172,12 +178,14 @@ fn collect_card_form(store: &Store, mut form: MapFromForm) -> IndexMap::<String, |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
None |
|
|
|
None |
|
|
|
} |
|
|
|
} |
|
|
|
}).collect(); |
|
|
|
}) |
|
|
|
|
|
|
|
.collect(); |
|
|
|
|
|
|
|
|
|
|
|
serde_json::to_value(tags).unwrap() |
|
|
|
serde_json::to_value(tags).unwrap() |
|
|
|
} |
|
|
|
} |
|
|
|
FieldKind::FreeTags { .. } => { |
|
|
|
FieldKind::FreeTags { .. } => { |
|
|
|
let tags: Vec<String> = input.split(' ') |
|
|
|
let tags: Vec<String> = input |
|
|
|
|
|
|
|
.split(' ') |
|
|
|
.map(str::trim) |
|
|
|
.map(str::trim) |
|
|
|
.filter(|s| !s.is_empty()) |
|
|
|
.filter(|s| !s.is_empty()) |
|
|
|
.map(ToOwned::to_owned) |
|
|
|
.map(ToOwned::to_owned) |
|
|
@ -216,7 +224,7 @@ fn route_add(store: State<RwLock<Store>>) -> Template { |
|
|
|
let rg = store.read(); |
|
|
|
let rg = store.read(); |
|
|
|
|
|
|
|
|
|
|
|
let context = AddCardContext { |
|
|
|
let context = AddCardContext { |
|
|
|
fields: render_empty_fields(&rg) |
|
|
|
fields: render_empty_fields(&rg), |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Template::render("add", context) |
|
|
|
Template::render("add", context) |
|
|
@ -249,7 +257,11 @@ fn route_edit(id: usize, store: State<RwLock<Store>>) -> Option<Template> { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[post("/edit/<id>", data = "<form>")] |
|
|
|
#[post("/edit/<id>", data = "<form>")] |
|
|
|
fn route_edit_save(id: usize, form: Form<MapFromForm>, store: State<RwLock<Store>>) -> Option<Redirect> { |
|
|
|
fn route_edit_save( |
|
|
|
|
|
|
|
id: usize, |
|
|
|
|
|
|
|
form: Form<MapFromForm>, |
|
|
|
|
|
|
|
store: State<RwLock<Store>>, |
|
|
|
|
|
|
|
) -> Option<Redirect> { |
|
|
|
let mut rg = store.write(); |
|
|
|
let mut rg = store.write(); |
|
|
|
|
|
|
|
|
|
|
|
let card = collect_card_form(&rg, form.into_inner()); |
|
|
|
let card = collect_card_form(&rg, form.into_inner()); |
|
|
@ -278,12 +290,16 @@ fn main() { |
|
|
|
.attach(Template::fairing()) |
|
|
|
.attach(Template::fairing()) |
|
|
|
.manage(RwLock::new(store)) |
|
|
|
.manage(RwLock::new(store)) |
|
|
|
.mount("/", StaticFiles::from(cwd.join("templates/static/"))) |
|
|
|
.mount("/", StaticFiles::from(cwd.join("templates/static/"))) |
|
|
|
.mount("/", routes![ |
|
|
|
.mount( |
|
|
|
route_index, |
|
|
|
"/", |
|
|
|
route_add, |
|
|
|
routes![ |
|
|
|
route_add_save, |
|
|
|
route_index, |
|
|
|
route_edit, |
|
|
|
route_add, |
|
|
|
route_edit_save, |
|
|
|
route_add_save, |
|
|
|
route_delete, |
|
|
|
route_edit, |
|
|
|
]).launch(); |
|
|
|
route_edit_save, |
|
|
|
|
|
|
|
route_delete, |
|
|
|
|
|
|
|
], |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
.launch(); |
|
|
|
} |
|
|
|
} |
|
|
|