///|
pub enum CsvParseError {
EmptyCsv
UnterminatedQuote
UnexpectedCharacter
InvalidHeader
InvalidNumber
} derive(Debug, Eq)
///|
pub struct ReportSummaryRow {
title : String
input_count : Int
result_count : Int
warning_count : Int
source_count : Int
publishable : Bool
} derive(Debug, Eq)
///|
fn csv_rows(text : String) -> Result[Array[Array[String]], CsvParseError] {
if text == "" {
return Err(EmptyCsv)
}
let rows : Array[Array[String]] = []
let fields : Array[String] = []
let mut field = ""
let mut in_quotes = false
let mut quote_pending = false
for c in text {
if quote_pending {
if c == '"' {
field += "\""
quote_pending = false
in_quotes = true
} else {
in_quotes = false
quote_pending = false
if c == ',' {
fields.push(field)
field = ""
} else if c == '\n' {
fields.push(field)
rows.push(fields.copy())
fields.clear()
field = ""
} else if c == '\r' {
()
} else {
return Err(UnexpectedCharacter)
}
}
} else if in_quotes {
if c == '"' {
quote_pending = true
} else {
field += "\{c}"
}
} else if c == '"' && field == "" {
in_quotes = true
} else if c == ',' {
fields.push(field)
field = ""
} else if c == '\n' {
fields.push(field)
rows.push(fields.copy())
fields.clear()
field = ""
} else if c == '\r' {
()
} else {
field += "\{c}"
}
}
if quote_pending {
in_quotes = false
}
if in_quotes {
return Err(UnterminatedQuote)
}
fields.push(field)
if fields.length() > 1 || fields[0] != "" {
rows.push(fields)
}
Ok(rows)
}
///|
fn csv_count(text : String) -> Int? {
match decimal_scaled(text) {
Some(value) => if value % 1000 == 0 { Some(value / 1000) } else { None }
None => None
}
}
///|
pub fn parse_report_summary_csv(
text : String,
) -> Result[Array[ReportSummaryRow], CsvParseError] {
let rows = match csv_rows(text) {
Ok(value) => value
Err(error) => return Err(error)
}
if rows.length() == 0 ||
rows[0] !=
[
"title", "inputs", "results", "warnings", "sources", "publishable", "first_input_note",
] {
return Err(InvalidHeader)
}
let output : Array[ReportSummaryRow] = []
for row in rows[1:] {
if row.length() < 6 {
return Err(InvalidHeader)
}
let input_count = csv_count(row[1])
let result_count = csv_count(row[2])
let warning_count = csv_count(row[3])
let source_count = csv_count(row[4])
if input_count is None ||
result_count is None ||
warning_count is None ||
source_count is None {
return Err(InvalidNumber)
}
if row[5] != "true" && row[5] != "false" {
return Err(InvalidHeader)
}
output.push({
title: row[0],
input_count: input_count.unwrap(),
result_count: result_count.unwrap(),
warning_count: warning_count.unwrap(),
source_count: source_count.unwrap(),
publishable: row[5] == "true",
})
}
Ok(output)
}