#![warn(clippy::all)]
#![allow(clippy::type_complexity)]
use core::{fmt::Display, str::FromStr};
use egui::TextBuffer;
mod impls;
#[must_use = "The input parsing buffer must be used in a ui input"]
pub struct ValText<T, E> {
text: String,
parsed_val: Option<Result<T, E>>,
value_parser: Box<dyn Fn(&str) -> Result<T, E>>,
input_validator: Box<dyn Fn(&str, &str, usize) -> bool>,
}
impl<T, E> ValText<T, E> {
pub fn new(
value_parser: impl Fn(&str) -> Result<T, E> + 'static,
input_validator: impl Fn(&str, &str, usize) -> bool + 'static,
) -> Self {
ValText {
text: String::new(),
parsed_val: None,
value_parser: Box::new(value_parser),
input_validator: Box::new(input_validator),
}
}
pub fn new_box(
value_parser: Box<dyn Fn(&str) -> Result<T, E>>,
input_validator: Box<dyn Fn(&str, &str, usize) -> bool>,
) -> Self {
ValText {
text: String::new(),
parsed_val: None,
value_parser,
input_validator,
}
}
pub fn with_parser(validator: impl Fn(&str) -> Result<T, E> + 'static) -> Self {
Self {
text: String::new(),
parsed_val: None,
value_parser: Box::new(validator),
input_validator: Box::new(|_, _, _| true),
}
}
pub fn with_parser_fixed_charset(
parser: impl Fn(&str) -> Result<T, E> + 'static,
charset: &'static [char],
) -> Self {
Self {
text: String::new(),
parsed_val: None,
value_parser: Box::new(parser),
input_validator: Box::new(|_, s, _| s.chars().all(|c| charset.contains(&c))),
}
}
pub const fn get_val(&self) -> Option<Result<&T, &E>> {
match self.parsed_val.as_ref() {
Some(res) => Some(res.as_ref()),
None => None,
}
}
pub fn is_valid(&self) -> bool {
self.parsed_val.as_ref().is_some_and(Result::is_ok)
}
}
impl<T: FromStr> ValText<Option<T>, T::Err> {
pub fn option_parse() -> Self {
Self {
text: String::new(),
parsed_val: None,
value_parser: Box::new(|str| {
if str.is_empty() {
Ok(None)
} else {
str.parse::<T>().map(|t| Some(t))
}
}),
input_validator: Box::new(|_, _, _| true),
}
}
}
impl<T: Display, E> ValText<T, E> {
pub fn set_val(&mut self, val: T) {
self.text = val.to_string();
self.parsed_val = Some(Ok(val));
}
}
impl<T: FromStr> Default for ValText<T, T::Err> {
fn default() -> Self {
Self {
text: String::new(),
parsed_val: None,
value_parser: Box::new(|text| text.parse()),
input_validator: Box::new(|_, _, _| true),
}
}
}
impl<T: 'static, E> TextBuffer for ValText<T, E> {
fn is_mutable(&self) -> bool {
true
}
fn as_str(&self) -> &str {
self.text.as_str()
}
fn insert_text(&mut self, text: &str, char_index: usize) -> usize {
if (self.input_validator)(&self.text, text, char_index) {
let n = self.text.insert_text(text, char_index);
self.parsed_val = Some((self.value_parser)(&self.text));
n
} else {
0
}
}
fn delete_char_range(&mut self, char_range: std::ops::Range<usize>) {
self.text.delete_char_range(char_range);
self.parsed_val = Some((self.value_parser)(&self.text));
}
fn clear(&mut self) {
self.parsed_val = None;
self.text.clear();
}
fn take(&mut self) -> String {
self.parsed_val = None;
self.text.take()
}
fn type_id(&self) -> std::any::TypeId {
std::any::TypeId::of::<T>()
}
}