Flat file database editor and browser with web interface
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
rocket-inv/src/main.rs

192 lines
4.6 KiB

#![feature(proc_macro_hygiene, decl_macro)]
#[macro_use]
extern crate rocket;
#[macro_use]
extern crate serde;
use serde_json::Value;
//use rocket::request::FromSegments;
//use rocket::http::uri::Segments;
use rocket_contrib::serve::StaticFiles;
use rocket_contrib::templates::Template;
mod store;
use crate::store::form::{render_card_fields, render_empty_fields, RenderedCard, RenderedField, MapFromForm, collect_card_form};
use crate::store::Store;
use parking_lot::RwLock;
use rocket::request::Form;
use rocket::response::Redirect;
use rocket::State;
use std::env;
#[derive(Serialize, Debug)]
pub struct ListContext<'a> {
pub fields: Vec<RenderedField<'a>>,
pub cards: Vec<RenderedCard<'a>>,
pub page: usize,
pub pages: usize,
}
const PER_PAGE: usize = 20; // TODO configurable
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)
{
Some(n / PER_PAGE)
} else {
None
}
}
#[get("/?<page>")]
fn route_index(store: State<RwLock<Store>>, page: Option<usize>) -> Template {
let rg = store.read();
let mut page = page.unwrap_or_default();
let n_pages = (rg.data.cards.len() as f64 / PER_PAGE as f64).ceil() as usize;
if page >= n_pages {
page = n_pages - 1;
}
let context = ListContext {
fields: render_empty_fields(&rg),
pages: n_pages,
page,
cards: rg
.data
.cards
.iter()
.skip(page * PER_PAGE)
.take(PER_PAGE)
.filter_map(|(id, card)| {
if let Value::Object(ref map) = card {
Some(RenderedCard {
fields: render_card_fields(&rg, map),
id: *id,
})
} else {
None
}
})
.collect(),
};
Template::render("index", context)
}
#[derive(Serialize)]
struct AddCardContext<'a> {
pub fields: Vec<RenderedField<'a>>,
}
#[derive(Serialize)]
struct EditCardContext<'a> {
pub fields: Vec<RenderedField<'a>>,
pub id: usize,
}
#[get("/add")]
fn route_add(store: State<RwLock<Store>>) -> Template {
let rg = store.read();
let context = AddCardContext {
fields: render_empty_fields(&rg),
};
Template::render("add", context)
}
#[post("/add", data = "<form>")]
fn route_add_save(form: Form<MapFromForm>, store: State<RwLock<Store>>) -> Redirect {
let mut wg = store.write();
let card = collect_card_form(&wg, form.into_inner());
let id = wg.add_card(card);
let page = find_page_with_card(&wg, id).unwrap_or(0);
Redirect::found(uri!(route_index: page))
}
#[get("/edit/<id>")]
fn route_edit(id: usize, store: State<RwLock<Store>>) -> Option<Template> {
let rg = store.read();
if let Some(Value::Object(ref map)) = rg.data.cards.get(&id) {
let context = EditCardContext {
fields: render_card_fields(&rg, map),
id,
};
Some(Template::render("edit", context))
} else {
None
}
}
#[post("/edit/<id>", data = "<form>")]
fn route_edit_save(
id: usize,
form: Form<MapFromForm>,
store: State<RwLock<Store>>,
) -> Option<Redirect> {
let mut wg = store.write();
let card = collect_card_form(&wg, form.into_inner());
wg.update_card(id, card);
let page = find_page_with_card(&wg, id).unwrap_or(0);
Some(Redirect::found(uri!(route_index: page)))
}
#[get("/delete/<id>")]
fn route_delete(id: usize, store: State<RwLock<Store>>) -> Redirect {
let mut wg = store.write();
// must find page before deleting
let page = find_page_with_card(&wg, id).unwrap_or(0);
wg.delete_card(id);
Redirect::found(uri!(route_index: page))
}
#[get("/maintenance/reindex")]
fn route_reindex(store: State<RwLock<Store>>) -> Redirect {
let mut wg = store.write();
wg.rebuild_indexes();
wg.persist();
Redirect::found(uri!(route_index: _))
}
fn main() {
let cwd = env::current_dir().unwrap();
let data_dir = cwd.join("data");
let store = Store::new(data_dir);
rocket::ignite()
.attach(Template::fairing())
.manage(RwLock::new(store))
.mount("/", StaticFiles::from(cwd.join("templates/static/")))
.mount(
"/",
routes![
route_index,
route_add,
route_add_save,
route_edit,
route_edit_save,
route_delete,
route_reindex,
],
)
.launch();
}