diff --git a/crates/typedialog-core/src/form_parser/conditions.rs b/crates/typedialog-core/src/form_parser/conditions.rs index fd0421a..45faf97 100644 --- a/crates/typedialog-core/src/form_parser/conditions.rs +++ b/crates/typedialog-core/src/form_parser/conditions.rs @@ -3,6 +3,7 @@ //! Provides logic for evaluating `when` conditions on form elements. use std::collections::HashMap; +use std::path::Path; use super::types::FormDefinition; @@ -120,9 +121,16 @@ pub fn should_load_fragment( /// - "field_name contains value" /// - "field_name startswith value" /// - "value in field_name" (array membership) +/// - "file_exists(path)" (file existence check) +/// - "!file_exists(path)" (negated file existence check) pub fn evaluate_condition(condition: &str, results: &HashMap) -> bool { let condition = condition.trim(); + // Check for function calls first (file_exists, etc.) + if let Some(result) = evaluate_function_call(condition) { + return result; + } + // Check string operators first (word boundaries) let string_operators = ["contains", "startswith", "endswith", "in"]; for op_str in &string_operators { @@ -246,6 +254,44 @@ pub fn evaluate_condition(condition: &str, results: &HashMap Option { + let condition = condition.trim(); + + // Handle negation prefix + let (is_negated, clean_condition) = if let Some(stripped) = condition.strip_prefix('!') { + (true, stripped.trim()) + } else { + (false, condition) + }; + + // Check for file_exists() function + if clean_condition.starts_with("file_exists(") && clean_condition.ends_with(')') { + let start = "file_exists(".len(); + let end = clean_condition.len() - 1; + let path_arg = clean_condition[start..end].trim(); + + // Remove quotes if present + let path = if (path_arg.starts_with('"') && path_arg.ends_with('"')) + || (path_arg.starts_with('\'') && path_arg.ends_with('\'')) + { + &path_arg[1..path_arg.len() - 1] + } else { + path_arg + }; + + // Check if file exists + let exists = Path::new(path).exists(); + + // Apply negation if needed + return Some(if is_negated { !exists } else { exists }); + } + + None +} + /// Parse a value from condition right-hand side fn parse_condition_value(s: &str) -> serde_json::Value { let s = s.trim(); @@ -467,4 +513,87 @@ mod tests { assert!(evaluate_condition("enable_feature == true", &results)); assert!(evaluate_condition("name == test", &results)); } + + #[test] + fn test_file_exists_with_existing_file() { + let results = HashMap::new(); + + // Create a temporary file + let temp_dir = std::env::temp_dir(); + let test_file = temp_dir.join("typedialog_test_file.txt"); + std::fs::write(&test_file, "test content").unwrap(); + + // Test with double quotes + let condition = format!("file_exists(\"{}\")", test_file.display()); + assert!(evaluate_condition(&condition, &results)); + + // Test with single quotes + let condition = format!("file_exists('{}')", test_file.display()); + assert!(evaluate_condition(&condition, &results)); + + // Test without quotes + let condition = format!("file_exists({})", test_file.display()); + assert!(evaluate_condition(&condition, &results)); + + // Clean up + std::fs::remove_file(&test_file).unwrap(); + } + + #[test] + fn test_file_exists_with_nonexistent_file() { + let results = HashMap::new(); + + // Use a path that definitely doesn't exist + let nonexistent = "/tmp/typedialog_nonexistent_file_12345.txt"; + + let condition = format!("file_exists(\"{}\")", nonexistent); + assert!(!evaluate_condition(&condition, &results)); + } + + #[test] + fn test_file_exists_negated() { + let results = HashMap::new(); + + // Create a temporary file + let temp_dir = std::env::temp_dir(); + let test_file = temp_dir.join("typedialog_test_negation.txt"); + std::fs::write(&test_file, "test").unwrap(); + + // Negated: should return false for existing file + let condition = format!("!file_exists(\"{}\")", test_file.display()); + assert!(!evaluate_condition(&condition, &results)); + + // Clean up + std::fs::remove_file(&test_file).unwrap(); + + // After removal, negated should return true + let condition = format!("!file_exists(\"{}\")", test_file.display()); + assert!(evaluate_condition(&condition, &results)); + } + + #[test] + fn test_file_exists_with_directory() { + let results = HashMap::new(); + + // Test with an existing directory + let temp_dir = std::env::temp_dir(); + let condition = format!("file_exists(\"{}\")", temp_dir.display()); + + // Path::exists() returns true for both files and directories + assert!(evaluate_condition(&condition, &results)); + } + + #[test] + fn test_file_exists_with_relative_path() { + let results = HashMap::new(); + + // Create a file in current directory + std::fs::write("typedialog_test_relative.txt", "test").unwrap(); + + let condition = "file_exists(\"typedialog_test_relative.txt\")"; + assert!(evaluate_condition(condition, &results)); + + // Clean up + std::fs::remove_file("typedialog_test_relative.txt").unwrap(); + } }