extern crate zakform;
+use zakform::TField;
+
use zakform::filters;
use std::collections::HashMap;
settings: Settings,
}
-#[derive(Serialize,Deserialize)]
-struct FormData {
- name: String,
- notes: String,
- sex: String,
-}
-
-async fn index_post(
+async fn index(
data: web::Data<AppState>,
- form: web::Form<FormData>,
) -> Result<impl Responder, Error> {
+
let mut s = String::new();
s.push_str("<html>\n");
let mut f = zakform::Form::new();
- let strform = serde_json::to_string(&form).unwrap();
- println!("{:#?}",strform);
- let map: HashMap<String, String> = serde_json::from_str(&strform).unwrap();
-
- let mut fi = zakform::Field::new(zakform::FType::Text, "name");
- fi.set_label("Name");
- fi.set_value(map.get(fi.name().as_str()).unwrap().as_str());
- fi.add_filter(filters::filter_uppercase);
- fi.filter();
- fi.set_template(r#"{% if label %}<div class="mb-3" style="background-color: #FF0000;">
- <label for="{{ name }}" class="form-label">{{ label }}</label>{% endif %}
- <input type="text" class="form-control{% if help %} is-invalid{% endif %}" name="{{ name }}" id="{{ name }}" {% if value %} value="{{ value }}" {% endif %} {% if disabled %} readonly {% endif %} {% if maxlen > 0 %}maxlength="{{ maxlen }}"{% endif %} {% if invisible %}style="display: none;"{% endif %}/>
- {% if help %}<div id="helpBox_{{ name }}_" class="invalid-feedback">{{ help }}</div>{% endif %}
- {% if label %}</div>{% endif %}"#);
- f.add_field(fi);
-
- let mut fi = zakform::Field::new(zakform::FType::TextArea, "notes");
- fi.set_label("Notes");
- fi.set_rows(15);
- fi.set_value(map.get(fi.name().as_str()).unwrap().as_str());
- fi.add_filter(filters::filter_trim);
- fi.filter();
- f.add_field(fi);
-
- let mut fi = zakform::Field::new(zakform::FType::Select, "sex");
- fi.set_label("Sex");
- fi.add_option(zakform::FOption{value: String::from("M"), label: String::from("Male")});
- fi.add_option(zakform::FOption{value: String::from("F"), label: String::from("Female")});
- fi.set_value(map.get(fi.name().as_str()).unwrap().as_str());
- f.add_field(fi);
-
- let fs = f.fields();
- for fi in fs {
- s.push_str(fi.render().as_str());
- }
-
- let js: HashMap<String, String> = serde_json::from_str(f.as_json_string().as_str()).unwrap();
- println!("{:#?}", js);
-
- s.push_str(r#"<button type="submit">Send</button>"#);
-
- s.push_str("</form>\n");
-
- s.push_str("</body>\n");
- s.push_str("</html>");
-
- Ok(HttpResponse::Ok().body(s))
-}
-
-async fn index(
- data: web::Data<AppState>,
-) -> Result<impl Responder, Error> {
-
- let mut s = String::new();
-
- s.push_str("<html>\n");
- s.push_str("<body>\n");
+ let mut fi = zakform::FieldText::new("text");
+ f.add_field(zakform::TFields::TFieldText(fi));
- s.push_str(r#"<form method="post" action="/">"#);
+ let mut fi = zakform::FieldRadio::new("radio");
+ fi.add_option(zakform::FOption{ value: String::from("F"), label: String::from("Female") });
+ fi.add_option(zakform::FOption{ value: String::from("M"), label: String::from("Male") });
- let mut f = zakform::Form::new();
+ let single = fi.render_single(1);
- let mut fi = zakform::Field::new(zakform::FType::Text, "name");
- fi.set_label("Name");
- fi.set_template(r#"{% if label %}<div class="mb-3" style="background-color: #FF0000;">
- <label for="{{ name }}" class="form-label">{{ label }}</label>{% endif %}
- <input type="text" class="form-control{% if help %} is-invalid{% endif %}" name="{{ name }}" id="{{ name }}" {% if value %} value="{{ value }}" {% endif %} {% if disabled %} readonly {% endif %} {% if maxlen > 0 %}maxlength="{{ maxlen }}"{% endif %} {% if invisible %}style="display: none;"{% endif %}/>
- {% if help %}<div id="helpBox_{{ name }}_" class="invalid-feedback">{{ help }}</div>{% endif %}
- {% if label %}</div>{% endif %}"#);
- f.add_field(fi);
-
- let mut fi = zakform::Field::new(zakform::FType::TextArea, "notes");
- fi.set_label("Notes");
- fi.set_rows(15);
- f.add_field(fi);
-
- let mut fi = zakform::Field::new(zakform::FType::Select, "sex");
- fi.set_label("Sex");
- fi.add_option(zakform::FOption{value: String::from("M"), label: String::from("Male")});
- fi.add_option(zakform::FOption{value: String::from("F"), label: String::from("Female")});
- f.add_field(fi);
+ f.add_field(zakform::TFields::TFieldRadio(fi));
let fs = f.fields();
for fi in fs {
- s.push_str(fi.render().as_str());
+ match fi {
+ zakform::TFields::TFieldText(fi) => { s.push_str(fi.render().as_str()); },
+ zakform::TFields::TFieldRadio(fi) => { s.push_str(fi.render().as_str()); },
+ }
}
+ s.push_str(&single);
+
s.push_str(r#"<button type="submit">Send</button>"#);
s.push_str("</form>\n");
}))
.wrap(middleware::Logger::default()) // enable logger
.service(web::resource("/")
- .route(web::get().to(index))
- .route(web::post().to(index_post)))
+ .route(web::get().to(index)))
.service(web::scope("").wrap(error_handlers()))
})
.bind((appstate.settings.http.bind_ip, appstate.settings.http.bind_port))?
-use serde_derive::{Serialize,Deserialize};
+use serde_derive::{Serialize};
-use tera::Tera;
+use std::default::Default;
pub mod filters;
-pub enum FType {
- Text,
- TextArea,
- Check,
- Radio,
- Select,
-}
-
-pub struct Field {
- ftype: FType,
+#[derive(Default)]
+struct Field {
name: String,
label: String,
- maxlen: i32,
disabled: bool,
invisible: bool,
- rows: i32,
help: String,
value: String,
- options: Vec<FOption>,
tmpl: tera::Tera,
filters: Vec<filters::Filter>,
}
-#[derive(Serialize)]
-pub struct FOption {
- pub value: String,
- pub label: String,
+pub trait TField {
+ fn new(name: &str) -> Self;
+
+ fn render(&self) -> String;
}
-impl Field {
- pub fn new(ftype: FType, name: &str) -> Field {
- let mut f = Field {
- ftype: ftype,
- name: String::from(name),
- label: String::from(name),
- maxlen: 0,
- disabled: false,
- invisible: false,
- rows: 0,
- help: String::new(),
- value: String::new(),
- options: Vec::new(),
- tmpl: Tera::default(),
- filters: vec![],
- };
-
- match f.ftype {
- FType::Text => {
- f.tmpl.add_raw_template("field", r#"{% if label %}<div class="mb-3">
- <label for="{{ name }}" class="form-label">{{ label }}</label>{% endif %}
- <input type="text" class="form-control{% if help %} is-invalid{% endif %}" name="{{ name }}" id="{{ name }}" {% if value %} value="{{ value }}" {% endif %} {% if disabled %} readonly {% endif %} {% if maxlen > 0 %}maxlength="{{ maxlen }}"{% endif %} {% if invisible %}style="display: none;"{% endif %}/>
- {% if help %}<div id="helpBox_{{ name }}_" class="invalid-feedback">{{ help }}</div>{% endif %}
- {% if label %}</div>{% endif %}"#);
- },
- FType::TextArea => {
- f.rows = 5;
+#[derive(Default)]
+pub struct FieldText {
+ field: Field,
+ maxlen: i32,
+}
+
+impl TField for FieldText {
+ fn new(name: &str) -> Self {
+ let mut f: FieldText = Default::default();
- f.tmpl.add_raw_template("field", r#"{% if label %}<div class="mb-3">
+ f.field.name = String::from(name);
+ f.field.label = String::from(name);
+
+ match f.field.tmpl.add_raw_template("field", r#"{% if label %}<div class="mb-3">
<label for="{{ name }}" class="form-label">{{ label }}</label>{% endif %}
- <textarea class="form-control{%if help %} is-invalid{% endif %}" name="{{ name }}" id="{{ name }}" rows="{{ rows }}">{{ value }}</textarea>
- {% if help %}<div id="helpBox_{{ name }}_" class="invalid-feedback">{{ help }}</div>{% endif %}
- {% if label %}</div>{% endif %}"#);
- },
- FType::Check => {
- f.tmpl.add_raw_template("field", r#"<div class="form-check">
- <input type="checkbox" class="form-check-input{% if help %} is-invalid{% endif %}" name="{{ name }}" id="{{ name }}" {% if value == "1" or value == "on" %}checked{% endif %}/>
- {% if label %}<label for="{{ name }}" class="form-check-label">{{ label }}</label>{% endif %}
- {% if help %}<div id="helpBox_{{ name }}_" class="invalid-feedback">{{ help }}</div>{% endif %}
- </div>"#);
- },
- FType::Radio => {
- match f.tmpl.add_raw_template("field", r#"{% if label %}<div class="mb-3">
- <label for="{{ name }}" class="form-label">{{ label }}</label><br/>{% endif %}
- {% for o in options %}
- <div class="form-check{% if help %} is-invalid{% endif %}">
- <input type="radio" class="form-check-input{% if help %} is-invalid{% endif %}" name="{{ name }}" id="{{ name }}_{{ loop.index }}" value="{{ o.value }}"{% if value == o.value %} checked{% endif %}/>
- <label class="form-label" for="{{ name }}_{{ loop.index }}">{{ o.label }}</label>
- </div>
- {% endfor %}
+ <input type="text" class="form-control{% if help %} is-invalid{% endif %}" name="{{ name }}" id="{{ name }}" {% if value %} value="{{ value }}" {% endif %} {% if disabled %} readonly {% endif %} {% if maxlen > 0 %}maxlength="{{ maxlen }}"{% endif %} {% if invisible %}style="display: none;"{% endif %}/>
{% if help %}<div id="helpBox_{{ name }}_" class="invalid-feedback">{{ help }}</div>{% endif %}
{% if label %}</div>{% endif %}"#) {
Err(e) => { println!("{:?}", e) },
_ => {},
};
- },
- FType::Select => {
- f.tmpl.add_raw_template("field", r#"{% if label %}<div class="mb-3">
- <label for="{{ name }}" class="form-label">{{ label }}</label><br/>{% endif %}
- <select class="form-select{% if help %} is-invalid{% endif %}" name="{{ name }}" id="{{ name }}">
- {% for o in options %}
- <option value="{{ o.value }}"{% if value == o.value %} selected{% endif %}>{{ o.label }}</option>
- {% endfor %}
- </select>
- {% if help %}<div id="helpBox_{{ name }}_" class="invalid-feedback">{{ help }}</div>{% endif %}
- {% if label %}</div>{% endif %}"#);
- },
- }
f
}
- pub fn name(&self) -> String {
- String::from(self.name.as_str())
- }
-
- pub fn label(&self) -> String {
- String::from(self.label.as_str())
- }
-
- pub fn set_label(&mut self, label: &str) {
- self.label = String::from(label);
- }
+ fn render(&self) -> String {
+ let mut s = String::new();
- pub fn maxlen(&self) -> i32 {
- self.maxlen
- }
+ let mut context = tera::Context::new();
+ context.insert("name", &self.field.name);
+ context.insert("label", &self.field.label);
+ context.insert("maxlen", &self.maxlen);
+ context.insert("disabled", &self.field.disabled);
+ context.insert("invisible", &self.field.invisible);
+ context.insert("help", &self.field.help);
+ context.insert("value", &self.field.value);
+ s.push_str(self.field.tmpl.render("field", &context).unwrap().as_str());
- pub fn set_maxlen(&mut self, maxlen: i32) {
- self.maxlen = maxlen;
+ s
}
+}
- pub fn disabled(&self) -> bool {
- self.disabled
- }
+#[derive(Serialize)]
+pub struct FOption {
+ pub value: String,
+ pub label: String,
+}
- pub fn set_disabled(&mut self, disabled: bool) {
- self.disabled = disabled;
- }
+#[derive(Default)]
+pub struct FieldRadio {
+ field: Field,
+ options: Vec<FOption>,
+}
- pub fn invisible(&self) -> bool {
- self.invisible
- }
+impl TField for FieldRadio {
+ fn new(name: &str) -> Self {
+ let mut f: FieldRadio = Default::default();
- pub fn set_invisible(&mut self, invisible: bool) {
- self.invisible = invisible;
- }
+ f.field.name = String::from(name);
+ f.field.label = String::from(name);
- pub fn rows(&self) -> i32 {
- self.rows
- }
+ match f.field.tmpl.add_raw_template("field", r#"{% if label %}<div class="mb-3">
+ <label for="{{ name }}" class="form-label">{{ label }}</label><br/>{% endif %}
+ {% for o in options %}
+ <div class="form-check{% if help %} is-invalid{% endif %}">
+ <input type="radio" class="form-check-input{% if help %} is-invalid{% endif %}" name="{{ name }}" id="{{ name }}_{{ loop.index }}" value="{{ o.value }}"{% if value == o.value %} checked{% endif %}/>
+ <label class="form-label" for="{{ name }}_{{ loop.index }}">{{ o.label }}</label>
+ </div>
+ {% endfor %}
+ {% if help %}<div id="helpBox_{{ name }}_" class="invalid-feedback">{{ help }}</div>{% endif %}
+ {% if label %}</div>{% endif %}"#) {
+Err(e) => { println!("{:?}", e) },
+_ => {},
+};
- pub fn set_rows(&mut self, rows: i32) {
- self.rows = rows;
- }
+ match f.field.tmpl.add_raw_template("field_single", r#"<div class="form-check{% if help %} is-invalid{% endif %}">
+ <input type="radio" class="form-check-input{% if help %} is-invalid{% endif %}" name="{{ name }}" id="{{ name }}_{{ idx }}" value="{{ value }}"{% if checked != "" %} checked{% endif %}/>
+ <label class="form-label" for="{{ name }}_{{ idx }}">{{ label }}</label>
+ </div>"#) {
+Err(e) => { println!("{:?}", e) },
+_ => {},
+};
- pub fn help(&self) -> String {
- String::from(self.help.as_str())
+ f
}
- pub fn set_help(&mut self, help: &str) {
- self.help = String::from(help);
- }
+ fn render(&self) -> String {
+ let mut s = String::new();
- pub fn value(&self) -> String {
- String::from(self.value.as_str())
- }
+ let mut context = tera::Context::new();
+ context.insert("name", &self.field.name);
+ context.insert("label", &self.field.label);
+ context.insert("disabled", &self.field.disabled);
+ context.insert("invisible", &self.field.invisible);
+ context.insert("help", &self.field.help);
+ context.insert("value", &self.field.value);
+ context.insert("options", &self.options);
+ s.push_str(self.field.tmpl.render("field", &context).unwrap().as_str());
- pub fn set_value(&mut self, value: &str) {
- self.value = String::from(value);
+ s
}
+}
+impl FieldRadio {
pub fn add_option(&mut self, option: FOption) {
self.options.push(option);
}
- pub fn set_template(&mut self, template: &str) {
- self.tmpl.add_raw_template("field", template);
- }
+ pub fn render_single(&self, idx: usize) -> String {
+ let mut s = String::new();
- pub fn add_filter(&mut self, filter: filters::Filter) {
- self.filters.push(filter);
- }
+ let mut context = tera::Context::new();
+
+ context.insert("name", &self.field.name);
+ context.insert("help", &self.field.help);
+
+ let opt = &self.options[idx];
+ context.insert("label", &opt.label);
+ context.insert("value", &opt.value);
- pub fn filter(&mut self) {
- for f in &self.filters {
- self.value = (f)(&self.value);
+ if opt.value == self.field.value {
+ context.insert("checked", "checked");
+ } else {
+ context.insert("checked", "");
}
- }
- pub fn render(&self) -> String {
- let mut s = String::new();
+ context.insert("idx", &idx);
- let mut context = tera::Context::new();
- context.insert("name", &self.name);
- context.insert("label", &self.label);
- context.insert("maxlen", &self.maxlen);
- context.insert("disabled", &self.disabled);
- context.insert("invisible", &self.invisible);
- context.insert("rows", &self.rows);
- context.insert("help", &self.help);
- context.insert("value", &self.value);
- context.insert("options", &self.options);
- s.push_str(self.tmpl.render("field", &context).unwrap().as_str());
+ s.push_str(self.field.tmpl.render("field_single", &context).unwrap().as_str());
s
}
}
+pub enum TFields {
+ TFieldText(FieldText),
+ TFieldRadio(FieldRadio),
+}
+
pub struct Form {
- fields: Vec<Field>
+ fields: Vec<TFields>,
}
impl Form {
}
}
- pub fn add_field(&mut self, field: Field) {
+ pub fn add_field(&mut self, field: TFields) {
self.fields.push(field);
}
- pub fn fields(&self) -> &Vec<Field> {
+ pub fn fields(&self) -> &Vec<TFields> {
&self.fields
}
-
- pub fn get_field(&mut self, field_name: &str) -> Result<&mut Field, ()> {
- for f in &mut self.fields {
- if f.name.as_str() == field_name {
- return Ok(f);
- }
- }
-
- Err(())
- }
-
- pub fn as_json_string(&mut self) -> String {
- let mut s = String::from("{");
-
- for f in &mut self.fields {
- s.push_str(format!(r#""{}": "{}","#, f.name, f.value).as_str());
- }
-
- s.truncate(s.len() - 1);
- s.push_str("}");
-
- s
- }
}