use std::collections::HashSet;
use polydat::ast::{PortType, Value};
pub fn workload_param_type_name(value: &str) -> &'static str {
let trimmed = value.trim();
if trimmed.parse::<u64>().is_ok() {
"u64"
} else if trimmed.parse::<f64>().is_ok() {
"f64"
} else if trimmed == "true" || trimmed == "false" {
"bool"
} else {
"String"
}
}
pub fn format_value_as_final_literal(v: &Value) -> Option<String> {
match v {
Value::U64(n) => Some(n.to_string()),
Value::F64(f) => {
if f.fract() == 0.0 && f.is_finite() {
Some(format!("{f:.1}"))
} else {
Some(format!("{f}"))
}
}
Value::Bool(b) => Some(b.to_string()),
Value::Str(s) => {
let escaped = s.replace('\\', "\\\\").replace('"', "\\\"");
Some(format!("\"{escaped}\""))
}
_ => None,
}
}
pub fn format_value_as_polydat_literal(v: &Value) -> String {
match v {
Value::U64(n) => n.to_string(),
Value::F64(f) => {
if f.fract() == 0.0 && f.is_finite() {
format!("{f:.1}")
} else {
format!("{f}")
}
}
Value::Bool(b) => b.to_string(),
Value::Str(s) => {
let escaped = s.replace('\\', "\\\\").replace('"', "\\\"");
format!("\"{escaped}\"")
}
_ => {
let display = v.to_display_string();
let escaped = display.replace('\\', "\\\\").replace('"', "\\\"");
format!("\"{escaped}\"")
}
}
}
pub fn format_workload_param_as_polydat_literal(value: &str) -> String {
let trimmed = value.trim();
if trimmed.parse::<u64>().is_ok() || trimmed.parse::<f64>().is_ok() {
trimmed.to_string()
} else {
let escaped = value.replace('\\', "\\\\").replace('"', "\\\"");
format!("\"{escaped}\"")
}
}
pub fn value_to_param_string(v: &Value) -> Option<String> {
match v {
Value::U64(n) => Some(n.to_string()),
Value::F64(n) => Some(n.to_string()),
Value::Bool(b) => Some(b.to_string()),
Value::Str(s) => Some(s.to_string()),
_ => None,
}
}
pub fn port_type_to_extern_name(t: PortType) -> &'static str {
t.to_keyword()
}
pub fn collect_leaf_placeholders(texts: &[String]) -> HashSet<String> {
let mut out = HashSet::new();
for text in texts {
scan_one(text, &mut out);
}
out
}
pub fn scan_one(text: &str, out: &mut HashSet<String>) {
let bytes = text.as_bytes();
let n = bytes.len();
let mut i = 0;
while i < n {
if bytes[i] == b'\\' && i + 1 < n && (bytes[i + 1] == b'{' || bytes[i + 1] == b'}') {
i += 2;
continue;
}
if bytes[i] == b'{' {
let mut j = i + 1;
let mut nested = false;
while j < n {
if bytes[j] == b'\\' && j + 1 < n && (bytes[j + 1] == b'{' || bytes[j + 1] == b'}')
{
j += 2;
continue;
}
if bytes[j] == b'{' {
nested = true;
break;
}
if bytes[j] == b'}' {
break;
}
j += 1;
}
if !nested && j < n && bytes[j] == b'}' {
let name = &text[i + 1..j];
if !name.is_empty() {
out.insert(name.to_string());
}
i = j + 1;
continue;
}
i += 1;
continue;
}
i += 1;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn workload_param_type_name_classifies_basic_scalars() {
assert_eq!(workload_param_type_name("42"), "u64");
assert_eq!(workload_param_type_name("3.14"), "f64");
assert_eq!(workload_param_type_name("true"), "bool");
assert_eq!(workload_param_type_name("false"), "bool");
assert_eq!(workload_param_type_name("hello"), "String");
}
#[test]
fn format_value_as_polydat_literal_renders_scalars() {
assert_eq!(format_value_as_polydat_literal(&Value::U64(42)), "42");
assert_eq!(format_value_as_polydat_literal(&Value::Bool(true)), "true");
assert_eq!(
format_value_as_polydat_literal(&Value::Str("x".into())),
"\"x\""
);
assert_eq!(format_value_as_polydat_literal(&Value::F64(2.0)), "2.0");
}
#[test]
fn format_workload_param_quotes_non_numeric() {
assert_eq!(format_workload_param_as_polydat_literal("42"), "42");
assert_eq!(
format_workload_param_as_polydat_literal("hello"),
"\"hello\""
);
assert_eq!(format_workload_param_as_polydat_literal("true"), "\"true\"");
}
#[test]
fn value_to_param_string_handles_scalars_only() {
assert_eq!(value_to_param_string(&Value::U64(7)), Some("7".to_string()));
assert_eq!(
value_to_param_string(&Value::Str("y".into())),
Some("y".to_string())
);
assert_eq!(value_to_param_string(&Value::None), None);
}
#[test]
fn collect_leaf_placeholders_extracts_simple_names() {
let names = collect_leaf_placeholders(&[
"k in 1..{n}".to_string(),
"{profile} matches {prefix}".to_string(),
]);
assert!(names.contains("n"));
assert!(names.contains("profile"));
assert!(names.contains("prefix"));
}
#[test]
fn collect_leaf_placeholders_skips_nested() {
let names = collect_leaf_placeholders(&["{a_{b}_c}".to_string()]);
assert!(names.contains("b"));
assert!(!names.contains("a_{b}_c"));
}
#[test]
fn collect_leaf_placeholders_honors_escapes() {
let names = collect_leaf_placeholders(&["\\{not_a_placeholder\\}".to_string()]);
assert!(names.is_empty());
}
}