auto format
This commit is contained in:
+54
-38
@@ -5,7 +5,7 @@ extern crate rocket;
|
||||
#[macro_use]
|
||||
extern crate serde;
|
||||
|
||||
use serde_json::{json, Value, Number};
|
||||
use serde_json::{json, Number, Value};
|
||||
|
||||
//use rocket::request::FromSegments;
|
||||
//use rocket::http::uri::Segments;
|
||||
@@ -14,17 +14,17 @@ use rocket_contrib::templates::Template;
|
||||
|
||||
mod store;
|
||||
|
||||
use crate::store::Store;
|
||||
use rocket::{State, Data};
|
||||
use parking_lot::RwLock;
|
||||
use rocket::response::Redirect;
|
||||
use rocket::http::Status;
|
||||
use rocket::request::{Form, LenientForm, FromForm, FormItems};
|
||||
use std::env;
|
||||
use crate::store::form::{RenderedField, render_empty_fields, RenderedCard, render_card_fields};
|
||||
use crate::store::form::{render_card_fields, render_empty_fields, RenderedCard, RenderedField};
|
||||
use crate::store::model::FieldKind;
|
||||
use std::ops::Deref;
|
||||
use crate::store::Store;
|
||||
use indexmap::map::IndexMap;
|
||||
use parking_lot::RwLock;
|
||||
use rocket::http::Status;
|
||||
use rocket::request::{Form, FormItems, FromForm, LenientForm};
|
||||
use rocket::response::Redirect;
|
||||
use rocket::{Data, State};
|
||||
use std::env;
|
||||
use std::ops::Deref;
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct ListContext<'a> {
|
||||
@@ -36,8 +36,14 @@ pub struct ListContext<'a> {
|
||||
|
||||
const per_page: usize = 20;
|
||||
|
||||
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) {
|
||||
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
|
||||
@@ -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 {
|
||||
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;
|
||||
|
||||
@@ -64,7 +69,10 @@ fn route_index(store: State<RwLock<Store>>, page: Option<usize>, card: Option<us
|
||||
fields: render_empty_fields(&rg),
|
||||
pages: n_pages,
|
||||
page,
|
||||
cards: rg.data.cards.iter()
|
||||
cards: rg
|
||||
.data
|
||||
.cards
|
||||
.iter()
|
||||
.skip(page * per_page)
|
||||
.take(per_page)
|
||||
.filter_map(|(id, card)| {
|
||||
@@ -76,7 +84,8 @@ fn route_index(store: State<RwLock<Store>>, page: Option<usize>, card: Option<us
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).collect(),
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
|
||||
Template::render("index", context)
|
||||
@@ -84,7 +93,7 @@ fn route_index(store: State<RwLock<Store>>, page: Option<usize>, card: Option<us
|
||||
|
||||
#[derive(Default)]
|
||||
struct MapFromForm {
|
||||
pub data: IndexMap<String, String>
|
||||
pub data: IndexMap<String, String>,
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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 { .. } => {
|
||||
Value::String(input)
|
||||
}
|
||||
FieldKind::Bool { .. } => {
|
||||
serde_json::to_value(true).unwrap()
|
||||
}
|
||||
FieldKind::Bool { .. } => serde_json::to_value(true).unwrap(),
|
||||
FieldKind::Int { min, max, default } => {
|
||||
let mut val: i64 = if input.is_empty() {
|
||||
*default
|
||||
@@ -151,20 +158,19 @@ fn collect_card_form(store: &Store, mut form: MapFromForm) -> IndexMap::<String,
|
||||
if options.contains(&input) {
|
||||
Value::String(input)
|
||||
} else {
|
||||
let val = default
|
||||
.as_ref()
|
||||
.map(ToOwned::to_owned)
|
||||
.unwrap_or_else(|| options
|
||||
let val = default.as_ref().map(ToOwned::to_owned).unwrap_or_else(|| {
|
||||
options
|
||||
.first()
|
||||
.expect("fixed enum must have values")
|
||||
.to_owned()
|
||||
);
|
||||
});
|
||||
|
||||
Value::String(val)
|
||||
}
|
||||
}
|
||||
FieldKind::Tags { options } => {
|
||||
let tags: Vec<String> = input.split(' ')
|
||||
let tags: Vec<String> = input
|
||||
.split(' ')
|
||||
.map(ToOwned::to_owned)
|
||||
.filter_map(|tag| {
|
||||
if options.contains(&tag) {
|
||||
@@ -172,12 +178,14 @@ fn collect_card_form(store: &Store, mut form: MapFromForm) -> IndexMap::<String,
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).collect();
|
||||
})
|
||||
.collect();
|
||||
|
||||
serde_json::to_value(tags).unwrap()
|
||||
}
|
||||
FieldKind::FreeTags { .. } => {
|
||||
let tags: Vec<String> = input.split(' ')
|
||||
let tags: Vec<String> = input
|
||||
.split(' ')
|
||||
.map(str::trim)
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(ToOwned::to_owned)
|
||||
@@ -216,7 +224,7 @@ fn route_add(store: State<RwLock<Store>>) -> Template {
|
||||
let rg = store.read();
|
||||
|
||||
let context = AddCardContext {
|
||||
fields: render_empty_fields(&rg)
|
||||
fields: render_empty_fields(&rg),
|
||||
};
|
||||
|
||||
Template::render("add", context)
|
||||
@@ -249,7 +257,11 @@ fn route_edit(id: usize, store: State<RwLock<Store>>) -> Option<Template> {
|
||||
}
|
||||
|
||||
#[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 card = collect_card_form(&rg, form.into_inner());
|
||||
@@ -278,12 +290,16 @@ fn main() {
|
||||
.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,
|
||||
]).launch();
|
||||
.mount(
|
||||
"/",
|
||||
routes![
|
||||
route_index,
|
||||
route_add,
|
||||
route_add_save,
|
||||
route_edit,
|
||||
route_edit_save,
|
||||
route_delete,
|
||||
],
|
||||
)
|
||||
.launch();
|
||||
}
|
||||
|
||||
+30
-17
@@ -1,7 +1,7 @@
|
||||
use crate::store::model::FieldKind;
|
||||
use crate::store::{model, Indexes, Store};
|
||||
use serde_json::Value;
|
||||
use std::borrow::Cow;
|
||||
use crate::store::{model, Indexes, Store};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
@@ -30,7 +30,7 @@ impl<'a> RenderedField<'a> {
|
||||
key: &'i String,
|
||||
field: &'i model::Field,
|
||||
value: Option<&'i Value>,
|
||||
index: &'i Indexes
|
||||
index: &'i Indexes,
|
||||
) -> RenderedField<'i> {
|
||||
let mut rendered = RenderedField::default();
|
||||
rendered.key = key.as_str().into();
|
||||
@@ -134,21 +134,23 @@ impl<'a> RenderedField<'a> {
|
||||
if let Some(Value::String(s)) = value {
|
||||
rendered.value = Cow::Borrowed(&s.as_str());
|
||||
}
|
||||
},
|
||||
}
|
||||
FieldKind::Enum { .. } | FieldKind::FreeEnum { .. } => {
|
||||
if let Some(Value::String(s)) = value {
|
||||
rendered.value = Cow::Borrowed(&s.as_str());
|
||||
}
|
||||
},
|
||||
}
|
||||
FieldKind::Tags { .. } | FieldKind::FreeTags { .. } => {
|
||||
if let Some(v) = value {
|
||||
rendered.value = serde_json::from_value::<Vec<String>>(v.clone())
|
||||
.unwrap().join(" ").into();
|
||||
.unwrap()
|
||||
.join(" ")
|
||||
.into();
|
||||
rendered.tags_json = serde_json::to_string(v).unwrap();
|
||||
} else {
|
||||
rendered.tags_json = "[]".into();
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -156,24 +158,35 @@ impl<'a> RenderedField<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize,Debug)]
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct RenderedCard<'a> {
|
||||
pub fields : Vec<RenderedField<'a>>,
|
||||
pub id : usize,
|
||||
pub fields: Vec<RenderedField<'a>>,
|
||||
pub id: usize,
|
||||
}
|
||||
|
||||
pub fn render_empty_fields(store : &Store) -> Vec<RenderedField> {
|
||||
pub fn render_empty_fields(store: &Store) -> Vec<RenderedField> {
|
||||
let indexes = &store.index;
|
||||
|
||||
store.model.fields.iter().map(|(key, field)| {
|
||||
RenderedField::from_template_field(key, field, None, indexes)
|
||||
}).collect()
|
||||
store
|
||||
.model
|
||||
.fields
|
||||
.iter()
|
||||
.map(|(key, field)| RenderedField::from_template_field(key, field, None, indexes))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn render_card_fields<'a>(store : &'a Store, values : &'a serde_json::Map<String, Value>) -> Vec<RenderedField<'a>> {
|
||||
pub fn render_card_fields<'a>(
|
||||
store: &'a Store,
|
||||
values: &'a serde_json::Map<String, Value>,
|
||||
) -> Vec<RenderedField<'a>> {
|
||||
let indexes = &store.index;
|
||||
|
||||
store.model.fields.iter().map(|(key, field)| {
|
||||
RenderedField::from_template_field(key, field, values.get(key), indexes)
|
||||
}).collect()
|
||||
store
|
||||
.model
|
||||
.fields
|
||||
.iter()
|
||||
.map(|(key, field)| {
|
||||
RenderedField::from_template_field(key, field, values.get(key), indexes)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
+32
-29
@@ -1,56 +1,56 @@
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{Read, Write, Error};
|
||||
use std::path::{Path, PathBuf};
|
||||
use rocket::request::FromForm;
|
||||
use crate::store::model::Model;
|
||||
use std::collections::HashMap;
|
||||
use indexmap::map::IndexMap;
|
||||
use rocket::request::FromForm;
|
||||
use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
use indexmap::map::IndexMap;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{Error, Read, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub mod model;
|
||||
pub mod form;
|
||||
pub mod model;
|
||||
|
||||
/// Store instance
|
||||
#[derive(Debug)]
|
||||
pub struct Store {
|
||||
path : PathBuf,
|
||||
path: PathBuf,
|
||||
pub model: Model,
|
||||
pub data: Cards,
|
||||
pub index : Indexes,
|
||||
pub index: Indexes,
|
||||
}
|
||||
|
||||
/// Indexes loaded from the indexes file
|
||||
#[derive(Serialize,Deserialize,Debug,Default)]
|
||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||
pub struct Indexes {
|
||||
pub free_enums : HashMap<String, Vec<String>>,
|
||||
pub free_tags : HashMap<String, Vec<String>>,
|
||||
pub free_enums: HashMap<String, Vec<String>>,
|
||||
pub free_tags: HashMap<String, Vec<String>>,
|
||||
}
|
||||
|
||||
/// Struct loaded from the repositroy config file
|
||||
#[derive(Deserialize,Debug)]
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct RepositoryConfig {
|
||||
pub model : Model,
|
||||
pub model: Model,
|
||||
}
|
||||
|
||||
#[derive(Serialize,Deserialize,Debug)]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Cards {
|
||||
#[serde(default)]
|
||||
pub cards : IndexMap<usize, Value>,
|
||||
pub cards: IndexMap<usize, Value>,
|
||||
#[serde(default)]
|
||||
pub counter: usize,
|
||||
}
|
||||
|
||||
const REPO_CONFIG_FILE : &'static str = "repository.yaml";
|
||||
const REPO_DATA_FILE : &'static str = "data.json";
|
||||
const REPO_INDEX_FILE : &'static str = "index.json";
|
||||
const REPO_CONFIG_FILE: &'static str = "repository.yaml";
|
||||
const REPO_DATA_FILE: &'static str = "data.json";
|
||||
const REPO_INDEX_FILE: &'static str = "index.json";
|
||||
|
||||
impl Store {
|
||||
pub fn new(path: impl AsRef<Path>) -> Self {
|
||||
let file = load_file(path.as_ref().join(REPO_CONFIG_FILE));
|
||||
|
||||
let repository_config : RepositoryConfig = serde_yaml::from_str(&file)
|
||||
.expect("Error parsing repository config file.");
|
||||
let repository_config: RepositoryConfig =
|
||||
serde_yaml::from_str(&file).expect("Error parsing repository config file.");
|
||||
|
||||
let items = load_file_or(path.as_ref().join(REPO_DATA_FILE), "{}");
|
||||
let indexes = load_file_or(path.as_ref().join(REPO_INDEX_FILE), "{}");
|
||||
@@ -72,10 +72,11 @@ impl Store {
|
||||
.expect("Error opening data file for writing.");
|
||||
|
||||
let serialized = serde_json::to_string_pretty(&self.data).expect("Error serialize.");
|
||||
file.write(serialized.as_bytes()).expect("Error write data file");
|
||||
file.write(serialized.as_bytes())
|
||||
.expect("Error write data file");
|
||||
}
|
||||
|
||||
pub fn add_card(&mut self, values : IndexMap::<String, Value>) -> usize {
|
||||
pub fn add_card(&mut self, values: IndexMap<String, Value>) -> usize {
|
||||
let packed = serde_json::to_value(values).expect("Error serialize");
|
||||
if let p @ Value::Object(_) = packed {
|
||||
let id = self.data.counter;
|
||||
@@ -88,7 +89,7 @@ impl Store {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_card(&mut self, id : usize, values : IndexMap::<String, Value>) {
|
||||
pub fn update_card(&mut self, id: usize, values: IndexMap<String, Value>) {
|
||||
let packed = serde_json::to_value(values).expect("Error serialize");
|
||||
|
||||
if !self.data.cards.contains_key(&id) {
|
||||
@@ -104,7 +105,7 @@ impl Store {
|
||||
self.persist()
|
||||
}
|
||||
|
||||
pub fn delete_card(&mut self, id : usize) {
|
||||
pub fn delete_card(&mut self, id: usize) {
|
||||
self.data.cards.remove(&id);
|
||||
|
||||
self.persist()
|
||||
@@ -112,17 +113,19 @@ impl Store {
|
||||
}
|
||||
|
||||
fn load_file(path: impl AsRef<Path>) -> String {
|
||||
let mut file= File::open(&path).expect(&format!("Error opening file {}", path.as_ref().display()));
|
||||
let mut file =
|
||||
File::open(&path).expect(&format!("Error opening file {}", path.as_ref().display()));
|
||||
|
||||
let mut buf = String::new();
|
||||
file.read_to_string(&mut buf).expect(&format!("Error reading file {}", path.as_ref().display()));
|
||||
file.read_to_string(&mut buf)
|
||||
.expect(&format!("Error reading file {}", path.as_ref().display()));
|
||||
buf
|
||||
}
|
||||
|
||||
fn load_file_or(file : impl AsRef<Path>, def : impl Into<String>) -> String {
|
||||
fn load_file_or(file: impl AsRef<Path>, def: impl Into<String>) -> String {
|
||||
let mut file = match File::open(file) {
|
||||
Ok(file) => file,
|
||||
Err(_) => return def.into()
|
||||
Err(_) => return def.into(),
|
||||
};
|
||||
|
||||
let mut buf = String::new();
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
use indexmap::map::IndexMap;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// A data card's model.
|
||||
/// Cards of one model can be sorted, searched and filtered by their fields.
|
||||
|
||||
Reference in New Issue
Block a user