#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ChunkOptionValue {
Simple(String),
Expression(String),
}
pub fn hashpipe_comment_prefix(language: &str) -> Option<&'static str> {
match language.to_ascii_lowercase().as_str() {
"r" | "python" | "julia" | "bash" | "shell" | "sh" | "ruby" | "perl" => Some("#|"),
"c" | "cpp" | "c++" | "rcpp" | "java" | "javascript" | "js" | "typescript" | "ts"
| "rust" | "go" | "swift" | "kotlin" | "scala" | "csharp" | "c#" | "php" | "ojs"
| "dot" => Some("//|"),
"sql" | "mysql" | "postgres" | "postgresql" | "sqlite" => Some("--|"),
"mermaid" => Some("%%|"),
_ => None,
}
}
pub fn classify_value(value: &Option<String>) -> ChunkOptionValue {
match value {
None => ChunkOptionValue::Simple(String::new()), Some(v) => {
if is_boolean_literal(v) || is_numeric_literal(v) || is_simple_string(v) {
ChunkOptionValue::Simple(v.clone())
} else {
ChunkOptionValue::Expression(v.clone())
}
}
}
}
fn is_simple_string(s: &str) -> bool {
if s.is_empty() {
return true;
}
if s.contains('(')
|| s.contains(')')
|| s.contains('{')
|| s.contains('}')
|| s.contains('$')
|| s.contains('[')
|| s.contains(']')
|| s.contains('+')
|| s.contains('-')
|| s.contains('*')
|| s.contains('/')
|| s.contains('<')
|| s.contains('>')
|| s.contains('!')
|| s.contains(':')
{
return false;
}
if !s.contains(' ')
&& !s.contains('.')
&& !s.contains('/')
&& !s.contains('\\')
&& !s.contains(',')
&& s.chars().all(|c| c.is_alphanumeric() || c == '_')
{
return false;
}
true
}
pub fn is_boolean_literal(s: &str) -> bool {
matches!(s, "TRUE" | "FALSE" | "T" | "F")
}
pub fn is_numeric_literal(s: &str) -> bool {
s.parse::<f64>().is_ok()
}
pub fn is_quoted_string(s: &str) -> bool {
(s.starts_with('"') && s.ends_with('"') && s.len() >= 2)
|| (s.starts_with('\'') && s.ends_with('\'') && s.len() >= 2)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_boolean_literal() {
assert!(is_boolean_literal("TRUE"));
assert!(is_boolean_literal("FALSE"));
assert!(is_boolean_literal("T"));
assert!(is_boolean_literal("F"));
assert!(!is_boolean_literal("true"));
assert!(!is_boolean_literal("false"));
assert!(!is_boolean_literal("True"));
assert!(!is_boolean_literal("MAYBE"));
}
#[test]
fn test_is_numeric_literal() {
assert!(is_numeric_literal("7"));
assert!(is_numeric_literal("0"));
assert!(is_numeric_literal("-3"));
assert!(is_numeric_literal("100"));
assert!(is_numeric_literal("3.14"));
assert!(is_numeric_literal("-2.5"));
assert!(is_numeric_literal("0.1"));
assert!(is_numeric_literal("1e5"));
assert!(is_numeric_literal("1.5e-3"));
assert!(!is_numeric_literal("abc"));
assert!(!is_numeric_literal("7x"));
assert!(!is_numeric_literal(""));
}
#[test]
fn test_is_quoted_string() {
assert!(is_quoted_string("\"hello\""));
assert!(is_quoted_string("\"with spaces\""));
assert!(is_quoted_string("\"\""));
assert!(is_quoted_string("'hello'"));
assert!(is_quoted_string("'with spaces'"));
assert!(is_quoted_string("''"));
assert!(!is_quoted_string("hello"));
assert!(!is_quoted_string("\""));
assert!(!is_quoted_string("'"));
assert!(!is_quoted_string("\"hello'"));
assert!(!is_quoted_string("'hello\""));
assert!(!is_quoted_string(""));
}
#[test]
fn test_classify_boolean() {
let result = classify_value(&Some("TRUE".to_string()));
assert_eq!(result, ChunkOptionValue::Simple("TRUE".to_string()));
let result = classify_value(&Some("FALSE".to_string()));
assert_eq!(result, ChunkOptionValue::Simple("FALSE".to_string()));
}
#[test]
fn test_classify_number() {
let result = classify_value(&Some("7".to_string()));
assert_eq!(result, ChunkOptionValue::Simple("7".to_string()));
let result = classify_value(&Some("3.14".to_string()));
assert_eq!(result, ChunkOptionValue::Simple("3.14".to_string()));
}
#[test]
fn test_classify_quoted_string() {
let result = classify_value(&Some("\"hello\"".to_string()));
assert_eq!(result, ChunkOptionValue::Simple("\"hello\"".to_string()));
let result = classify_value(&Some("'world'".to_string()));
assert_eq!(result, ChunkOptionValue::Simple("'world'".to_string()));
}
#[test]
fn test_classify_function_call() {
let result = classify_value(&Some("paste(\"a\", \"b\")".to_string()));
assert_eq!(
result,
ChunkOptionValue::Expression("paste(\"a\", \"b\")".to_string())
);
}
#[test]
fn test_classify_variable() {
let result = classify_value(&Some("my_var".to_string()));
assert_eq!(result, ChunkOptionValue::Expression("my_var".to_string()));
}
#[test]
fn test_classify_none() {
let result = classify_value(&None);
assert_eq!(result, ChunkOptionValue::Simple(String::new()));
}
#[test]
fn test_classify_expression_with_operators() {
let result = classify_value(&Some("x + y".to_string()));
assert_eq!(result, ChunkOptionValue::Expression("x + y".to_string()));
let result = classify_value(&Some("data$col".to_string()));
assert_eq!(result, ChunkOptionValue::Expression("data$col".to_string()));
let result = classify_value(&Some("vec[1]".to_string()));
assert_eq!(result, ChunkOptionValue::Expression("vec[1]".to_string()));
}
#[test]
fn test_hashpipe_comment_prefix() {
assert_eq!(hashpipe_comment_prefix("r"), Some("#|"));
assert_eq!(hashpipe_comment_prefix("cpp"), Some("//|"));
assert_eq!(hashpipe_comment_prefix("Rcpp"), Some("//|"));
assert_eq!(hashpipe_comment_prefix("sql"), Some("--|"));
assert_eq!(hashpipe_comment_prefix("mermaid"), Some("%%|"));
assert_eq!(hashpipe_comment_prefix("fortran"), None);
}
}