#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
#[test]
fn string_to_int0(){
let t: Option<u32> = super::string_to_value(&String::from("12981398"));
assert_eq!(t, Option::Some(12981398));
}
#[test]
fn string_to_int1(){
let t: Option<i32> = super::string_to_value(&String::from("-1234"));
assert_eq!(t, Option::Some(-1234));
}
#[test]
fn string_to_int2(){
let t: Option<u8> = super::string_to_value(&String::from("70000"));
assert_eq!(t, Option::None);
}
#[test]
fn string_to_int3(){
let t: Option<i32> = super::string_to_value(&String::from("23ohno23"));
assert_eq!(t, Option::None);
}
#[test]
fn string_to_float0(){
let t: Option<f32> = super::string_to_value(&String::from("34.5"));
assert_eq!(t, Option::Some(34.5));
}
#[test]
fn string_to_float1(){
let t: Option<f64> = super::string_to_value(&String::from("-0.00000000000001"));
assert_eq!(t, Option::Some(-0.00000000000001));
}
#[test]
fn string_to_bool0(){
assert_eq!(super::string_to_bool(&String::from("yes")), true);
}
#[test]
fn string_to_bool1(){
let t: Option<bool> = super::string_to_value(&String::from("true"));
assert_eq!(t, Option::Some(true));
}
#[test]
fn string_to_bool2(){
let t: Option<bool> = super::string_to_value(&String::from("false"));
assert_eq!(t, Option::Some(false));
}
}
use std::io;
use std::io::Write;
use std::io::Read;
use termios::{Termios, TCSANOW, ECHO, ICANON, tcsetattr};
use std::cmp::{ max, min };
use num_derive::ToPrimitive;
use num_traits::ToPrimitive;
use std::slice::Iter;
use std::sync::atomic::AtomicU8;
use std::sync::atomic::Ordering;
use std::collections::VecDeque;
static FG_COL: AtomicU8 = AtomicU8::new(9);
static BG_COL: AtomicU8 = AtomicU8::new(9);
pub fn reset_all(){
print!("\x1B[00m");
}
#[derive(PartialEq,Eq,Clone,ToPrimitive)]
pub enum UserColour {
Std = 9,
Black = 0,
Red = 1,
Green = 2,
Yellow = 3,
Blue = 4,
Magenta = 5,
Cyan = 6,
Grey = 7,
}
impl UserColour {
pub fn iterator() -> Iter<'static, Self> {
static ARR: [UserColour; 9] = [
UserColour::Std,
UserColour::Black,
UserColour::Red,
UserColour::Green,
UserColour::Yellow,
UserColour::Blue,
UserColour::Magenta,
UserColour::Cyan,
UserColour::Grey];
ARR.iter()
}
}
#[derive(PartialEq,Eq,Clone,ToPrimitive)]
pub enum TextStyle {
Std = 0,
Bold = 1,
Faint = 2,
Italic = 3,
Underlined = 4,
Blink = 5,
Hidden = 8,
Crossed = 9,
}
impl TextStyle {
pub fn iterator() -> Iter<'static, Self> {
static ARR: [TextStyle; 8] = [
TextStyle::Std,
TextStyle::Bold,
TextStyle::Faint,
TextStyle::Italic,
TextStyle::Underlined,
TextStyle::Blink,
TextStyle::Hidden,
TextStyle::Crossed];
ARR.iter()
}
}
#[derive(PartialEq,Eq)]
pub enum FGBG { FG, BG }
pub fn set_colour(col: UserColour, fgbg: FGBG){
let _id = ToPrimitive::to_u8(&col);
let mut id = 0;
if let Some(_idv) = _id { id = _idv; }
let mut colorcode = String::from("\x1B[");
if fgbg == FGBG::FG {
colorcode.push('3');
FG_COL.store(id, Ordering::Relaxed);
}else{
colorcode.push('4');
BG_COL.store(id, Ordering::Relaxed);
}
colorcode.push_str(&format!("{}", id));
colorcode.push_str("m");
print!("{}", colorcode);
}
pub fn set_colours(fg: UserColour, bg: UserColour){
set_colour(fg, FGBG::FG);
set_colour(bg, FGBG::BG);
}
pub fn set_style(sty: TextStyle){
let _id = ToPrimitive::to_u8(&sty);
let mut id = 0;
if let Some(_idv) = _id { id = _idv; }
print!("\x1B[00m");
let mut colorcode = String::from("\x1B[0");
colorcode.push_str(&format!("{}", id));
colorcode.push_str("m");
print!("{}", colorcode);
print!("\x1B[3{}m", FG_COL.load(Ordering::Relaxed));
print!("\x1B[4{}m", BG_COL.load(Ordering::Relaxed));
}
pub fn print<T: std::fmt::Display>(msg: T){
print!("{}", msg);
}
pub fn println<T: std::fmt::Display>(msg: T){
println!("{}", msg);
}
pub fn print_col<T: std::fmt::Display>(msg: T, col: UserColour){
set_colour(col, FGBG::FG);
print!("{}", msg);
}
pub fn println_col<T: std::fmt::Display>(msg: T, col: UserColour){
set_colour(col, FGBG::FG);
println!("{}", msg);
}
pub fn print_cols<T: std::fmt::Display>(msg: T, fg: UserColour, bg: UserColour){
set_colours(fg, bg);
print!("{}", msg);
}
pub fn println_cols<T: std::fmt::Display>(msg: T, fg: UserColour, bg: UserColour){
set_colours(fg, bg);
println!("{}", msg);
}
pub fn print_style<T: std::fmt::Display>(msg: T, sty: TextStyle){
set_style(sty);
print!("{}", msg);
}
pub fn println_style<T: std::fmt::Display>(msg: T, sty: TextStyle){
set_style(sty);
println!("{}", msg);
}
pub fn print_cols_style<T: std::fmt::Display>(msg: T, fg: UserColour, bg: UserColour, sty: TextStyle){
set_colours(fg, bg);
set_style(sty);
print!("{}", msg);
}
pub fn println_cols_style<T: std::fmt::Display>(msg: T, fg: UserColour, bg: UserColour, sty: TextStyle){
set_colours(fg, bg);
set_style(sty);
println!("{}", msg);
}
pub fn getch() -> u8{
let stdin = 0;
let termios = Termios::from_fd(stdin).unwrap();
let mut new_termios = termios;
new_termios.c_lflag &= !(ICANON | ECHO);
tcsetattr(stdin, TCSANOW, &new_termios).unwrap();
let stdout = io::stdout();
let mut reader = io::stdin();
let mut buffer = [0;1];
stdout.lock().flush().unwrap();
reader.read_exact(&mut buffer).unwrap();
tcsetattr(stdin, TCSANOW, & termios).unwrap();
buffer[0]
}
pub fn test_chars(){
loop {
println!("{:?}", getch());
}
}
pub struct InputHistory{
history: VecDeque<String>,
maxlen: usize,
}
impl InputHistory{
pub fn new(maxlen: usize) -> Self{
Self{
history: VecDeque::new(),
maxlen
}
}
pub fn trim(&mut self){
self.history.truncate(self.maxlen);
}
pub fn add(&mut self, string: &String){
self.history.push_back(string.clone());
self.trim();
}
pub fn get_index(&self, mut index: i32) -> Option<&String>{
if self.history.len() != 0 {
index %= self.history.len() as i32;
}
self.history.get(index as usize)
}
}
pub fn input_field() -> String{
input_field_scrollable(&mut InputHistory::new(0))
}
pub fn input_field_scrollable(history: &mut InputHistory) -> String{
fn charvec_to_string(vec: &[char]) -> String{
let mut string = String::new();
for &ch in vec {
string.push(ch);
}
string
}
fn typed_char(ch: u8, buff: &mut Vec<char>, gstate: &mut u8, hstate: &mut u8, pos: &mut usize){
let ch = ch as char;
buff.insert(*pos, ch);
if *pos != buff.len() - 1{
for item in buff.iter().skip(*pos){
print!("{}", item);
}
for _ in *pos..buff.len()-1{
print!("{}", 8 as char);
}
}else{
print!("{}", ch);
}
*hstate = 0;
*gstate = 0;
*pos += 1;
}
fn delete_all(buff: &mut Vec<char>){
for _ in 0..buff.len(){
print!("{}", 8 as char);
}
buff.clear();
}
fn feed_into_buffer(buff: &mut Vec<char>, string: &str){
for ch in string.chars(){
buff.push(ch);
}
}
fn delete(res: &mut Vec<char>, pos: &mut usize, gstate: &mut u8){
if res.is_empty() { return; }
if *pos >= res.len() - 1 { return; }
res.remove(*pos);
for item in res.iter().skip(*pos){
print!("{}", item);
}
print!(" ");
for _ in *pos..res.len()+1{
print!("{}", 8 as char);
}
*gstate = 0;
}
fn end(res: &mut Vec<char>, pos: &mut usize, hoen_state: &mut u8){
for _ in *pos..res.len() {
print!("\x1B[1C");
}
*pos = res.len();
*hoen_state = 0;
}
let mut res = Vec::new();
let mut gstate: u8 = 0;
let mut hoen_state: u8 = 0;
let mut pos = 0;
let mut his_index: i32 = 0;
loop {
let mut x = getch();
if x == 8 { x = 127; }
match x{
10 => { println!(); break; }
127 => {
if res.is_empty() { continue; }
if pos <= 0 { continue; }
res.remove(pos - 1);
print!("{}", 8 as char);
for item in res.iter().skip(pos-1){
print!("{}", item);
}
print!(" ");
for _ in pos-1..res.len()+1{
print!("{}", 8 as char);
}
pos -= 1;
gstate = 0;
}
27 => {
gstate = 1;
hoen_state = 1;
}
91 => {
if gstate == 1 { gstate = 2; }
if hoen_state == 1 { hoen_state = 2; }
if gstate == 2 || hoen_state == 2 { continue; }
typed_char(91, &mut res, &mut gstate, &mut hoen_state, &mut pos);
}
65 => {
if gstate == 2 {
gstate = 0;
his_index += 1;
let val = history.get_index(his_index);
if let Some(valv) = val{
delete_all(&mut res);
feed_into_buffer(&mut res, valv);
}
}
else { typed_char(65, &mut res, &mut gstate, &mut hoen_state, &mut pos); }
}
66 => {
if gstate == 2 {
gstate = 0;
his_index -= 1;
let val = history.get_index(his_index);
if let Some(valv) = val{
delete_all(&mut res);
feed_into_buffer(&mut res, valv);
}
}
else { typed_char(66, &mut res, &mut gstate, &mut hoen_state, &mut pos); }
}
72 => {
if hoen_state != 2 {
typed_char(72, &mut res, &mut gstate, &mut hoen_state, &mut pos);
continue;
}
for _ in 0..pos {
print!("{}", 8 as char);
}
pos = 0;
hoen_state = 0;
}
51 => {
if gstate != 2 {
typed_char(51, &mut res, &mut gstate, &mut hoen_state, &mut pos);
}
else {
gstate = 3;
}
}
52 => {
if hoen_state == 2 { hoen_state = 3; }
else { typed_char(52, &mut res, &mut gstate, &mut hoen_state, &mut pos); }
}
70 =>{
if hoen_state == 2 {
end(&mut res, &mut pos, &mut hoen_state);
}
else{
typed_char(70, &mut res, &mut gstate, &mut hoen_state, &mut pos);
}
}
126 => {
if hoen_state == 3 {
end(&mut res, &mut pos, &mut hoen_state);
}
else if gstate >= 2{
delete(&mut res, &mut pos, &mut gstate);
}
else {
typed_char(126, &mut res, &mut gstate, &mut hoen_state, &mut pos);
}
}
80 => {
if gstate != 2 {
typed_char(80, &mut res, &mut gstate, &mut hoen_state, &mut pos);
continue;
}
delete(&mut res, &mut pos, &mut gstate);
}
67 => {
if gstate == 2 {
if pos < res.len() { print!("\x1B[1C"); }
gstate = 0;
pos = min(pos + 1, res.len());
}
else { typed_char(67, &mut res, &mut gstate, &mut hoen_state, &mut pos); }
}
68 => {
if gstate == 2 {
if pos > 0 { print!("{}", 8 as char); }
gstate = 0;
pos = max(pos as i32 - 1, 0 as i32) as usize;
}
else { typed_char(68, &mut res, &mut gstate, &mut hoen_state, &mut pos); }
}
x => { typed_char(x, &mut res, &mut gstate, &mut hoen_state, &mut pos); }
}
}
charvec_to_string(&res)
}
pub fn string_to_bool(string: &str) -> bool{
match string{
"y" => true,
"ye" => true,
"yes" => true,
"ok" => true,
"+" => true,
"t" => true,
"tr" => true,
"tru" => true,
"true" => true,
_ => false,
}
}
pub fn string_to_value<T: std::str::FromStr>(string: &str) -> Option<T>{
let res = string.parse::<T>();
if res.is_err() { return Option::None; }
res.ok()
}
pub fn flush() -> io::Result<()>{
std::io::stdout().flush()
}
pub fn prompt(msg : &str) -> String{
print(msg);
flush().expect("Error: stdout flush failed.");
input_field()
}