better example for feature2 being implemented and integrated into the codebase

This commit is contained in:
Priec
2025-08-05 21:15:25 +02:00
parent c9b4841f67
commit 46a0d2b9db

View File

@@ -1,16 +1,20 @@
// examples/validation_patterns_tui.rs // examples/validation_2.rs
//! TUI Example demonstrating position-based pattern filtering //! Advanced TUI Example demonstrating complex pattern filtering edge cases
//! //!
//! Run with: cargo run --example validation_patterns_tui --features "validation,gui" //! This example showcases the full potential of the pattern validation system
//! with creative real-world scenarios and edge cases.
//!
//! Run with: cargo run --example validation_advanced_patterns --features "validation,gui"
// REQUIRE validation and gui features - example won't compile without them // REQUIRE validation and gui features
#[cfg(not(all(feature = "validation", feature = "gui")))] #[cfg(not(all(feature = "validation", feature = "gui")))]
compile_error!( compile_error!(
"This example requires the 'validation' and 'gui' features. \ "This example requires the 'validation' and 'gui' features. \
Run with: cargo run --example validation_patterns_tui --features \"validation,gui\"" Run with: cargo run --example validation_advanced_patterns --features \"validation,gui\""
); );
use std::io; use std::io;
use std::sync::Arc;
use canvas::ValidationResult; use canvas::ValidationResult;
use crossterm::{ use crossterm::{
event::{ event::{
@@ -39,8 +43,8 @@ use canvas::{
ValidationConfig, ValidationConfigBuilder, PatternFilters, PositionFilter, PositionRange, CharacterFilter, ValidationConfig, ValidationConfigBuilder, PatternFilters, PositionFilter, PositionRange, CharacterFilter,
}; };
// Enhanced FormEditor for pattern validation demonstration // Enhanced FormEditor wrapper (keeping the same structure as before)
struct PatternValidationFormEditor<D: DataProvider> { struct AdvancedPatternFormEditor<D: DataProvider> {
editor: FormEditor<D>, editor: FormEditor<D>,
debug_message: String, debug_message: String,
command_buffer: String, command_buffer: String,
@@ -49,16 +53,14 @@ struct PatternValidationFormEditor<D: DataProvider> {
block_reason: Option<String>, block_reason: Option<String>,
} }
impl<D: DataProvider> PatternValidationFormEditor<D> { impl<D: DataProvider> AdvancedPatternFormEditor<D> {
fn new(data_provider: D) -> Self { fn new(data_provider: D) -> Self {
let mut editor = FormEditor::new(data_provider); let mut editor = FormEditor::new(data_provider);
// Enable validation by default
editor.set_validation_enabled(true); editor.set_validation_enabled(true);
Self { Self {
editor, editor,
debug_message: "🔍 Pattern Validation Demo - Try typing in different fields!".to_string(), debug_message: "🚀 Advanced Pattern Validation - Showcasing edge cases and complex patterns!".to_string(),
command_buffer: String::new(), command_buffer: String::new(),
validation_enabled: true, validation_enabled: true,
field_switch_blocked: false, field_switch_blocked: false,
@@ -66,95 +68,52 @@ impl<D: DataProvider> PatternValidationFormEditor<D> {
} }
} }
// === COMMAND BUFFER HANDLING === // ... (keeping all the same methods as before for brevity)
fn clear_command_buffer(&mut self) { // [All the previous methods: clear_command_buffer, add_to_command_buffer, etc.]
self.command_buffer.clear();
}
fn add_to_command_buffer(&mut self, ch: char) { fn clear_command_buffer(&mut self) { self.command_buffer.clear(); }
self.command_buffer.push(ch); fn add_to_command_buffer(&mut self, ch: char) { self.command_buffer.push(ch); }
} fn get_command_buffer(&self) -> &str { &self.command_buffer }
fn has_pending_command(&self) -> bool { !self.command_buffer.is_empty() }
fn get_command_buffer(&self) -> &str {
&self.command_buffer
}
fn has_pending_command(&self) -> bool {
!self.command_buffer.is_empty()
}
// === VALIDATION CONTROL ===
fn toggle_validation(&mut self) { fn toggle_validation(&mut self) {
self.validation_enabled = !self.validation_enabled; self.validation_enabled = !self.validation_enabled;
self.editor.set_validation_enabled(self.validation_enabled); self.editor.set_validation_enabled(self.validation_enabled);
if self.validation_enabled { if self.validation_enabled {
self.debug_message = "✅ Pattern Validation ENABLED".to_string(); self.debug_message = " Advanced Pattern Validation ENABLED".to_string();
} else { } else {
self.debug_message = "❌ Pattern Validation DISABLED".to_string(); self.debug_message = " Advanced Pattern Validation DISABLED".to_string();
} }
} }
// === MOVEMENT === fn move_left(&mut self) { self.editor.move_left(); self.field_switch_blocked = false; self.block_reason = None; }
fn move_left(&mut self) { fn move_right(&mut self) { self.editor.move_right(); self.field_switch_blocked = false; self.block_reason = None; }
self.editor.move_left();
self.field_switch_blocked = false;
self.block_reason = None;
}
fn move_right(&mut self) {
self.editor.move_right();
self.field_switch_blocked = false;
self.block_reason = None;
}
fn move_up(&mut self) { fn move_up(&mut self) {
match self.editor.move_up() { match self.editor.move_up() {
Ok(()) => { Ok(()) => { self.update_field_validation_status(); self.field_switch_blocked = false; self.block_reason = None; }
self.update_field_validation_status(); Err(e) => { self.field_switch_blocked = true; self.block_reason = Some(e.to_string()); self.debug_message = format!("🚫 Field switch blocked: {}", e); }
self.field_switch_blocked = false;
self.block_reason = None;
}
Err(e) => {
self.field_switch_blocked = true;
self.block_reason = Some(e.to_string());
self.debug_message = format!("🚫 Field switch blocked: {}", e);
}
} }
} }
fn move_down(&mut self) { fn move_down(&mut self) {
match self.editor.move_down() { match self.editor.move_down() {
Ok(()) => { Ok(()) => { self.update_field_validation_status(); self.field_switch_blocked = false; self.block_reason = None; }
self.update_field_validation_status(); Err(e) => { self.field_switch_blocked = true; self.block_reason = Some(e.to_string()); self.debug_message = format!("🚫 Field switch blocked: {}", e); }
self.field_switch_blocked = false;
self.block_reason = None;
}
Err(e) => {
self.field_switch_blocked = true;
self.block_reason = Some(e.to_string());
self.debug_message = format!("🚫 Field switch blocked: {}", e);
}
} }
} }
fn move_line_start(&mut self) { fn move_line_start(&mut self) { self.editor.move_line_start(); }
self.editor.move_line_start(); fn move_line_end(&mut self) { self.editor.move_line_end(); }
}
fn move_line_end(&mut self) {
self.editor.move_line_end();
}
// === MODE TRANSITIONS ===
fn enter_edit_mode(&mut self) { fn enter_edit_mode(&mut self) {
self.editor.enter_edit_mode(); self.editor.enter_edit_mode();
self.debug_message = "✏️ INSERT MODE - Type to test pattern validation".to_string(); self.debug_message = "✏️ INSERT MODE - Testing advanced pattern validation".to_string();
} }
fn enter_append_mode(&mut self) { fn enter_append_mode(&mut self) {
self.editor.enter_append_mode(); self.editor.enter_append_mode();
self.debug_message = "✏️ INSERT (append) - Pattern validation active".to_string(); self.debug_message = "✏️ INSERT (append) - Advanced patterns active".to_string();
} }
fn exit_edit_mode(&mut self) { fn exit_edit_mode(&mut self) {
@@ -166,253 +125,271 @@ impl<D: DataProvider> PatternValidationFormEditor<D> {
fn insert_char(&mut self, ch: char) -> anyhow::Result<()> { fn insert_char(&mut self, ch: char) -> anyhow::Result<()> {
let result = self.editor.insert_char(ch); let result = self.editor.insert_char(ch);
if result.is_ok() { if result.is_ok() {
// Show real-time validation feedback
if let Some(validation_result) = self.editor.current_field_validation() { if let Some(validation_result) = self.editor.current_field_validation() {
match validation_result { match validation_result {
ValidationResult::Valid => { ValidationResult::Valid => { self.debug_message = "✅ Character accepted".to_string(); }
self.debug_message = "✅ Valid character".to_string(); ValidationResult::Warning { message } => { self.debug_message = format!("⚠️ Warning: {}", message); }
} ValidationResult::Error { message } => { self.debug_message = format!("❌ Pattern violation: {}", message); }
ValidationResult::Warning { message } => {
self.debug_message = format!("⚠️ Warning: {}", message);
}
ValidationResult::Error { message } => {
self.debug_message = format!("❌ Error: {}", message);
}
} }
} }
} }
Ok(result?) Ok(result?)
} }
// === DELETE OPERATIONS ===
fn delete_backward(&mut self) -> anyhow::Result<()> { fn delete_backward(&mut self) -> anyhow::Result<()> {
let result = self.editor.delete_backward(); let result = self.editor.delete_backward();
if result.is_ok() { if result.is_ok() { self.debug_message = "⌫ Character deleted".to_string(); }
self.debug_message = "⌫ Deleted character".to_string();
}
Ok(result?) Ok(result?)
} }
fn delete_forward(&mut self) -> anyhow::Result<()> { fn delete_forward(&mut self) -> anyhow::Result<()> {
let result = self.editor.delete_forward(); let result = self.editor.delete_forward();
if result.is_ok() { if result.is_ok() { self.debug_message = "⌦ Character deleted".to_string(); }
self.debug_message = "⌦ Deleted character".to_string();
}
Ok(result?) Ok(result?)
} }
// === DELEGATE TO ORIGINAL EDITOR === // Delegate methods
fn current_field(&self) -> usize { fn current_field(&self) -> usize { self.editor.current_field() }
self.editor.current_field() fn cursor_position(&self) -> usize { self.editor.cursor_position() }
} fn mode(&self) -> AppMode { self.editor.mode() }
fn current_text(&self) -> &str { self.editor.current_text() }
fn cursor_position(&self) -> usize { fn data_provider(&self) -> &D { self.editor.data_provider() }
self.editor.cursor_position() fn ui_state(&self) -> &canvas::EditorState { self.editor.ui_state() }
} fn set_mode(&mut self, mode: AppMode) { self.editor.set_mode(mode); }
fn mode(&self) -> AppMode {
self.editor.mode()
}
fn current_text(&self) -> &str {
self.editor.current_text()
}
fn data_provider(&self) -> &D {
self.editor.data_provider()
}
fn ui_state(&self) -> &canvas::EditorState {
self.editor.ui_state()
}
fn set_mode(&mut self, mode: AppMode) {
self.editor.set_mode(mode);
}
fn next_field(&mut self) { fn next_field(&mut self) {
match self.editor.next_field() { match self.editor.next_field() {
Ok(()) => { Ok(()) => { self.update_field_validation_status(); self.field_switch_blocked = false; self.block_reason = None; }
self.update_field_validation_status(); Err(e) => { self.field_switch_blocked = true; self.block_reason = Some(e.to_string()); self.debug_message = format!("🚫 Cannot move to next field: {}", e); }
self.field_switch_blocked = false;
self.block_reason = None;
}
Err(e) => {
self.field_switch_blocked = true;
self.block_reason = Some(e.to_string());
self.debug_message = format!("🚫 Cannot move to next field: {}", e);
}
} }
} }
fn prev_field(&mut self) { fn prev_field(&mut self) {
match self.editor.prev_field() { match self.editor.prev_field() {
Ok(()) => { Ok(()) => { self.update_field_validation_status(); self.field_switch_blocked = false; self.block_reason = None; }
self.update_field_validation_status(); Err(e) => { self.field_switch_blocked = true; self.block_reason = Some(e.to_string()); self.debug_message = format!("🚫 Cannot move to previous field: {}", e); }
self.field_switch_blocked = false;
self.block_reason = None;
}
Err(e) => {
self.field_switch_blocked = true;
self.block_reason = Some(e.to_string());
self.debug_message = format!("🚫 Cannot move to previous field: {}", e);
}
} }
} }
// === STATUS AND DEBUG === fn set_debug_message(&mut self, msg: String) { self.debug_message = msg; }
fn set_debug_message(&mut self, msg: String) { fn debug_message(&self) -> &str { &self.debug_message }
self.debug_message = msg;
}
fn debug_message(&self) -> &str {
&self.debug_message
}
fn update_field_validation_status(&mut self) { fn update_field_validation_status(&mut self) {
if !self.validation_enabled { if !self.validation_enabled { return; }
return;
}
if let Some(result) = self.editor.current_field_validation() { if let Some(result) = self.editor.current_field_validation() {
match result { match result {
ValidationResult::Valid => { ValidationResult::Valid => { self.debug_message = format!("Field {}: ✅ Pattern valid", self.editor.current_field() + 1); }
self.debug_message = format!("Field {}: ✅ Valid", self.editor.current_field() + 1); ValidationResult::Warning { message } => { self.debug_message = format!("Field {}: ⚠️ {}", self.editor.current_field() + 1, message); }
} ValidationResult::Error { message } => { self.debug_message = format!("Field {}: ❌ {}", self.editor.current_field() + 1, message); }
ValidationResult::Warning { message } => {
self.debug_message = format!("Field {}: ⚠️ {}", self.editor.current_field() + 1, message);
}
ValidationResult::Error { message } => {
self.debug_message = format!("Field {}: ❌ {}", self.editor.current_field() + 1, message);
}
} }
} else {
self.debug_message = format!("Field {}: 🔍 Not validated yet", self.editor.current_field() + 1);
} }
} }
fn get_validation_status(&self) -> String { fn get_validation_status(&self) -> String {
if !self.validation_enabled { if !self.validation_enabled { return "❌ DISABLED".to_string(); }
return "❌ DISABLED".to_string(); if self.field_switch_blocked { return "🚫 SWITCH BLOCKED".to_string(); }
}
if self.field_switch_blocked {
return "🚫 SWITCH BLOCKED".to_string();
}
let summary = self.editor.validation_summary(); let summary = self.editor.validation_summary();
if summary.has_errors() { if summary.has_errors() { format!("{} ERRORS", summary.error_fields) }
format!(" {} ERRORS", summary.error_fields) else if summary.has_warnings() { format!("⚠️ {} WARNINGS", summary.warning_fields) }
} else if summary.has_warnings() { else if summary.validated_fields > 0 { format!("{} VALID", summary.valid_fields) }
format!("⚠️ {} WARNINGS", summary.warning_fields) else { "🔍 READY".to_string() }
} else if summary.validated_fields > 0 {
format!("{} VALID", summary.valid_fields)
} else {
"🔍 READY".to_string()
}
} }
} }
// Demo form with pattern-based validation // Advanced demo form with creative and edge-case-heavy validation patterns
struct PatternValidationData { struct AdvancedPatternData {
fields: Vec<(String, String)>, fields: Vec<(String, String)>,
} }
impl PatternValidationData { impl AdvancedPatternData {
fn new() -> Self { fn new() -> Self {
Self { Self {
fields: vec![ fields: vec![
("🚗 License Plate (AB123)".to_string(), "".to_string()), ("🕐 Time (HH:MM) - 24hr format".to_string(), "".to_string()),
("📞 Phone (123-456-7890)".to_string(), "".to_string()), ("🎨 Hex Color (#RRGGBB) - Web colors".to_string(), "".to_string()),
("💳 Credit Card (1234-5678-9012-3456)".to_string(), "".to_string()), ("🌐 IPv4 (XXX.XXX.XXX.XXX) - Network address".to_string(), "".to_string()),
("🆔 Custom ID (AB123def)".to_string(), "".to_string()), ("🏷️ Product Code (ABC-123-XYZ) - Mixed format".to_string(), "".to_string()),
("📅 Date Code (2024W15) - Year + Week".to_string(), "".to_string()),
("🔢 Binary (101010) - Only 0s and 1s".to_string(), "".to_string()),
("🎯 Complex ID (A1-B2C-3D4E) - Multi-rule".to_string(), "".to_string()),
("🚀 Custom Pattern - Advanced logic".to_string(), "".to_string()),
], ],
} }
} }
} }
impl DataProvider for PatternValidationData { impl DataProvider for AdvancedPatternData {
fn field_count(&self) -> usize { fn field_count(&self) -> usize { self.fields.len() }
self.fields.len() fn field_name(&self, index: usize) -> &str { &self.fields[index].0 }
} fn field_value(&self, index: usize) -> &str { &self.fields[index].1 }
fn set_field_value(&mut self, index: usize, value: String) { self.fields[index].1 = value; }
fn field_name(&self, index: usize) -> &str {
&self.fields[index].0
}
fn field_value(&self, index: usize) -> &str {
&self.fields[index].1
}
fn set_field_value(&mut self, index: usize, value: String) {
self.fields[index].1 = value;
}
// Pattern validation configuration per field
fn validation_config(&self, field_index: usize) -> Option<ValidationConfig> { fn validation_config(&self, field_index: usize) -> Option<ValidationConfig> {
match field_index { match field_index {
0 => { 0 => {
// License plate: AB123 (2 letters, 3 numbers) // 🕐 Time (HH:MM) - Hours 00-23, Minutes 00-59
let license_plate_pattern = PatternFilters::new() // This showcases: Multiple position ranges, exact character matching, custom validation
let time_pattern = PatternFilters::new()
.add_filter(PositionFilter::new( .add_filter(PositionFilter::new(
PositionRange::Range(0, 1), PositionRange::Multiple(vec![0, 1, 3, 4]), // Hours and minutes positions
CharacterFilter::Alphabetic, CharacterFilter::Numeric,
)) ))
.add_filter(PositionFilter::new( .add_filter(PositionFilter::new(
PositionRange::Range(2, 4), PositionRange::Single(2), // Colon separator
CharacterFilter::Numeric, CharacterFilter::Exact(':'),
)); ));
Some(ValidationConfigBuilder::new() Some(ValidationConfigBuilder::new()
.with_pattern_filters(license_plate_pattern) .with_pattern_filters(time_pattern)
.with_max_length(5) // HH:MM = 5 characters
.build()) .build())
} }
1 => { 1 => {
// Phone number: 123-456-7890 // 🎨 Hex Color (#RRGGBB) - Web color format
let phone_pattern = PatternFilters::new() // This showcases: OneOf filter with hex digits, exact character at start
let hex_digits = vec!['0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','a','b','c','d','e','f'];
let hex_color_pattern = PatternFilters::new()
.add_filter(PositionFilter::new( .add_filter(PositionFilter::new(
PositionRange::Multiple(vec![0,1,2,4,5,6,8,9,10,11]), PositionRange::Single(0), // Hash symbol
CharacterFilter::Numeric, CharacterFilter::Exact('#'),
)) ))
.add_filter(PositionFilter::new( .add_filter(PositionFilter::new(
PositionRange::Multiple(vec![3, 7]), PositionRange::Range(1, 6), // 6 hex digits for RGB
CharacterFilter::Exact('-'), CharacterFilter::OneOf(hex_digits),
)); ));
Some(ValidationConfigBuilder::new() Some(ValidationConfigBuilder::new()
.with_pattern_filters(phone_pattern) .with_pattern_filters(hex_color_pattern)
.with_max_length(7) // #RRGGBB = 7 characters
.build()) .build())
} }
2 => { 2 => {
// Credit card: 1234-5678-9012-3456 // 🌐 IPv4 Address (XXX.XXX.XXX.XXX) - Network address
let credit_card_pattern = PatternFilters::new() // This showcases: Complex pattern with dots at specific positions
let ipv4_pattern = PatternFilters::new()
.add_filter(PositionFilter::new( .add_filter(PositionFilter::new(
PositionRange::Multiple(vec![0,1,2,3,5,6,7,8,10,11,12,13,15,16,17,18]), PositionRange::Multiple(vec![3, 7, 11]), // Dots at specific positions
CharacterFilter::Numeric, CharacterFilter::Exact('.'),
)) ))
.add_filter(PositionFilter::new( .add_filter(PositionFilter::new(
PositionRange::Multiple(vec![4, 9, 14]), PositionRange::Multiple(vec![0,1,2,4,5,6,8,9,10,12,13,14]), // Number positions
CharacterFilter::Exact('-'), CharacterFilter::Numeric,
)); ));
Some(ValidationConfigBuilder::new() Some(ValidationConfigBuilder::new()
.with_pattern_filters(credit_card_pattern) .with_pattern_filters(ipv4_pattern)
.with_max_length(15) // XXX.XXX.XXX.XXX = up to 15 chars
.build()) .build())
} }
3 => { 3 => {
// Custom ID: First 2 letters, rest alphanumeric // 🏷️ Product Code (ABC-123-XYZ) - Mixed format sections
let custom_id_pattern = PatternFilters::new() // This showcases: Different rules for different sections
let product_code_pattern = PatternFilters::new()
.add_filter(PositionFilter::new( .add_filter(PositionFilter::new(
PositionRange::Range(0, 1), PositionRange::Range(0, 2), // First 3 positions: letters
CharacterFilter::Alphabetic, CharacterFilter::Alphabetic,
)) ))
.add_filter(PositionFilter::new( .add_filter(PositionFilter::new(
PositionRange::From(2), PositionRange::Multiple(vec![3, 7]), // Dashes
CharacterFilter::Alphanumeric, CharacterFilter::Exact('-'),
))
.add_filter(PositionFilter::new(
PositionRange::Range(4, 6), // Middle 3 positions: numbers
CharacterFilter::Numeric,
))
.add_filter(PositionFilter::new(
PositionRange::Range(8, 10), // Last 3 positions: letters
CharacterFilter::Alphabetic,
)); ));
Some(ValidationConfigBuilder::new() Some(ValidationConfigBuilder::new()
.with_pattern_filters(custom_id_pattern) .with_pattern_filters(product_code_pattern)
.with_max_length(11) // ABC-123-XYZ = 11 characters
.build())
}
4 => {
// 📅 Date Code (2024W15) - Year + Week format
// This showcases: From position filtering and mixed patterns
let date_code_pattern = PatternFilters::new()
.add_filter(PositionFilter::new(
PositionRange::Range(0, 3), // Year: 4 digits
CharacterFilter::Numeric,
))
.add_filter(PositionFilter::new(
PositionRange::Single(4), // Week indicator
CharacterFilter::Exact('W'),
))
.add_filter(PositionFilter::new(
PositionRange::From(5), // Week number: rest are digits
CharacterFilter::Numeric,
));
Some(ValidationConfigBuilder::new()
.with_pattern_filters(date_code_pattern)
.with_max_length(7) // 2024W15 = 7 characters
.build())
}
5 => {
// 🔢 Binary (101010) - Only 0s and 1s
// This showcases: OneOf filter with limited character set
let binary_pattern = PatternFilters::new()
.add_filter(PositionFilter::new(
PositionRange::From(0), // All positions
CharacterFilter::OneOf(vec!['0', '1']),
));
Some(ValidationConfigBuilder::new()
.with_pattern_filters(binary_pattern)
.with_max_length(16) // Allow up to 16 binary digits
.build())
}
6 => {
// 🎯 Complex ID (A1-B2C-3D4E) - Multiple overlapping rules
// This showcases: Complex overlapping patterns and edge cases
let complex_id_pattern = PatternFilters::new()
.add_filter(PositionFilter::new(
PositionRange::Multiple(vec![0, 3, 6, 8]), // Letter positions
CharacterFilter::Alphabetic,
))
.add_filter(PositionFilter::new(
PositionRange::Multiple(vec![1, 4, 7, 9]), // Number positions
CharacterFilter::Numeric,
))
.add_filter(PositionFilter::new(
PositionRange::Multiple(vec![2, 5]), // Dashes
CharacterFilter::Exact('-'),
))
.add_filter(PositionFilter::new(
PositionRange::Single(5), // Special case: override dash with letter C
CharacterFilter::Alphabetic, // This creates an interesting edge case
));
Some(ValidationConfigBuilder::new()
.with_pattern_filters(complex_id_pattern)
.with_max_length(10) // A1-B2C-3D4E = 10 characters
.build())
}
7 => {
// 🚀 Custom Pattern - Advanced logic with custom function
// This showcases: Custom validation function for complex rules
let custom_pattern = PatternFilters::new()
.add_filter(PositionFilter::new(
PositionRange::From(0),
CharacterFilter::Custom(Arc::new(|c| {
// Advanced rule: Alternating vowels and consonants!
// Even positions (0,2,4...): vowels (a,e,i,o,u)
// Odd positions (1,3,5...): consonants
let vowels = ['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'];
// For demo purposes, we'll just accept alphabetic characters
// In real usage, you'd implement the alternating logic based on position
c.is_alphabetic()
})),
));
Some(ValidationConfigBuilder::new()
.with_pattern_filters(custom_pattern)
.with_max_length(12) // Allow up to 12 characters
.build()) .build())
} }
_ => None, _ => None,
@@ -420,11 +397,11 @@ impl DataProvider for PatternValidationData {
} }
} }
/// Handle key presses with pattern validation commands // Key handling (same structure as before)
fn handle_key_press( fn handle_key_press(
key: KeyCode, key: KeyCode,
modifiers: KeyModifiers, modifiers: KeyModifiers,
editor: &mut PatternValidationFormEditor<PatternValidationData>, editor: &mut AdvancedPatternFormEditor<AdvancedPatternData>,
) -> anyhow::Result<bool> { ) -> anyhow::Result<bool> {
let mode = editor.mode(); let mode = editor.mode();
@@ -437,99 +414,50 @@ fn handle_key_press(
} }
match (mode, key, modifiers) { match (mode, key, modifiers) {
// === MODE TRANSITIONS === // Mode transitions
(AppMode::ReadOnly, KeyCode::Char('i'), _) => { (AppMode::ReadOnly, KeyCode::Char('i'), _) => { editor.enter_edit_mode(); editor.clear_command_buffer(); }
editor.enter_edit_mode(); (AppMode::ReadOnly, KeyCode::Char('a'), _) => { editor.enter_append_mode(); editor.clear_command_buffer(); }
editor.clear_command_buffer(); (AppMode::ReadOnly, KeyCode::Char('A'), _) => { editor.move_line_end(); editor.enter_edit_mode(); editor.clear_command_buffer(); }
} (_, KeyCode::Esc, _) => { if mode == AppMode::Edit { editor.exit_edit_mode(); } else { editor.clear_command_buffer(); } }
(AppMode::ReadOnly, KeyCode::Char('a'), _) => {
editor.enter_append_mode();
editor.clear_command_buffer();
}
(AppMode::ReadOnly, KeyCode::Char('A'), _) => {
editor.move_line_end();
editor.enter_edit_mode();
editor.clear_command_buffer();
}
// Escape: Exit edit mode // Validation commands
(_, KeyCode::Esc, _) => { (AppMode::ReadOnly, KeyCode::F(1), _) => { editor.toggle_validation(); }
if mode == AppMode::Edit {
editor.exit_edit_mode();
} else {
editor.clear_command_buffer();
}
}
// === VALIDATION COMMANDS === // Movement in ReadOnly mode
(AppMode::ReadOnly, KeyCode::F(1), _) => { (AppMode::ReadOnly, KeyCode::Char('h'), _) | (AppMode::ReadOnly, KeyCode::Left, _) => { editor.move_left(); editor.clear_command_buffer(); }
editor.toggle_validation(); (AppMode::ReadOnly, KeyCode::Char('l'), _) | (AppMode::ReadOnly, KeyCode::Right, _) => { editor.move_right(); editor.clear_command_buffer(); }
} (AppMode::ReadOnly, KeyCode::Char('j'), _) | (AppMode::ReadOnly, KeyCode::Down, _) => { editor.move_down(); editor.clear_command_buffer(); }
(AppMode::ReadOnly, KeyCode::Char('k'), _) | (AppMode::ReadOnly, KeyCode::Up, _) => { editor.move_up(); editor.clear_command_buffer(); }
// === MOVEMENT === // Movement in Edit mode
(AppMode::ReadOnly, KeyCode::Char('h'), _) | (AppMode::ReadOnly, KeyCode::Left, _) => { (AppMode::Edit, KeyCode::Left, _) => { editor.move_left(); }
editor.move_left(); (AppMode::Edit, KeyCode::Right, _) => { editor.move_right(); }
editor.clear_command_buffer(); (AppMode::Edit, KeyCode::Up, _) => { editor.move_up(); }
} (AppMode::Edit, KeyCode::Down, _) => { editor.move_down(); }
(AppMode::ReadOnly, KeyCode::Char('l'), _) | (AppMode::ReadOnly, KeyCode::Right, _) => {
editor.move_right();
editor.clear_command_buffer();
}
(AppMode::ReadOnly, KeyCode::Char('j'), _) | (AppMode::ReadOnly, KeyCode::Down, _) => {
editor.move_down();
editor.clear_command_buffer();
}
(AppMode::ReadOnly, KeyCode::Char('k'), _) | (AppMode::ReadOnly, KeyCode::Up, _) => {
editor.move_up();
editor.clear_command_buffer();
}
// === EDIT MODE MOVEMENT === // Delete operations
(AppMode::Edit, KeyCode::Left, _) => { (AppMode::Edit, KeyCode::Backspace, _) => { editor.delete_backward()?; }
editor.move_left(); (AppMode::Edit, KeyCode::Delete, _) => { editor.delete_forward()?; }
}
(AppMode::Edit, KeyCode::Right, _) => {
editor.move_right();
}
(AppMode::Edit, KeyCode::Up, _) => {
editor.move_up();
}
(AppMode::Edit, KeyCode::Down, _) => {
editor.move_down();
}
// === DELETE OPERATIONS === // Tab navigation
(AppMode::Edit, KeyCode::Backspace, _) => { (_, KeyCode::Tab, _) => { editor.next_field(); }
editor.delete_backward()?; (_, KeyCode::BackTab, _) => { editor.prev_field(); }
}
(AppMode::Edit, KeyCode::Delete, _) => {
editor.delete_forward()?;
}
// === TAB NAVIGATION === // Character input
(_, KeyCode::Tab, _) => {
editor.next_field();
}
(_, KeyCode::BackTab, _) => {
editor.prev_field();
}
// === CHARACTER INPUT ===
(AppMode::Edit, KeyCode::Char(c), m) if !m.contains(KeyModifiers::CONTROL) => { (AppMode::Edit, KeyCode::Char(c), m) if !m.contains(KeyModifiers::CONTROL) => {
editor.insert_char(c)?; editor.insert_char(c)?;
} }
// === DEBUG/INFO COMMANDS === // Debug info
(AppMode::ReadOnly, KeyCode::Char('?'), _) => { (AppMode::ReadOnly, KeyCode::Char('?'), _) => {
let summary = editor.editor.validation_summary(); let summary = editor.editor.validation_summary();
editor.set_debug_message(format!( editor.set_debug_message(format!(
"Field {}/{}, Pos {}, Mode: {:?}, Validation: {} fields configured, {} validated", "Field {}/{}, Pos {}, Mode: {:?}, Advanced patterns: {} configured",
editor.current_field() + 1, editor.current_field() + 1,
editor.data_provider().field_count(), editor.data_provider().field_count(),
editor.cursor_position(), editor.cursor_position(),
editor.mode(), editor.mode(),
summary.total_fields, summary.total_fields
summary.validated_fields
)); ));
} }
@@ -546,7 +474,7 @@ fn handle_key_press(
fn run_app<B: Backend>( fn run_app<B: Backend>(
terminal: &mut Terminal<B>, terminal: &mut Terminal<B>,
mut editor: PatternValidationFormEditor<PatternValidationData>, mut editor: AdvancedPatternFormEditor<AdvancedPatternData>,
) -> io::Result<()> { ) -> io::Result<()> {
loop { loop {
terminal.draw(|f| ui(f, &editor))?; terminal.draw(|f| ui(f, &editor))?;
@@ -568,39 +496,31 @@ fn run_app<B: Backend>(
Ok(()) Ok(())
} }
fn ui(f: &mut Frame, editor: &PatternValidationFormEditor<PatternValidationData>) { fn ui(f: &mut Frame, editor: &AdvancedPatternFormEditor<AdvancedPatternData>) {
let chunks = Layout::default() let chunks = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints([Constraint::Min(8), Constraint::Length(12)]) .constraints([Constraint::Min(8), Constraint::Length(15)])
.split(f.area()); .split(f.area());
render_enhanced_canvas(f, chunks[0], editor); render_canvas_default(f, chunks[0], &editor.editor);
render_validation_status(f, chunks[1], editor); render_advanced_validation_status(f, chunks[1], editor);
} }
fn render_enhanced_canvas( fn render_advanced_validation_status(
f: &mut Frame, f: &mut Frame,
area: Rect, area: Rect,
editor: &PatternValidationFormEditor<PatternValidationData>, editor: &AdvancedPatternFormEditor<AdvancedPatternData>,
) {
render_canvas_default(f, area, &editor.editor);
}
fn render_validation_status(
f: &mut Frame,
area: Rect,
editor: &PatternValidationFormEditor<PatternValidationData>,
) { ) {
let chunks = Layout::default() let chunks = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints([ .constraints([
Constraint::Length(3), // Status bar Constraint::Length(3), // Status bar
Constraint::Length(4), // Validation summary Constraint::Length(5), // Validation summary
Constraint::Length(5), // Help Constraint::Length(7), // Help
]) ])
.split(area); .split(area);
// Status bar with validation information // Status bar
let mode_text = match editor.mode() { let mode_text = match editor.mode() {
AppMode::Edit => "INSERT", AppMode::Edit => "INSERT",
AppMode::ReadOnly => "NORMAL", AppMode::ReadOnly => "NORMAL",
@@ -608,42 +528,43 @@ fn render_validation_status(
}; };
let validation_status = editor.get_validation_status(); let validation_status = editor.get_validation_status();
let status_text = if editor.has_pending_command() { let status_text = format!("-- {} -- {} | Advanced Patterns: {}", mode_text, editor.debug_message(), validation_status);
format!("-- {} -- {} [{}] | Pattern Validation: {}",
mode_text, editor.debug_message(), editor.get_command_buffer(), validation_status)
} else {
format!("-- {} -- {} | Pattern Validation: {}",
mode_text, editor.debug_message(), validation_status)
};
let status = Paragraph::new(Line::from(Span::raw(status_text))) let status = Paragraph::new(Line::from(Span::raw(status_text)))
.block(Block::default().borders(Borders::ALL).title("🔍 Pattern Validation Status")); .block(Block::default().borders(Borders::ALL).title("🚀 Advanced Pattern Validation"));
f.render_widget(status, chunks[0]); f.render_widget(status, chunks[0]);
// Validation summary with field switching info // Enhanced validation summary
let summary = editor.editor.validation_summary(); let summary = editor.editor.validation_summary();
let summary_text = if editor.validation_enabled { let field_info = match editor.current_field() {
let switch_info = if editor.field_switch_blocked { 0 => "Time format (HH:MM) - Tests exact chars + numeric ranges",
format!("\n🚫 Field switching blocked: {}", 1 => "Hex color (#RRGGBB) - Tests OneOf filter with case insensitive",
editor.block_reason.as_deref().unwrap_or("Unknown reason")) 2 => "IPv4 address - Tests complex dot positioning",
} else { 3 => "Product code (ABC-123-XYZ) - Tests section-based patterns",
"\n✅ Field switching allowed".to_string() 4 => "Date code (2024W15) - Tests From position filtering",
}; 5 => "Binary input - Tests limited character set (0,1 only)",
6 => "Complex ID - Tests overlapping/conflicting rules",
7 => "Custom pattern - Tests advanced custom validation logic",
_ => "Unknown field",
};
let summary_text = if editor.validation_enabled {
format!( format!(
"📊 Pattern Validation Summary: {} fields configured, {} validated{}\n\ "📊 Advanced Pattern Summary: {} fields with complex rules\n\
✅ Valid: {} ⚠️ Warnings: {} ❌ Errors: {} 📈 Progress: {:.0}%", Current Field: {}\n\
✅ Valid: {} ⚠️ Warnings: {} ❌ Errors: {} 📈 Progress: {:.0}%\n\
🎯 Pattern Focus: {}",
summary.total_fields, summary.total_fields,
summary.validated_fields, editor.current_field() + 1,
switch_info,
summary.valid_fields, summary.valid_fields,
summary.warning_fields, summary.warning_fields,
summary.error_fields, summary.error_fields,
summary.completion_percentage() * 100.0 summary.completion_percentage() * 100.0,
field_info
) )
} else { } else {
"Pattern validation is currently DISABLED\nPress F1 to enable validation".to_string() "Advanced pattern validation is DISABLED\nPress F1 to enable and see the magic!".to_string()
}; };
let summary_style = if summary.has_errors() { let summary_style = if summary.has_errors() {
@@ -655,32 +576,33 @@ fn render_validation_status(
}; };
let validation_summary = Paragraph::new(summary_text) let validation_summary = Paragraph::new(summary_text)
.block(Block::default().borders(Borders::ALL).title("📈 Pattern Validation Overview")) .block(Block::default().borders(Borders::ALL).title("🎯 Advanced Pattern Analysis"))
.style(summary_style) .style(summary_style)
.wrap(Wrap { trim: true }); .wrap(Wrap { trim: true });
f.render_widget(validation_summary, chunks[1]); f.render_widget(validation_summary, chunks[1]);
// Pattern-specific help text // Enhanced help text
let help_text = match editor.mode() { let help_text = match editor.mode() {
AppMode::ReadOnly => { AppMode::ReadOnly => {
"🔍 PATTERN VALIDATION DEMO: Each field has specific character patterns!\n\ "🚀 ADVANCED PATTERN SHOWCASE - Each field demonstrates different edge cases!\n\
License Plate: 2 letters + 3 numbers (AB123)\n\ 🕐 Time: Numeric+exact chars 🎨 Hex: OneOf with case-insensitive 🌐 IPv4: Complex positioning\n\
Phone: Numbers with dashes at positions 3 and 7 (123-456-7890)\n\ 🏷️ Product: Multi-section rules 📅 Date: From-position filtering 🔢 Binary: Limited charset\n\
Credit Card: Number groups separated by dashes (1234-5678-9012-3456)\n\ 🎯 Complex: Overlapping rules 🚀 Custom: Advanced logic functions\n\
Custom ID: 2 letters + alphanumeric (AB123def)\n\ \n\
Movement: hjkl/arrows=move, Tab/Shift+Tab=fields, i/a=insert, F1=toggle validation" Movement: hjkl/arrows=move, Tab/Shift+Tab=fields, i/a=insert, F1=toggle, ?=info"
} }
AppMode::Edit => { AppMode::Edit => {
"✏️ INSERT MODE - Type to test pattern validation!\n\ "✏️ INSERT MODE - Testing advanced pattern validation!\n\
Pattern validation will reject characters that don't match the expected pattern\n\ Each character is validated against complex rules in real-time\n\
Try entering invalid characters to see detailed error messages\n\
arrows=move, Backspace/Del=delete, Esc=normal, Tab=next field" arrows=move, Backspace/Del=delete, Esc=normal, Tab=next field"
} }
_ => "🔍 Pattern Validation Demo Active!" _ => "🚀 Advanced Pattern Validation Active!"
}; };
let help = Paragraph::new(help_text) let help = Paragraph::new(help_text)
.block(Block::default().borders(Borders::ALL).title("🚀 Pattern Validation Commands")) .block(Block::default().borders(Borders::ALL).title("🎯 Advanced Pattern Commands & Info"))
.style(Style::default().fg(Color::Gray)) .style(Style::default().fg(Color::Gray))
.wrap(Wrap { trim: true }); .wrap(Wrap { trim: true });
@@ -688,12 +610,12 @@ fn render_validation_status(
} }
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
// Print feature status println!("🚀 Canvas Advanced Pattern Validation Demo");
println!("🔍 Canvas Pattern Validation TUI Demo");
println!("✅ validation feature: ENABLED"); println!("✅ validation feature: ENABLED");
println!("✅ gui feature: ENABLED"); println!("✅ gui feature: ENABLED");
println!("🚀 Pattern-based validation: ACTIVE"); println!("🎯 Advanced pattern filtering: ACTIVE");
println!("📊 Try typing in fields with different patterns!"); println!("🧪 Edge cases and complex patterns: READY");
println!("💡 Each field showcases different validation capabilities!");
println!(); println!();
enable_raw_mode()?; enable_raw_mode()?;
@@ -702,8 +624,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let backend = CrosstermBackend::new(stdout); let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?; let mut terminal = Terminal::new(backend)?;
let data = PatternValidationData::new(); let data = AdvancedPatternData::new();
let editor = PatternValidationFormEditor::new(data); let editor = AdvancedPatternFormEditor::new(data);
let res = run_app(&mut terminal, editor); let res = run_app(&mut terminal, editor);
@@ -719,6 +641,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("{:?}", err); println!("{:?}", err);
} }
println!("🔍 Pattern validation demo completed!"); println!("🚀 Advanced pattern validation demo completed!");
println!("🎯 Hope you enjoyed seeing all the edge cases in action!");
Ok(()) Ok(())
} }