use clap;
use cursive::view::View;
use cursive::views;
use serde_json::value::Value;
use std::rc::Rc;
use validators::{Required, Validator};
pub(crate) mod autocomplete;
mod checkbox;
pub(crate) mod multiselect;
mod text;
pub use self::autocomplete::Autocomplete;
pub use self::checkbox::Checkbox;
pub use self::multiselect::Multiselect;
pub use self::text::Text;
pub trait WidgetManager {
fn build_value_view(&self, value: &str) -> views::BoxedView;
fn get_value(&self, view: &views::BoxedView) -> String;
#[deprecated(
since = "1.0.0",
note = "Errors should be transferred to `Field`. Use `Field.set_error`"
)]
fn set_error(&self, viewbox: &mut views::BoxedView, error: &str) {
let layout: &mut views::LinearLayout = (**viewbox).as_any_mut().downcast_mut().unwrap();
let child: &mut dyn View = (*layout).get_child_mut(2).unwrap();
let text: &mut views::TextView = (*child).as_any_mut().downcast_mut().unwrap();
text.set_content(error);
}
#[deprecated(
since = "1.0.0",
note = "Values like `help` or `label` should be stored on `FormField`. Details in documentation of `WidgetManager.build_widget`"
)]
fn build_widget(&self, label: &str, help: &str, initial: &str) -> views::BoxedView;
}
#[derive(Clone)]
pub struct Field<W: WidgetManager, T> {
label: String,
help: String,
initial: T,
validators: Vec<Rc<dyn Validator>>,
widget_manager: W,
}
impl<W: WidgetManager, T> Field<W, T> {
pub fn new<IS: Into<String>>(label: IS, widget_manager: W, initial: T) -> Self {
Field {
label: label.into(),
help: "".into(),
initial: initial,
validators: vec![],
widget_manager: widget_manager,
}
}
pub fn help<IS: Into<String>>(mut self, msg: IS) -> Self {
self.help = msg.into();
self
}
pub fn validator<V: Validator + 'static>(mut self, validator: V) -> Self {
self.validators.push(Rc::new(validator));
self
}
pub fn is_required(&self) -> bool {
self.validators
.iter()
.any(|&ref x| (**x).as_any().downcast_ref::<Required>().is_some())
}
}
pub type FieldErrors = Vec<String>;
pub trait FormField {
fn build_widget(&self) -> views::BoxedView {
let view = self
.get_widget_manager()
.build_value_view(&self.get_initial());
label_with_help_layout(view, self.get_label(), &self.get_help())
}
fn validate(&self, data: &str) -> Result<Value, FieldErrors>;
fn get_label(&self) -> &str;
fn get_help(&self) -> &str;
fn get_initial(&self) -> String;
fn get_widget_manager(&self) -> &dyn WidgetManager;
fn clap_arg(&self) -> clap::Arg;
fn clap_args2str(&self, args: &clap::ArgMatches) -> String;
fn is_required(&self) -> bool;
fn set_error(&self, viewbox: &mut views::BoxedView, error: &str) {
let layout: &mut views::LinearLayout = (**viewbox).as_any_mut().downcast_mut().unwrap();
let child: &mut dyn View = (*layout).get_child_mut(2).unwrap();
let text: &mut views::TextView = (*child).as_any_mut().downcast_mut().unwrap();
text.set_content(error);
}
}
fn format_annotation(label: &str, help: &str) -> String {
if help.len() > 0 {
format!("{:20}: {}", label, help)
} else {
format!("{:20}", label)
}
}
pub fn label_with_help_layout(
view_box: views::BoxedView,
label: &str,
help: &str,
) -> views::BoxedView {
let text = format_annotation(label, help);
let widget = views::LinearLayout::vertical()
.child(views::TextView::new(text))
.child(view_box)
.child(views::TextView::new(""))
.child(views::DummyView);
views::BoxedView::new(Box::new(widget))
}
pub fn value_view_from_layout(layout: &views::BoxedView) -> &views::BoxedView {
let layout: &views::LinearLayout = (**layout).as_any().downcast_ref().unwrap();
let value_view: &dyn View = layout.get_child(1).unwrap();
(*value_view).as_any().downcast_ref().unwrap()
}