use crate::prelude::*;
use std::env;
use std::fmt::{self, Debug, Display, Formatter};
use std::io::{BufRead, Write};
use std::rc::Rc;
use std::str::FromStr;
pub enum Field<'a, R = In, W = Out> {
Value(ValueField<'a>),
Select(SelectMenu<'a, R, W>),
}
impl<'a, R, W> Field<'a, R, W> {
pub(crate) fn inherit_fmt(&mut self, fmt: Rc<ValueFieldFormatting<'a>>) {
match self {
Self::Value(vf) => vf.inherit_fmt(fmt),
Self::Select(sm) => sm.inherit_fmt(fmt),
}
}
}
impl<'a, R, W> Field<'a, R, W>
where
R: BufRead,
W: Write,
{
pub(crate) fn menu_build<Output>(&mut self, stream: &mut MenuStream<R, W>) -> MenuResult<Output>
where
Output: FromStr,
Output::Err: 'static + Debug,
{
match self {
Self::Value(vf) => vf.menu_build(stream),
Self::Select(sm) => sm.next_output_with(stream),
}
}
pub(crate) fn menu_build_or_default<Output>(&mut self, stream: &mut MenuStream<R, W>) -> Output
where
Output: FromStr + Default,
Output::Err: 'static + Debug,
{
match self {
Self::Value(vf) => vf.menu_build_or_default(stream),
Self::Select(sm) => sm.next_or_default_with(stream),
}
}
}
pub type Binding<R, W> = fn(&mut MenuStream<R, W>) -> MenuResult<()>;
pub struct SelectField<'a, R = In, W = Out> {
pub(crate) msg: &'a str,
chip: &'a str,
custom_chip: bool,
bind: Option<Binding<R, W>>,
}
impl<R, W> Display for SelectField<'_, R, W> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(self.chip, f)?;
Display::fmt(self.msg, f)
}
}
impl<'a, R, W> From<&'a str> for SelectField<'a, R, W> {
fn from(msg: &'a str) -> Self {
Self {
msg,
chip: " - ",
custom_chip: false,
bind: None,
}
}
}
impl<'a, R, W> SelectField<'a, R, W> {
pub fn chip(mut self, chip: &'a str) -> Self {
self.chip = chip;
self.custom_chip = true;
self
}
pub(crate) fn set_chip(&mut self, chip: &'a str) {
if !self.custom_chip {
self.chip = chip;
}
}
pub(crate) fn call_bind(&self, stream: &mut MenuStream<R, W>) -> MenuResult<()> {
if let Some(b) = self.bind {
b(stream)?;
}
Ok(())
}
pub fn bind(mut self, bind: Binding<R, W>) -> Self {
self.bind = Some(bind);
self
}
}
#[derive(Clone)]
pub struct ValueFieldFormatting<'a> {
pub chip: &'a str,
pub prefix: &'a str,
pub new_line: bool,
pub default: bool,
}
macro_rules! impl_constructors {
($(
#[doc = $doc:expr]
$i:ident: $t:ty
),*) => {
impl<'a> ValueFieldFormatting<'a> {$(
#[doc = $doc]
pub fn $i($i: $t) -> Self {
Self {
$i,
..Default::default()
}
}
)*}
}
}
impl_constructors!(
chip: &'a str,
prefix: &'a str,
new_line: bool,
default: bool
);
impl<'a> Default for ValueFieldFormatting<'a> {
fn default() -> Self {
Self {
chip: "--> ",
prefix: ">> ",
new_line: true,
default: true,
}
}
}
enum DefaultValue<'a> {
Value(&'a str),
Env(String),
}
impl<'a> DefaultValue<'a> {
pub fn env(var: &'a str) -> MenuResult<Self> {
Ok(Self::Env(
env::var(var).map_err(|e| MenuError::EnvVar(var.to_string(), e))?,
))
}
}
impl Display for DefaultValue<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
#[inline(never)]
fn write_str(f: &mut Formatter<'_>, s: impl AsRef<str>) -> fmt::Result {
write!(f, "(default: {})", s.as_ref())
}
match self {
Self::Value(s) => write_str(f, s),
Self::Env(s) => write_str(f, s),
}
}
}
pub struct ValueField<'a> {
msg: &'a str,
fmt: Rc<ValueFieldFormatting<'a>>,
custom_fmt: bool,
default: Option<DefaultValue<'a>>,
}
impl<'a> From<&'a str> for ValueField<'a> {
fn from(msg: &'a str) -> Self {
Self {
msg,
fmt: Rc::default(),
custom_fmt: false,
default: None,
}
}
}
impl<'a> Display for ValueField<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"{chip}{msg}{def}{nl}{prefix}",
chip = self.fmt.chip,
msg = self.msg,
def = match &self.default {
Some(val) if self.fmt.default => format!(" {}", val),
_ => "".to_owned(),
},
nl = if self.fmt.new_line { "\n" } else { "" },
prefix = self.fmt.prefix,
)
}
}
impl<'a> ValueField<'a> {
pub fn fmt(mut self, fmt: ValueFieldFormatting<'a>) -> Self {
self.fmt = Rc::new(fmt);
self.custom_fmt = true;
self
}
pub(crate) fn inherit_fmt(&mut self, fmt: Rc<ValueFieldFormatting<'a>>) {
if !self.custom_fmt {
self.fmt = fmt;
}
}
pub fn default_value(mut self, default: &'a str) -> Self {
self.default = Some(DefaultValue::Value(default));
self
}
pub fn default_env(mut self, var: &'a str) -> MenuResult<Self> {
self.default = Some(DefaultValue::env(var)?);
Ok(self)
}
#[inline]
pub fn build_init<T>(&self) -> MenuResult<T>
where
T: FromStr,
T::Err: 'static + Debug,
{
self.build_with(&mut MenuStream::default())
}
#[inline]
pub fn build<T>(&self, reader: &mut In, writer: &mut Out) -> MenuResult<T>
where
T: FromStr,
T::Err: 'static + Debug,
{
self.menu_build(&mut MenuStream::with(reader, writer))
}
#[inline]
pub fn build_with<T, R, W>(&self, stream: &mut MenuStream<R, W>) -> MenuResult<T>
where
T: FromStr,
T::Err: 'static + Debug,
R: BufRead,
W: Write,
{
self.menu_build(stream)
}
#[inline]
pub fn build_or_default<T>(&self, reader: &mut In, writer: &mut Out) -> T
where
T: FromStr + Default,
T::Err: 'static + Debug,
{
self.menu_build_or_default(&mut MenuStream::new(reader, writer))
}
#[inline]
pub fn build_or_default_with<T, R, W>(&self, stream: &mut MenuStream<R, W>) -> T
where
T: FromStr + Default,
T::Err: 'static + Debug,
R: BufRead,
W: Write,
{
self.menu_build_or_default(stream)
}
fn inner_build<T, R, W>(&self, stream: &mut MenuStream<R, W>) -> MenuResult<T>
where
T: FromStr,
T::Err: 'static + Debug,
R: BufRead,
W: Write,
{
write!(stream, "{}", self)?;
stream.flush()?;
let output = raw_read_input(stream)?;
parse_value(output)
}
pub(crate) fn menu_build<T, R, W>(&self, stream: &mut MenuStream<R, W>) -> MenuResult<T>
where
T: FromStr,
T::Err: 'static + Debug,
R: BufRead,
W: Write,
{
loop {
match self.inner_build(stream) {
Ok(t) => break Ok(t),
Err(_) => {
if let Some(default) = &self.default {
break Ok(default_parse(default));
}
}
}
}
}
pub(crate) fn menu_build_or_default<T, R, W>(&self, stream: &mut MenuStream<R, W>) -> T
where
T: FromStr + Default,
T::Err: 'static + Debug,
R: BufRead,
W: Write,
{
if let Ok(t) = self.inner_build(stream) {
t
} else {
self.default.as_ref().map(default_parse).unwrap_or_default()
}
}
}
pub(crate) fn raw_read_input<R, W>(stream: &mut MenuStream<R, W>) -> MenuResult<String>
where
R: BufRead,
W: Write,
{
let mut out = String::new();
stream.read_line(&mut out)?;
stream.write_all(b"\n")?;
Ok(out.trim().to_owned())
}
fn parse_value<T>(s: impl AsRef<str>) -> MenuResult<T>
where
T: FromStr,
T::Err: 'static + Debug,
{
let s = s.as_ref();
s.parse()
.map_err(|e| MenuError::Parse(s.to_owned(), Box::new(e)))
}
pub(crate) fn default_parse_failed<S, E>(s: S, e: E) -> !
where
S: ToString,
E: 'static + Debug,
{
panic!(
"`{}` has been used as default value but its type is incorrect: {:?}",
s.to_string(),
e
)
}
fn default_parse<T>(default: &DefaultValue<'_>) -> T
where
T: FromStr,
T::Err: 'static + Debug,
{
match default {
DefaultValue::Value(s) => s.parse().unwrap_or_else(|e| default_parse_failed(s, e)),
DefaultValue::Env(s) => s.parse().unwrap_or_else(|e| default_parse_failed(s, e)),
}
}