chore: add \!file_exists condition

This commit is contained in:
Jesús Pérez 2025-12-28 18:16:50 +00:00
parent 25e779a390
commit f7f7fec13b
Signed by: jesus
GPG Key ID: 9F243E355E0BC939

View File

@ -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<String, serde_json::Value>) -> 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<String, serde_json:
true
}
/// Evaluate function calls in conditions
/// Returns Some(bool) if a function call was found and evaluated
/// Returns None if no function call was found
fn evaluate_function_call(condition: &str) -> Option<bool> {
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();
}
}