///|
/// Quote one name and neutralize common spreadsheet formula prefixes.
fn coverage_csv_name(value : String) -> String {
let text = if value.has_prefix("=") ||
value.has_prefix("+") ||
value.has_prefix("-") ||
value.has_prefix("@") ||
value.has_prefix("\t") ||
value.has_prefix("\r") {
"'" + value
} else {
value
}
if text.contains(",") ||
text.contains("\"") ||
text.contains("\r") ||
text.contains("\n") {
"\"" + text.replace_all(old="\"", new="\"\"") + "\""
} else {
text
}
}
///|
/// Export filled positions as UTF-8 CSV in stable roster order.
///
/// Requirement and worker names are quoted when needed; formula-like names
/// receive a leading apostrophe to reduce formula interpretation risk.
pub fn encode_coverage_roster_csv(roster : CoverageRoster) -> String {
let mut output = "requirement_id,requirement_name,slot,position,worker_id,worker_name"
for entry in roster.entries {
output = output +
"\r\n\{entry.requirement.id},\{coverage_csv_name(entry.requirement.name)},\{entry.requirement.slot},\{entry.position},\{entry.worker.id},\{coverage_csv_name(entry.worker.name)}"
}
output
}