use crate::
{
buffer::TextBuffer,
builder::ValidatorFn,
error::Error,
keys::{ handle_key, KeyAction },
render::{ self, RenderConfig },
terminal::{ RealTerminal, TerminalOps },
};
pub struct Editor< T = RealTerminal >
where
T: TerminalOps,
{
pub allow_empty: bool,
pub min_length: Option< usize >,
pub max_length: Option< usize >,
pub validator: Option< ValidatorFn >,
pub initial_text: Option< String >,
pub render_config: RenderConfig,
pub terminal: T,
}
impl< T > Editor< T >
where
T: TerminalOps,
{
pub fn collect( mut self ) -> Result< Option< String >, Error >
{
self.terminal.enable_raw_mode()?;
self.terminal.hide_cursor()?;
let mut buffer = if let Some( initial ) = &self.initial_text
{
TextBuffer::with_text( initial )
}
else
{
TextBuffer::new()
};
let result = loop
{
render::render( &mut self.terminal, &buffer, &self.render_config )?;
self.terminal.show_cursor()?;
let key = self.terminal.read_key( None )?;
match handle_key( key, &mut buffer )
{
KeyAction::Submit =>
{
let text = buffer.text();
match self.validate( &text )
{
Ok( () ) =>
{
break Some( text );
}
Err( err ) =>
{
let _ = err;
continue;
}
}
}
KeyAction::Cancel =>
{
break None;
}
KeyAction::Continue =>
{
continue;
}
}
};
self.terminal.show_cursor()?;
match &result
{
Some( text ) =>
{
render::render_result( &mut self.terminal, text, &self.render_config )?;
}
None =>
{
render::render_cancelled( &mut self.terminal, &self.render_config )?;
}
}
self.terminal.disable_raw_mode()?;
Ok( result )
}
pub fn validate( &self, text: &str ) -> Result< (), String >
{
if !self.allow_empty && text.is_empty()
{
return Err( "Input cannot be empty".to_string() );
}
if let Some( min ) = self.min_length
{
if text.chars().count() < min
{
return Err( format!( "Input too short (min {} chars)", min ) );
}
}
if let Some( max ) = self.max_length
{
if text.chars().count() > max
{
return Err( format!( "Input too long (max {} chars)", max ) );
}
}
if let Some( validator ) = &self.validator
{
validator( text )?;
}
Ok( () )
}
}