mod stream;
pub use crate::menu::stream::MenuStream;
use crate::menu::stream::Stream;
use crate::prelude::*;
use std::fmt::{self, Debug, Display, Formatter};
use std::io::{BufRead, BufReader, Stdin, Stdout, Write};
use std::rc::Rc;
use std::str::FromStr;
use std::vec::IntoIter;
pub type In = BufReader<Stdin>;
pub type Out = Stdout;
pub trait GetStream<'s, R: 's, W: 's>: Sized {
fn get_stream(self) -> MenuStream<'s, R, W>;
#[inline]
fn retrieve(self) -> (R, W) {
self.get_stream().retrieve()
}
}
#[derive(Clone, Copy)]
pub enum TitlePos {
Top,
Bottom,
}
impl Default for TitlePos {
fn default() -> Self {
Self::Top
}
}
pub struct SelectMenu<'a, R = In, W = Out> {
title: SelectTitle<'a>,
fields: Vec<SelectField<'a, R, W>>,
stream: Stream<'a, MenuStream<'a, R, W>>,
default: Option<usize>,
prefix: &'a str,
}
pub struct SelectTitle<'a> {
inner: &'a str,
fmt: ValueFieldFormatting<'a>,
custom_fmt: bool,
pub(crate) pos: TitlePos,
}
impl Default for SelectTitle<'_> {
fn default() -> Self {
Self {
inner: "",
fmt: ValueFieldFormatting {
chip: "",
prefix: "",
new_line: true,
..Default::default()
},
custom_fmt: false,
pos: Default::default(),
}
}
}
impl<'a> From<&'a str> for SelectTitle<'a> {
fn from(inner: &'a str) -> Self {
Self {
inner,
fmt: ValueFieldFormatting::prefix(":"),
custom_fmt: false,
pos: Default::default(),
}
}
}
impl<'a> SelectTitle<'a> {
pub fn fmt(mut self, fmt: ValueFieldFormatting<'a>) -> Self {
self.fmt = fmt;
self.custom_fmt = true;
self
}
pub fn pos(mut self, pos: TitlePos) -> Self {
self.pos = pos;
self
}
pub(crate) fn inherit_fmt(&mut self, fmt: Rc<ValueFieldFormatting<'a>>) {
self.fmt = ValueFieldFormatting {
chip: fmt.chip,
new_line: fmt.new_line,
default: fmt.default,
prefix: self.fmt.prefix,
};
self.custom_fmt = false;
}
}
impl Display for SelectTitle<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if self.inner.is_empty() {
return Ok(());
}
let disp = format!(
"{chip}{title}{prefix}",
chip = self.fmt.chip,
title = self.inner,
prefix = self.fmt.prefix,
);
if self.fmt.new_line {
writeln!(f, "{}", disp)
} else {
write!(f, "{}", disp)
}
}
}
impl<'a> From<Vec<SelectField<'a>>> for SelectMenu<'a> {
#[inline]
fn from(fields: Vec<SelectField<'a>>) -> Self {
Self::with_owned(MenuStream::default(), fields)
}
}
impl<'a, const N: usize> From<[SelectField<'a>; N]> for SelectMenu<'a> {
#[inline]
fn from(fields: [SelectField<'a>; N]) -> Self {
Self::from(Vec::from(fields))
}
}
impl<'a, R, W> SelectMenu<'a, R, W> {
fn inner_new(
stream: Stream<'a, MenuStream<'a, R, W>>,
fields: Vec<SelectField<'a, R, W>>,
) -> Self {
Self {
title: Default::default(),
fields,
stream,
default: None,
prefix: ">> ",
}
}
#[inline]
pub fn with_owned(stream: MenuStream<'a, R, W>, fields: Vec<SelectField<'a, R, W>>) -> Self {
Self::inner_new(Stream::Owned(stream), fields)
}
#[inline]
pub fn with_ref(
stream: &'a mut MenuStream<'a, R, W>,
fields: Vec<SelectField<'a, R, W>>,
) -> Self {
Self::inner_new(Stream::Borrowed(stream), fields)
}
#[inline]
pub fn new_ref(
reader: &'a mut R,
writer: &'a mut W,
fields: Vec<SelectField<'a, R, W>>,
) -> Self {
Self::with_owned(MenuStream::with(reader, writer), fields)
}
#[inline]
pub fn title(mut self, title: SelectTitle<'a>) -> Self {
self.title = title;
self
}
pub fn default(mut self, default: usize) -> Self {
self.default = Some(default);
self
}
pub fn prefix(mut self, prefix: &'a str) -> Self {
self.prefix = prefix;
self
}
pub fn chip(mut self, chip: &'a str) -> Self {
for field in self.fields.as_mut_slice() {
field.set_chip(chip);
}
self
}
#[inline]
pub(crate) fn inherit_fmt(&mut self, fmt: Rc<ValueFieldFormatting<'a>>) {
self.title.inherit_fmt(fmt);
}
}
impl<'a, R, W> GetStream<'a, R, W> for SelectMenu<'a, R, W> {
#[inline]
fn get_stream(self) -> MenuStream<'a, R, W> {
self.stream.retrieve()
}
}
impl<R, W> Display for SelectMenu<'_, R, W> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if let TitlePos::Top = self.title.pos {
write!(f, "{}", self.title)?;
}
for (i, field) in self.fields.iter().enumerate() {
writeln!(
f,
"{i}{msg}{def}",
i = i + 1,
msg = field,
def = if matches!(self.default, Some(d) if d == i) {
" (default)"
} else {
""
},
)?;
}
if let TitlePos::Bottom = self.title.pos {
write!(f, "{}", self.title)?;
}
Ok(())
}
}
#[inline]
fn err_ty<E: 'static + Debug>(e: E) -> MenuError {
MenuError::from(format!("incorrect default value type: {:?}", e))
}
fn default_parse<Output, R, W>(
default: usize,
fields: &[SelectField<'_, R, W>],
stream: &mut MenuStream<R, W>,
) -> MenuResult<Output>
where
Output: FromStr,
Output::Err: 'static + Debug,
{
let field = fields.get(default).unwrap_or_else(|| {
default_parse_failed(
default,
format!(
"incorrect index: fields vector length is {}, index is {}",
fields.len(),
default
),
)
});
field.call_bind(stream)?;
Ok(field
.msg
.parse()
.unwrap_or_else(|e| default_parse_failed(default, e)))
}
impl<Output, R, W> MenuBuilder<Output, R, W> for SelectMenu<'_, R, W>
where
Output: FromStr,
Output::Err: 'static + Debug,
R: BufRead,
W: Write,
{
fn next_output(&mut self) -> MenuResult<Output> {
{
let s = format!("{}", self);
self.stream.write_all(s.as_bytes())?;
}
loop {
match select(&mut self.stream, self.prefix, self.default, &self.fields) {
Ok(out) => break Ok(out),
Err(_) => {
if let Some(default) = self.default {
break Ok(default_parse(default, &self.fields, &mut self.stream)?);
}
}
}
}
}
fn next_output_with(&mut self, stream: &mut MenuStream<R, W>) -> MenuResult<Output> {
stream.write_all(format!("{}", self).as_bytes())?;
loop {
match select(stream, self.prefix, self.default, &self.fields) {
Ok(out) => break Ok(out),
Err(_) => {
if let Some(default) = self.default {
break Ok(default_parse(default, &self.fields, stream)?);
}
}
}
}
}
fn next_or_default(&mut self) -> Output
where
Output: Default,
{
if {
let s = format!("{}", self);
self.stream.write_all(s.as_bytes())
}
.is_ok()
{
select(&mut self.stream, self.prefix, self.default, &self.fields).unwrap_or_default()
} else {
Output::default()
}
}
fn next_or_default_with(&mut self, stream: &mut MenuStream<R, W>) -> Output
where
Output: Default,
{
if stream.write_all(format!("{}", self).as_bytes()).is_ok() {
select(stream, self.prefix, self.default, &self.fields).unwrap_or_default()
} else {
Output::default()
}
}
}
fn select<Output, R, W>(
stream: &mut MenuStream<R, W>,
prefix: &str,
default: Option<usize>,
fields: &[SelectField<R, W>],
) -> MenuResult<Output>
where
W: Write,
R: BufRead,
Output: FromStr,
Output::Err: 'static + Debug,
{
stream.write_all(prefix.as_bytes())?;
stream.flush()?;
let out = raw_read_input(stream)?;
if let Some(field) = fields
.iter()
.find(|field| field.msg.to_lowercase() == out.to_lowercase())
{
match out.parse::<Output>() {
Ok(out) => {
field.call_bind(stream)?;
Ok(out)
}
Err(_) => {
if let Some(default) = default {
default_parse(default, fields, stream)
} else {
Err(MenuError::Select(out))
}
}
}
} else {
match out.parse::<usize>() {
Ok(idx) if idx >= 1 => {
if let Some(field) = fields.get(idx - 1) {
field.call_bind(stream)?;
field.msg.parse().map_err(err_ty)
} else {
Err(MenuError::Select(out))
}
}
Err(_) => {
if let Some(default) = default {
default_parse(default, fields, stream)
} else {
Err(MenuError::Select(out))
}
}
_ => Err(MenuError::Select(out)),
}
}
}
pub struct ValueMenu<'a, R = In, W = Out> {
title: &'a str,
fmt: Rc<ValueFieldFormatting<'a>>,
fields: IntoIter<Field<'a, R, W>>,
stream: Stream<'a, MenuStream<'a, R, W>>,
popped: bool,
}
impl<'a, const N: usize> From<[Field<'a>; N]> for ValueMenu<'a> {
#[inline]
fn from(fields: [Field<'a>; N]) -> Self {
Self::from(Vec::from(fields))
}
}
impl<'a> From<Vec<Field<'a>>> for ValueMenu<'a> {
#[inline]
fn from(fields: Vec<Field<'a>>) -> Self {
Self::with_owned(MenuStream::default(), fields)
}
}
impl<'a, R, W> ValueMenu<'a, R, W> {
fn inner_new(
stream: Stream<'a, MenuStream<'a, R, W>>,
mut fields: Vec<Field<'a, R, W>>,
) -> Self {
let fmt: Rc<ValueFieldFormatting> = Rc::default();
for field in fields.iter_mut() {
field.inherit_fmt(fmt.clone());
}
Self {
fields: fields.into_iter(),
title: "",
fmt,
stream,
popped: false,
}
}
#[inline]
pub fn with_owned(stream: MenuStream<'a, R, W>, fields: Vec<Field<'a, R, W>>) -> Self {
Self::inner_new(Stream::Owned(stream), fields)
}
#[inline]
pub fn with_ref(stream: &'a mut MenuStream<'a, R, W>, fields: Vec<Field<'a, R, W>>) -> Self {
Self::inner_new(Stream::Borrowed(stream), fields)
}
#[inline]
pub fn new(reader: R, writer: W, fields: Vec<Field<'a, R, W>>) -> Self {
Self::with_owned(MenuStream::new(reader, writer), fields)
}
#[inline]
pub fn new_ref(reader: &'a mut R, writer: &'a mut W, fields: Vec<Field<'a, R, W>>) -> Self {
Self::with_owned(MenuStream::with(reader, writer), fields)
}
pub fn fmt(mut self, fmt: ValueFieldFormatting<'a>) -> Self {
self.fmt = Rc::new(fmt);
for field in self.fields.as_mut_slice() {
field.inherit_fmt(self.fmt.clone());
}
self
}
pub fn title(mut self, title: &'a str) -> Self {
self.title = title;
self
}
#[inline]
fn next_field(&mut self) -> Field<'a, R, W> {
self.fields.next().expect("no more field in the value-menu")
}
}
pub trait MenuBuilder<Output, R, W> {
fn next_output(&mut self) -> MenuResult<Output>;
fn next_output_with(&mut self, stream: &mut MenuStream<R, W>) -> MenuResult<Output>;
fn next_or_default(&mut self) -> Output
where
Output: Default,
{
self.next_output().unwrap_or_default()
}
fn next_or_default_with(&mut self, stream: &mut MenuStream<R, W>) -> Output
where
Output: Default,
{
self.next_output_with(stream).unwrap_or_default()
}
}
impl<Output, R, W> MenuBuilder<Output, R, W> for ValueMenu<'_, R, W>
where
Output: FromStr,
Output::Err: 'static + Debug,
R: BufRead,
W: Write,
{
fn next_output(&mut self) -> MenuResult<Output> {
print_title(&mut self.stream, self.title, &mut self.popped)?;
self.next_field().menu_build(&mut self.stream)
}
fn next_output_with(&mut self, stream: &mut MenuStream<R, W>) -> MenuResult<Output> {
print_title(&mut self.stream, self.title, &mut self.popped)?;
self.next_field().menu_build(stream)
}
fn next_or_default(&mut self) -> Output
where
Output: Default,
{
let field = self.next_field();
next_or_default(&mut self.stream, field, &mut self.popped, self.title)
}
fn next_or_default_with(&mut self, stream: &mut MenuStream<R, W>) -> Output
where
Output: Default,
{
let field = self.next_field();
next_or_default(stream, field, &mut self.popped, self.title)
}
}
fn next_or_default<Output, R, W>(
stream: &mut MenuStream<R, W>,
mut field: Field<'_, R, W>,
popped: &mut bool,
title: &str,
) -> Output
where
Output: FromStr + Default,
Output::Err: 'static + Debug,
W: Write,
R: BufRead,
{
if print_title(stream, title, popped).is_ok() {
field.menu_build_or_default(stream)
} else {
Output::default()
}
}
fn print_title<R, W>(
stream: &mut MenuStream<R, W>,
title: &str,
popped: &mut bool,
) -> MenuResult<()>
where
W: Write,
{
if !*popped && !title.is_empty() {
writeln!(stream, "{}", title)?;
*popped = true;
}
Ok(())
}
impl<'a, R, W> GetStream<'a, R, W> for ValueMenu<'a, R, W> {
#[inline]
fn get_stream(self) -> MenuStream<'a, R, W> {
self.stream.retrieve()
}
}