fixing more code refactorization
This commit is contained in:
@@ -63,6 +63,10 @@ impl<D: DataProvider> AutoCursorFormEditor<D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn close_suggestions(&mut self) {
|
||||||
|
self.editor.close_suggestions();
|
||||||
|
}
|
||||||
|
|
||||||
// === COMMAND BUFFER HANDLING ===
|
// === COMMAND BUFFER HANDLING ===
|
||||||
|
|
||||||
fn clear_command_buffer(&mut self) {
|
fn clear_command_buffer(&mut self) {
|
||||||
@@ -223,10 +227,6 @@ impl<D: DataProvider> AutoCursorFormEditor<D> {
|
|||||||
self.editor.open_suggestions(field_index);
|
self.editor.open_suggestions(field_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close_suggestions(&mut self) {
|
|
||||||
self.editor.close_suggestions();
|
|
||||||
}
|
|
||||||
|
|
||||||
// === MODE TRANSITIONS WITH AUTOMATIC CURSOR MANAGEMENT ===
|
// === MODE TRANSITIONS WITH AUTOMATIC CURSOR MANAGEMENT ===
|
||||||
|
|
||||||
fn enter_edit_mode(&mut self) {
|
fn enter_edit_mode(&mut self) {
|
||||||
@@ -314,7 +314,8 @@ impl<D: DataProvider> AutoCursorFormEditor<D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn current_text(&self) -> &str {
|
fn current_text(&self) -> &str {
|
||||||
self.editor.current_text()
|
let field_index = self.editor.current_field();
|
||||||
|
self.editor.data_provider().field_value(field_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn data_provider(&self) -> &D {
|
fn data_provider(&self) -> &D {
|
||||||
@@ -671,7 +672,6 @@ async fn handle_key_press(
|
|||||||
}
|
}
|
||||||
(AppMode::ReadOnly | AppMode::Highlight, KeyCode::Char('j'), _)
|
(AppMode::ReadOnly | AppMode::Highlight, KeyCode::Char('j'), _)
|
||||||
| (AppMode::ReadOnly | AppMode::Highlight, KeyCode::Down, _) => {
|
| (AppMode::ReadOnly | AppMode::Highlight, KeyCode::Down, _) => {
|
||||||
editor.close_suggestions(); // ⬅ close dropdown
|
|
||||||
editor.move_down();
|
editor.move_down();
|
||||||
let field_names = ["Fruit", "Job", "Language", "Country", "Color"];
|
let field_names = ["Fruit", "Job", "Language", "Country", "Color"];
|
||||||
let field_name = field_names.get(editor.current_field()).unwrap_or(&"Field");
|
let field_name = field_names.get(editor.current_field()).unwrap_or(&"Field");
|
||||||
@@ -680,7 +680,6 @@ async fn handle_key_press(
|
|||||||
}
|
}
|
||||||
(AppMode::ReadOnly | AppMode::Highlight, KeyCode::Char('k'), _)
|
(AppMode::ReadOnly | AppMode::Highlight, KeyCode::Char('k'), _)
|
||||||
| (AppMode::ReadOnly | AppMode::Highlight, KeyCode::Up, _) => {
|
| (AppMode::ReadOnly | AppMode::Highlight, KeyCode::Up, _) => {
|
||||||
editor.close_suggestions(); // ⬅ close dropdown
|
|
||||||
editor.move_up();
|
editor.move_up();
|
||||||
let field_names = ["Fruit", "Job", "Language", "Country", "Color"];
|
let field_names = ["Fruit", "Job", "Language", "Country", "Color"];
|
||||||
let field_name = field_names.get(editor.current_field()).unwrap_or(&"Field");
|
let field_name = field_names.get(editor.current_field()).unwrap_or(&"Field");
|
||||||
@@ -751,11 +750,9 @@ async fn handle_key_press(
|
|||||||
editor.move_right();
|
editor.move_right();
|
||||||
}
|
}
|
||||||
(AppMode::Edit, KeyCode::Up, _) => {
|
(AppMode::Edit, KeyCode::Up, _) => {
|
||||||
editor.close_suggestions();
|
|
||||||
editor.move_up();
|
editor.move_up();
|
||||||
}
|
}
|
||||||
(AppMode::Edit, KeyCode::Down, _) => {
|
(AppMode::Edit, KeyCode::Down, _) => {
|
||||||
editor.close_suggestions();
|
|
||||||
editor.move_down();
|
editor.move_down();
|
||||||
}
|
}
|
||||||
(AppMode::Edit, KeyCode::Home, _) => {
|
(AppMode::Edit, KeyCode::Home, _) => {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
// examples/validation_5.rs
|
// examples/validation_5.rs
|
||||||
//! Enhanced Feature 5: Comprehensive external validation (UI-only) demo with Feature 4 integration
|
//! Enhanced Feature 5: Comprehensive external validation (UI-only) demo with automatic validation
|
||||||
//!
|
//!
|
||||||
//! Demonstrates:
|
//! Demonstrates:
|
||||||
//! - Multiple external validation types: PSC lookup, email domain check, username availability,
|
//! - Multiple external validation types: PSC lookup, email domain check, username availability,
|
||||||
//! API key validation, credit card verification
|
//! API key validation, credit card verification
|
||||||
|
//! - AUTOMATIC validation on field transitions (arrows, Tab, Esc)
|
||||||
//! - Async validation simulation with realistic delays
|
//! - Async validation simulation with realistic delays
|
||||||
//! - Validation caching and debouncing
|
//! - Validation caching and debouncing
|
||||||
//! - Progressive validation (local → remote)
|
//! - Progressive validation (local → remote)
|
||||||
@@ -15,7 +16,8 @@
|
|||||||
//! Controls:
|
//! Controls:
|
||||||
//! - i/a: insert/append
|
//! - i/a: insert/append
|
||||||
//! - Esc: exit edit mode (triggers validation on configured fields)
|
//! - Esc: exit edit mode (triggers validation on configured fields)
|
||||||
//! - Tab/Shift+Tab: next/prev field (triggers validation)
|
//! - Tab/Shift+Tab: next/prev field (triggers validation automatically)
|
||||||
|
//! - Arrow keys: move between fields (triggers validation automatically)
|
||||||
//! - v: manually trigger validation of current field
|
//! - v: manually trigger validation of current field
|
||||||
//! - V: validate all fields
|
//! - V: validate all fields
|
||||||
//! - c: clear external validation state for current field
|
//! - c: clear external validation state for current field
|
||||||
@@ -37,7 +39,7 @@ compile_error!(
|
|||||||
);
|
);
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, Mutex};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::time::{Instant, Duration};
|
use std::time::{Instant, Duration};
|
||||||
|
|
||||||
@@ -546,16 +548,15 @@ impl DataProvider for ValidationDemoData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enhanced editor with comprehensive external validation management
|
/// Enhanced editor with automatic external validation management
|
||||||
struct ValidationDemoEditor<D: DataProvider> {
|
struct ValidationDemoEditor<D: DataProvider> {
|
||||||
editor: FormEditor<D>,
|
editor: FormEditor<D>,
|
||||||
services: ValidationServices,
|
services: Arc<Mutex<ValidationServices>>,
|
||||||
validation_history: Vec<(usize, String, ValidationResult)>,
|
validation_history: Vec<(usize, String, ValidationResult)>,
|
||||||
debug_message: String,
|
debug_message: String,
|
||||||
show_history: bool,
|
show_history: bool,
|
||||||
example_mode: usize,
|
example_mode: usize,
|
||||||
validation_enabled: bool,
|
validation_enabled: bool,
|
||||||
auto_validate: bool,
|
|
||||||
validation_stats: HashMap<usize, (u32, Duration)>, // field -> (count, total_time)
|
validation_stats: HashMap<usize, (u32, Duration)>, // field -> (count, total_time)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -564,15 +565,70 @@ impl<D: DataProvider> ValidationDemoEditor<D> {
|
|||||||
let mut editor = FormEditor::new(data_provider);
|
let mut editor = FormEditor::new(data_provider);
|
||||||
editor.set_validation_enabled(true);
|
editor.set_validation_enabled(true);
|
||||||
|
|
||||||
|
let services = Arc::new(Mutex::new(ValidationServices::new()));
|
||||||
|
let services_for_cb = Arc::clone(&services);
|
||||||
|
let services_for_history = Arc::clone(&services);
|
||||||
|
|
||||||
|
// Create a history tracker that we'll share between callback and editor
|
||||||
|
let validation_history: Arc<Mutex<Vec<(usize, String, ValidationResult)>>> = Arc::new(Mutex::new(Vec::new()));
|
||||||
|
let history_for_cb = Arc::clone(&validation_history);
|
||||||
|
|
||||||
|
// Library-level automatic external validation on field transitions
|
||||||
|
editor.set_external_validation_callback(move |field_idx, text| {
|
||||||
|
let mut svc = services_for_cb.lock().unwrap();
|
||||||
|
|
||||||
|
let validation_type = match field_idx {
|
||||||
|
0 => "PSC Lookup",
|
||||||
|
1 => "Email Domain Check",
|
||||||
|
2 => "Username Availability",
|
||||||
|
3 => "API Key Auth",
|
||||||
|
4 => "Credit Card Verify",
|
||||||
|
_ => "Unknown",
|
||||||
|
}.to_string();
|
||||||
|
|
||||||
|
let start_time = Instant::now();
|
||||||
|
|
||||||
|
let validation_result = match field_idx {
|
||||||
|
0 => svc.validate_psc(text),
|
||||||
|
1 => svc.validate_email(text),
|
||||||
|
2 => svc.validate_username(text),
|
||||||
|
3 => svc.validate_api_key(text),
|
||||||
|
4 => svc.validate_credit_card(text),
|
||||||
|
_ => ExternalValidationState::NotValidated,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Record in shared history (if we can lock it)
|
||||||
|
if let Ok(mut history) = history_for_cb.try_lock() {
|
||||||
|
let duration = start_time.elapsed();
|
||||||
|
let result = ValidationResult {
|
||||||
|
state: validation_result.clone(),
|
||||||
|
started_at: start_time,
|
||||||
|
completed_at: Some(Instant::now()),
|
||||||
|
validation_type,
|
||||||
|
cached: false, // We could enhance this by checking if it was from cache
|
||||||
|
};
|
||||||
|
|
||||||
|
history.push((field_idx, text.to_string(), result));
|
||||||
|
|
||||||
|
// Limit history size
|
||||||
|
if history.len() > 50 {
|
||||||
|
history.remove(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validation_result
|
||||||
|
});
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
editor,
|
editor,
|
||||||
services: ValidationServices::new(),
|
services,
|
||||||
validation_history: Vec::new(),
|
validation_history: Vec::new(),
|
||||||
debug_message: "🧪 Enhanced External Validation Demo - Multiple validation types with rich scenarios!".to_string(),
|
debug_message:
|
||||||
|
"🧪 Enhanced External Validation Demo - Automatic validation on field transitions!"
|
||||||
|
.to_string(),
|
||||||
show_history: false,
|
show_history: false,
|
||||||
example_mode: 0,
|
example_mode: 0,
|
||||||
validation_enabled: true,
|
validation_enabled: true,
|
||||||
auto_validate: true,
|
|
||||||
validation_stats: HashMap::new(),
|
validation_stats: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -608,7 +664,7 @@ impl<D: DataProvider> ValidationDemoEditor<D> {
|
|||||||
self.current_field() < 5
|
self.current_field() < 5
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trigger external validation for specific field
|
/// Trigger external validation for specific field (manual validation)
|
||||||
fn validate_field(&mut self, field_index: usize) {
|
fn validate_field(&mut self, field_index: usize) {
|
||||||
if !self.validation_enabled || field_index >= 5 {
|
if !self.validation_enabled || field_index >= 5 {
|
||||||
return;
|
return;
|
||||||
@@ -634,14 +690,17 @@ impl<D: DataProvider> ValidationDemoEditor<D> {
|
|||||||
|
|
||||||
let mut result = ValidationResult::new(validation_type.clone());
|
let mut result = ValidationResult::new(validation_type.clone());
|
||||||
|
|
||||||
// Perform validation (in real app, this would be async)
|
// Perform validation using the shared services
|
||||||
let validation_result = match field_index {
|
let validation_result = {
|
||||||
0 => self.services.validate_psc(&raw_value),
|
let mut svc = self.services.lock().unwrap();
|
||||||
1 => self.services.validate_email(&raw_value),
|
match field_index {
|
||||||
2 => self.services.validate_username(&raw_value),
|
0 => svc.validate_psc(&raw_value),
|
||||||
3 => self.services.validate_api_key(&raw_value),
|
1 => svc.validate_email(&raw_value),
|
||||||
4 => self.services.validate_credit_card(&raw_value),
|
2 => svc.validate_username(&raw_value),
|
||||||
|
3 => svc.validate_api_key(&raw_value),
|
||||||
|
4 => svc.validate_credit_card(&raw_value),
|
||||||
_ => ExternalValidationState::NotValidated,
|
_ => ExternalValidationState::NotValidated,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
result = result.complete(validation_result.clone());
|
result = result.complete(validation_result.clone());
|
||||||
@@ -665,7 +724,7 @@ impl<D: DataProvider> ValidationDemoEditor<D> {
|
|||||||
let duration_ms = result.duration().as_millis();
|
let duration_ms = result.duration().as_millis();
|
||||||
let cached_text = if result.cached { " (cached)" } else { "" };
|
let cached_text = if result.cached { " (cached)" } else { "" };
|
||||||
self.debug_message = format!(
|
self.debug_message = format!(
|
||||||
"🔍 {} validation completed in {}ms{}",
|
"🔍 {} validation completed in {}ms{} (manual)",
|
||||||
validation_type, duration_ms, cached_text
|
validation_type, duration_ms, cached_text
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -675,7 +734,7 @@ impl<D: DataProvider> ValidationDemoEditor<D> {
|
|||||||
for i in 0..field_count {
|
for i in 0..field_count {
|
||||||
self.validate_field(i);
|
self.validate_field(i);
|
||||||
}
|
}
|
||||||
self.debug_message = "🔍 All fields validated".to_string();
|
self.debug_message = "🔍 All fields validated manually".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_validation_state(&mut self, field_index: Option<usize>) {
|
fn clear_validation_state(&mut self, field_index: Option<usize>) {
|
||||||
@@ -690,7 +749,9 @@ impl<D: DataProvider> ValidationDemoEditor<D> {
|
|||||||
}
|
}
|
||||||
self.validation_history.clear();
|
self.validation_history.clear();
|
||||||
self.validation_stats.clear();
|
self.validation_stats.clear();
|
||||||
self.services.clear_cache();
|
if let Ok(mut svc) = self.services.lock() {
|
||||||
|
svc.clear_cache();
|
||||||
|
}
|
||||||
self.debug_message = "🧹 Cleared all validation states and cache".to_string();
|
self.debug_message = "🧹 Cleared all validation states and cache".to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -720,7 +781,7 @@ impl<D: DataProvider> ValidationDemoEditor<D> {
|
|||||||
fn cycle_examples(&mut self) {
|
fn cycle_examples(&mut self) {
|
||||||
let examples = [
|
let examples = [
|
||||||
// Valid examples
|
// Valid examples
|
||||||
vec!["01001", "user@gmail.com", "alice_dev", "valid_api_key_123456789012345", "4000123456789012", "Valid data"],
|
vec!["01001", "user@gmail.com", "alice_dev_new", "valid_api_key_123456789012345", "4000123456789012", "Valid data"],
|
||||||
// Invalid examples
|
// Invalid examples
|
||||||
vec!["00000", "invalid-email", "admin", "short_key", "0000000000000000", "Invalid data"],
|
vec!["00000", "invalid-email", "admin", "short_key", "0000000000000000", "Invalid data"],
|
||||||
// Warning examples
|
// Warning examples
|
||||||
@@ -739,7 +800,7 @@ impl<D: DataProvider> ValidationDemoEditor<D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mode_names = ["Valid Examples", "Invalid Examples", "Warning Cases", "Mixed Scenarios"];
|
let mode_names = ["Valid Examples", "Invalid Examples", "Warning Cases", "Mixed Scenarios"];
|
||||||
self.debug_message = format!("📋 Loaded: {}", mode_names[self.example_mode]);
|
self.debug_message = format!("📋 Loaded: {} (navigate to trigger validation)", mode_names[self.example_mode]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_validation_summary(&self) -> String {
|
fn get_validation_summary(&self) -> String {
|
||||||
@@ -758,7 +819,7 @@ impl<D: DataProvider> ValidationDemoEditor<D> {
|
|||||||
self.editor.ui_state().validation_state().get_external_validation(field_index)
|
self.editor.ui_state().validation_state().get_external_validation(field_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Editor pass-through methods
|
// Editor pass-through methods - simplified since library handles automatic validation
|
||||||
fn enter_edit_mode(&mut self) {
|
fn enter_edit_mode(&mut self) {
|
||||||
self.editor.enter_edit_mode();
|
self.editor.enter_edit_mode();
|
||||||
let rules = self.field_validation_rules();
|
let rules = self.field_validation_rules();
|
||||||
@@ -766,34 +827,36 @@ impl<D: DataProvider> ValidationDemoEditor<D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn exit_edit_mode(&mut self) {
|
fn exit_edit_mode(&mut self) {
|
||||||
let current_field = self.current_field();
|
|
||||||
self.editor.exit_edit_mode();
|
self.editor.exit_edit_mode();
|
||||||
|
// Library automatically validates on exit, no manual call needed
|
||||||
// Auto-validate on blur if enabled
|
self.debug_message = format!("🔒 NORMAL - Cursor: Steady Block █ - {} (auto-validated)", self.field_type());
|
||||||
if self.auto_validate && self.has_external_validation() {
|
|
||||||
self.validate_field(current_field);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.debug_message = format!("🔒 NORMAL - Cursor: Steady Block █ - {}", self.field_type());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_field(&mut self) {
|
fn next_field(&mut self) {
|
||||||
let current = self.current_field();
|
|
||||||
if let Ok(()) = self.editor.next_field() {
|
if let Ok(()) = self.editor.next_field() {
|
||||||
if self.auto_validate && current < 5 {
|
// Library triggers external validation automatically via transition_to_field()
|
||||||
self.validate_field(current);
|
self.debug_message = "➡ Next field (auto-validation triggered by library)".to_string();
|
||||||
}
|
|
||||||
self.debug_message = "➡ Next field".to_string();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prev_field(&mut self) {
|
fn prev_field(&mut self) {
|
||||||
let current = self.current_field();
|
|
||||||
if let Ok(()) = self.editor.prev_field() {
|
if let Ok(()) = self.editor.prev_field() {
|
||||||
if self.auto_validate && current < 5 {
|
// Library triggers external validation automatically via transition_to_field()
|
||||||
self.validate_field(current);
|
self.debug_message = "⬅ Previous field (auto-validation triggered by library)".to_string();
|
||||||
}
|
}
|
||||||
self.debug_message = "⬅ Previous field".to_string();
|
}
|
||||||
|
|
||||||
|
fn move_up(&mut self) {
|
||||||
|
if let Ok(()) = self.editor.move_up() {
|
||||||
|
// Library triggers external validation automatically via transition_to_field()
|
||||||
|
self.debug_message = "⬆ Move up (auto-validation triggered by library)".to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_down(&mut self) {
|
||||||
|
if let Ok(()) = self.editor.move_down() {
|
||||||
|
// Library triggers external validation automatically via transition_to_field()
|
||||||
|
self.debug_message = "⬇ Move down (auto-validation triggered by library)".to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -839,16 +902,25 @@ fn run_app<B: Backend>(
|
|||||||
},
|
},
|
||||||
(_, KeyCode::Esc, _) => editor.exit_edit_mode(),
|
(_, KeyCode::Esc, _) => editor.exit_edit_mode(),
|
||||||
|
|
||||||
// Movement - cursor within field
|
// Movement - these now trigger automatic validation via the library!
|
||||||
(_, KeyCode::Left, _) | (AppMode::ReadOnly, KeyCode::Char('h'), _) => { let _ = editor.editor.move_left(); },
|
(_, KeyCode::Left, _) | (AppMode::ReadOnly, KeyCode::Char('h'), _) => {
|
||||||
(_, KeyCode::Right, _) | (AppMode::ReadOnly, KeyCode::Char('l'), _) => { let _ = editor.editor.move_right(); },
|
let _ = editor.editor.move_left();
|
||||||
(_, KeyCode::Up, _) | (AppMode::ReadOnly, KeyCode::Char('k'), _) => { let _ = editor.editor.move_up(); },
|
},
|
||||||
(_, KeyCode::Down, _) | (AppMode::ReadOnly, KeyCode::Char('j'), _) => { let _ = editor.editor.move_down(); },
|
(_, KeyCode::Right, _) | (AppMode::ReadOnly, KeyCode::Char('l'), _) => {
|
||||||
// Field switching
|
let _ = editor.editor.move_right();
|
||||||
|
},
|
||||||
|
(_, KeyCode::Up, _) | (AppMode::ReadOnly, KeyCode::Char('k'), _) => {
|
||||||
|
editor.move_up(); // Use wrapper to get debug message
|
||||||
|
},
|
||||||
|
(_, KeyCode::Down, _) | (AppMode::ReadOnly, KeyCode::Char('j'), _) => {
|
||||||
|
editor.move_down(); // Use wrapper to get debug message
|
||||||
|
},
|
||||||
|
|
||||||
|
// Field switching - these trigger automatic validation via the library!
|
||||||
(_, KeyCode::Tab, _) => editor.next_field(),
|
(_, KeyCode::Tab, _) => editor.next_field(),
|
||||||
(_, KeyCode::BackTab, _) => editor.prev_field(),
|
(_, KeyCode::BackTab, _) => editor.prev_field(),
|
||||||
|
|
||||||
// Validation commands (ONLY in ReadOnly mode)
|
// Manual validation commands (ONLY in ReadOnly mode)
|
||||||
(AppMode::ReadOnly, KeyCode::Char('v'), _) => {
|
(AppMode::ReadOnly, KeyCode::Char('v'), _) => {
|
||||||
let field = editor.current_field();
|
let field = editor.current_field();
|
||||||
editor.validate_field(field);
|
editor.validate_field(field);
|
||||||
@@ -868,8 +940,8 @@ fn run_app<B: Backend>(
|
|||||||
// Editing
|
// Editing
|
||||||
(AppMode::Edit, KeyCode::Left, _) => { let _ = editor.editor.move_left(); },
|
(AppMode::Edit, KeyCode::Left, _) => { let _ = editor.editor.move_left(); },
|
||||||
(AppMode::Edit, KeyCode::Right, _) => { let _ = editor.editor.move_right(); },
|
(AppMode::Edit, KeyCode::Right, _) => { let _ = editor.editor.move_right(); },
|
||||||
(AppMode::Edit, KeyCode::Up, _) => { let _ = editor.editor.move_up(); },
|
(AppMode::Edit, KeyCode::Up, _) => { editor.move_up(); },
|
||||||
(AppMode::Edit, KeyCode::Down, _) => { let _ = editor.editor.move_down(); },
|
(AppMode::Edit, KeyCode::Down, _) => { editor.move_down(); },
|
||||||
(AppMode::Edit, KeyCode::Char(c), m) if !m.contains(KeyModifiers::CONTROL) => {
|
(AppMode::Edit, KeyCode::Char(c), m) if !m.contains(KeyModifiers::CONTROL) => {
|
||||||
let _ = editor.insert_char(c);
|
let _ = editor.insert_char(c);
|
||||||
},
|
},
|
||||||
@@ -922,19 +994,18 @@ fn render_validation_panel(
|
|||||||
|
|
||||||
let summary = editor.get_validation_summary();
|
let summary = editor.get_validation_summary();
|
||||||
let status_text = format!(
|
let status_text = format!(
|
||||||
"-- {} -- {} | {} | Auto: {} | View: {}",
|
"-- {} -- {} | {} | View: {}",
|
||||||
mode_text,
|
mode_text,
|
||||||
editor.debug_message,
|
editor.debug_message,
|
||||||
summary,
|
summary,
|
||||||
if editor.auto_validate { "ON" } else { "OFF" },
|
|
||||||
if editor.show_history { "HISTORY" } else { "STATUS" }
|
if editor.show_history { "HISTORY" } else { "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("🧪 External Validation Demo"));
|
.block(Block::default().borders(Borders::ALL).title("🧪 Automatic External Validation Demo"));
|
||||||
f.render_widget(status, chunks[0]);
|
f.render_widget(status, chunks[0]);
|
||||||
|
|
||||||
// Validation states for all fields - FIXED: render each field on its own line
|
// Validation states for all fields - render each field on its own line
|
||||||
let mut field_lines: Vec<Line> = Vec::new();
|
let mut field_lines: Vec<Line> = Vec::new();
|
||||||
for i in 0..editor.data_provider().field_count() {
|
for i in 0..editor.data_provider().field_count() {
|
||||||
let field_name = editor.data_provider().field_name(i);
|
let field_name = editor.data_provider().field_name(i);
|
||||||
@@ -969,9 +1040,8 @@ fn render_validation_panel(
|
|||||||
field_lines.push(field_line);
|
field_lines.push(field_line);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use Vec<Line> to avoid a single long line overflowing
|
|
||||||
let validation_states = Paragraph::new(field_lines)
|
let validation_states = Paragraph::new(field_lines)
|
||||||
.block(Block::default().borders(Borders::ALL).title("🔍 Validation States"));
|
.block(Block::default().borders(Borders::ALL).title("🔍 Validation States (Library Auto-triggered)"));
|
||||||
f.render_widget(validation_states, chunks[1]);
|
f.render_widget(validation_states, chunks[1]);
|
||||||
|
|
||||||
// History or Help panel
|
// History or Help panel
|
||||||
@@ -1014,36 +1084,36 @@ fn render_validation_panel(
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let history = List::new(recent_history)
|
let history = List::new(recent_history)
|
||||||
.block(Block::default().borders(Borders::ALL).title("📜 Validation History (recent 5)"));
|
.block(Block::default().borders(Borders::ALL).title("📜 Auto-Validation History (recent 5)"));
|
||||||
f.render_widget(history, chunks[2]);
|
f.render_widget(history, chunks[2]);
|
||||||
} else {
|
} else {
|
||||||
let help_text = match editor.mode() {
|
let help_text = match editor.mode() {
|
||||||
AppMode::ReadOnly => {
|
AppMode::ReadOnly => {
|
||||||
"🎯 CURSOR-STYLE: Normal █ | Insert |\n\
|
"🎯 FULLY AUTOMATIC VALIDATION: Library handles all validation on field transitions!\n\
|
||||||
🧪 EXTERNAL VALIDATION DEMO - Multiple validation types with async simulation\n\
|
🧪 EXTERNAL VALIDATION DEMO - No manual triggers needed, just navigate!\n\
|
||||||
\n\
|
\n\
|
||||||
Commands: v=validate current, V=validate all, c=clear current, C=clear all\n\
|
🚀 AUTOMATIC: Arrow keys, Tab, and Esc trigger validation automatically\n\
|
||||||
e=cycle examples, r=toggle history, h=field help, F1=toggle validation\n\
|
Manual: v=validate current, V=validate all, c=clear current, C=clear all\n\
|
||||||
Movement: Tab/Shift+Tab=switch fields, i/a=insert/append, Esc=exit edit\n\
|
Controls: e=cycle examples, r=toggle history, h=field help, F1=toggle validation\n\
|
||||||
\n\
|
\n\
|
||||||
Try different values to see validation in action!"
|
Just load examples and navigate - validation happens automatically!"
|
||||||
}
|
}
|
||||||
AppMode::Edit => {
|
AppMode::Edit => {
|
||||||
"🎯 INSERT MODE - Cursor: | (bar)\n\
|
"🎯 INSERT MODE - Cursor: | (bar)\n\
|
||||||
✏️ Type to see validation on field blur\n\
|
✏️ Type to edit field content\n\
|
||||||
\n\
|
\n\
|
||||||
Current field validation will trigger when you:\n\
|
🚀 AUTOMATIC: Library validates when you leave this field via:\n\
|
||||||
• Press Esc (exit edit mode)\n\
|
• Press Esc (exit edit mode)\n\
|
||||||
• Press Tab (move to next field)\n\
|
• Press Tab/Shift+Tab (move between fields)\n\
|
||||||
• Press 'v' manually\n\
|
• Press arrow keys (Up/Down move between fields)\n\
|
||||||
\n\
|
\n\
|
||||||
Esc=exit edit, arrows=navigate, Backspace/Del=delete"
|
Esc=exit edit, arrows=navigate, Backspace/Del=delete"
|
||||||
}
|
}
|
||||||
_ => "🧪 Enhanced External Validation Demo"
|
_ => "🧪 Enhanced Fully Automatic External Validation Demo"
|
||||||
};
|
};
|
||||||
|
|
||||||
let help = Paragraph::new(help_text)
|
let help = Paragraph::new(help_text)
|
||||||
.block(Block::default().borders(Borders::ALL).title("🚀 External Validation Features"))
|
.block(Block::default().borders(Borders::ALL).title("🚀 Fully Automatic External Validation"))
|
||||||
.style(Style::default().fg(Color::Gray))
|
.style(Style::default().fg(Color::Gray))
|
||||||
.wrap(Wrap { trim: true });
|
.wrap(Wrap { trim: true });
|
||||||
f.render_widget(help, chunks[2]);
|
f.render_widget(help, chunks[2]);
|
||||||
@@ -1051,16 +1121,19 @@ fn render_validation_panel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
println!("🧪 Enhanced External Validation Demo (Feature 5)");
|
println!("🧪 Enhanced Fully Automatic External Validation Demo (Feature 5)");
|
||||||
println!("✅ validation feature: ENABLED");
|
println!("✅ validation feature: ENABLED");
|
||||||
println!("✅ gui feature: ENABLED");
|
println!("✅ gui feature: ENABLED");
|
||||||
println!("✅ cursor-style feature: ENABLED");
|
println!("✅ cursor-style feature: ENABLED");
|
||||||
|
println!("🚀 NEW: Library handles all automatic validation!");
|
||||||
println!("🧪 Enhanced features:");
|
println!("🧪 Enhanced features:");
|
||||||
println!(" • 5 different external validation types with realistic scenarios");
|
println!(" • 5 different external validation types with realistic scenarios");
|
||||||
|
println!(" • LIBRARY-LEVEL automatic validation on all field transitions");
|
||||||
println!(" • Validation caching and performance metrics");
|
println!(" • Validation caching and performance metrics");
|
||||||
println!(" • Comprehensive validation history and error handling");
|
println!(" • Comprehensive validation history and error handling");
|
||||||
println!(" • Multiple example datasets for testing edge cases");
|
println!(" • Multiple example datasets for testing edge cases");
|
||||||
println!(" • Progressive validation patterns (local + remote simulation)");
|
println!(" • Progressive validation patterns (local + remote simulation)");
|
||||||
|
println!(" • NO manual validation calls needed - library handles everything!");
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
@@ -1092,11 +1165,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
println!("{:?}", err);
|
println!("{:?}", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("🧪 Enhanced external validation demo completed!");
|
println!("🧪 Enhanced fully automatic external validation demo completed!");
|
||||||
println!("🏆 You experienced comprehensive external validation with:");
|
println!("🏆 You experienced library-level automatic external validation with:");
|
||||||
println!(" • Multiple validation services (PSC, Email, Username, API Key, Credit Card)");
|
println!(" • Multiple validation services (PSC, Email, Username, API Key, Credit Card)");
|
||||||
|
println!(" • AUTOMATIC validation handled entirely by the library");
|
||||||
println!(" • Realistic async validation simulation with caching");
|
println!(" • Realistic async validation simulation with caching");
|
||||||
println!(" • Comprehensive error handling and user feedback");
|
println!(" • Comprehensive error handling and user feedback");
|
||||||
println!(" • Performance metrics and validation history tracking");
|
println!(" • Performance metrics and validation history tracking");
|
||||||
|
println!(" • Zero manual validation calls needed!");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,13 @@ pub struct FormEditor<D: DataProvider> {
|
|||||||
pub(crate) suggestions: Vec<SuggestionItem>,
|
pub(crate) suggestions: Vec<SuggestionItem>,
|
||||||
|
|
||||||
#[cfg(feature = "validation")]
|
#[cfg(feature = "validation")]
|
||||||
external_validation_callback: Option<Box<dyn FnMut(usize, &str) + Send + Sync>>,
|
external_validation_callback: Option<
|
||||||
|
Box<
|
||||||
|
dyn FnMut(usize, &str) -> crate::validation::ExternalValidationState
|
||||||
|
+ Send
|
||||||
|
+ Sync,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: DataProvider> FormEditor<D> {
|
impl<D: DataProvider> FormEditor<D> {
|
||||||
@@ -59,7 +65,7 @@ impl<D: DataProvider> FormEditor<D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get current field text (convenience method)
|
/// Get current field text (convenience method)
|
||||||
fn current_text(&self) -> &str {
|
pub fn current_text(&self) -> &str {
|
||||||
// Convenience wrapper, kept for compatibility with existing code
|
// Convenience wrapper, kept for compatibility with existing code
|
||||||
let field_index = self.ui_state.current_field;
|
let field_index = self.ui_state.current_field;
|
||||||
if field_index < self.data_provider.field_count() {
|
if field_index < self.data_provider.field_count() {
|
||||||
@@ -208,7 +214,10 @@ impl<D: DataProvider> FormEditor<D> {
|
|||||||
#[cfg(feature = "validation")]
|
#[cfg(feature = "validation")]
|
||||||
pub fn set_external_validation_callback<F>(&mut self, callback: F)
|
pub fn set_external_validation_callback<F>(&mut self, callback: F)
|
||||||
where
|
where
|
||||||
F: FnMut(usize, &str) + Send + Sync + 'static,
|
F: FnMut(usize, &str) -> crate::validation::ExternalValidationState
|
||||||
|
+ Send
|
||||||
|
+ Sync
|
||||||
|
+ 'static,
|
||||||
{
|
{
|
||||||
self.external_validation_callback = Some(Box::new(callback));
|
self.external_validation_callback = Some(Box::new(callback));
|
||||||
}
|
}
|
||||||
@@ -373,9 +382,10 @@ impl<D: DataProvider> FormEditor<D> {
|
|||||||
// Trigger external validation state
|
// Trigger external validation state
|
||||||
self.set_external_validation(prev_field, crate::validation::ExternalValidationState::Validating);
|
self.set_external_validation(prev_field, crate::validation::ExternalValidationState::Validating);
|
||||||
|
|
||||||
// Invoke external callback if registered
|
// Invoke external callback if registered and set final state
|
||||||
if let Some(cb) = self.external_validation_callback.as_mut() {
|
if let Some(cb) = self.external_validation_callback.as_mut() {
|
||||||
cb(prev_field, &text);
|
let final_state = cb(prev_field, &text);
|
||||||
|
self.set_external_validation(prev_field, final_state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -399,6 +409,9 @@ impl<D: DataProvider> FormEditor<D> {
|
|||||||
self.ui_state.current_mode == AppMode::Edit,
|
self.ui_state.current_mode == AppMode::Edit,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Automatically close suggestions on field switch
|
||||||
|
self.close_suggestions();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -785,8 +798,9 @@ impl<D: DataProvider> FormEditor<D> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Update cursor position
|
// Update cursor position
|
||||||
self.ui_state.cursor_pos = suggestion.value_to_store.len();
|
let char_len = suggestion.value_to_store.chars().count();
|
||||||
self.ui_state.ideal_cursor_column = self.ui_state.cursor_pos;
|
self.ui_state.cursor_pos = char_len;
|
||||||
|
self.ui_state.ideal_cursor_column = char_len;
|
||||||
|
|
||||||
// Close suggestions
|
// Close suggestions
|
||||||
self.ui_state.deactivate_suggestions();
|
self.ui_state.deactivate_suggestions();
|
||||||
@@ -811,16 +825,6 @@ impl<D: DataProvider> FormEditor<D> {
|
|||||||
// MOVEMENT METHODS (keeping existing implementations)
|
// MOVEMENT METHODS (keeping existing implementations)
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|
||||||
/// Move to previous field (vim k / up arrow)
|
|
||||||
pub fn move_up_only(&mut self) -> Result<()> {
|
|
||||||
self.move_up()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Move to next field (vim j / down arrow)
|
|
||||||
pub fn move_down_only(&mut self) -> Result<()> {
|
|
||||||
self.move_down()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Move to first line (vim gg)
|
/// Move to first line (vim gg)
|
||||||
pub fn move_first_line(&mut self) -> Result<()> {
|
pub fn move_first_line(&mut self) -> Result<()> {
|
||||||
self.transition_to_field(0)
|
self.transition_to_field(0)
|
||||||
@@ -1165,6 +1169,27 @@ impl<D: DataProvider> FormEditor<D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Trigger external validation on blur/exit edit mode
|
||||||
|
#[cfg(feature = "validation")]
|
||||||
|
{
|
||||||
|
let field_index = self.ui_state.current_field;
|
||||||
|
if let Some(cfg) = self.ui_state.validation.get_field_config(field_index) {
|
||||||
|
if cfg.external_validation_enabled {
|
||||||
|
let text = self.current_text().to_string();
|
||||||
|
if !text.is_empty() {
|
||||||
|
self.set_external_validation(
|
||||||
|
field_index,
|
||||||
|
crate::validation::ExternalValidationState::Validating,
|
||||||
|
);
|
||||||
|
if let Some(cb) = self.external_validation_callback.as_mut() {
|
||||||
|
let final_state = cb(field_index, &text);
|
||||||
|
self.set_external_validation(field_index, final_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.set_mode(AppMode::ReadOnly);
|
self.set_mode(AppMode::ReadOnly);
|
||||||
// Deactivate suggestions when exiting edit mode
|
// Deactivate suggestions when exiting edit mode
|
||||||
self.ui_state.deactivate_suggestions();
|
self.ui_state.deactivate_suggestions();
|
||||||
|
|||||||
Reference in New Issue
Block a user