diff --git a/csv_parser.c b/csv_parser.c index 7b8d1ec..08cfd42 100644 --- a/csv_parser.c +++ b/csv_parser.c @@ -1,5 +1,6 @@ #include "csv_parser.h" #include "arena.h" +#include "csv_utils.h" #include #include #include @@ -33,17 +34,13 @@ static bool grow_field_array(FieldArray *arr, Arena *arena) { return true; } -static bool add_field(FieldArray *arr, const char *start, size_t len, Arena *arena) { +static bool add_field(FieldArray *arr, const char *start, size_t len, Arena *arena, const CSVConfig *config) { if (arr->count >= arr->capacity) { if (!grow_field_array(arr, arena)) { return false; } } - - while (len > 0 && (start[len-1] == ' ' || start[len-1] == '\t')) { - len--; - } - + void *ptr; ArenaResult result = arena_alloc(arena, len + 1, &ptr); if (result != ARENA_OK) { @@ -52,6 +49,14 @@ static bool add_field(FieldArray *arr, const char *start, size_t len, Arena *are char *field = (char*)ptr; memcpy(field, start, len); field[len] = '\0'; + + if (config->trimFields) { + CSVUtilsResult utilsResult = csv_utils_trim_whitespace(field, len); + if (utilsResult != CSV_UTILS_OK) { + return false; + } + } + arr->fields[arr->count++] = field; return true; } @@ -122,7 +127,7 @@ CSVParseResult csv_parse_line_inplace(const char *line, Arena *arena, const CSVC field_start = &line[pos + 1]; field_len = 0; } else if (c == config->delimiter) { - if (!add_field(&result.fields, "", 0, arena)) { + if (!add_field(&result.fields, "", 0, arena, config)) { result.success = false; result.error = "Memory allocation failed"; result.error_column = pos; @@ -139,7 +144,7 @@ CSVParseResult csv_parse_line_inplace(const char *line, Arena *arena, const CSVC case UNQUOTED_FIELD: if (c == config->delimiter) { - if (!add_field(&result.fields, field_start, field_len, arena)) { + if (!add_field(&result.fields, field_start, field_len, arena, config)) { result.success = false; result.error = "Memory allocation failed"; result.error_column = pos; @@ -209,7 +214,7 @@ CSVParseResult csv_parse_line_inplace(const char *line, Arena *arena, const CSVC return result; } } else { - if (!add_field(&result.fields, field_start, field_len, arena)) { + if (!add_field(&result.fields, field_start, field_len, arena, config)) { result.success = false; result.error = "Memory allocation failed"; return result; diff --git a/tests/test_csv_parser.c b/tests/test_csv_parser.c index bac7fa9..be073fe 100644 --- a/tests/test_csv_parser.c +++ b/tests/test_csv_parser.c @@ -69,28 +69,38 @@ void test_csv_parser_whitespace_trimming() { assert(arena_create(&arena, 4096) == ARENA_OK); CSVConfig *config = csv_config_create(&arena); - // Test trailing whitespace trimming (parser only trims trailing, not leading) + // Test with trimFields = false + config->trimFields = false; CSVParseResult result1 = csv_parse_line_inplace(" field1 , field2 , field3 ", &arena, config, 1); assert(result1.success == true); assert(result1.fields.count == 3); - assert(strcmp(result1.fields.fields[0], " field1") == 0); // Leading spaces preserved - assert(strcmp(result1.fields.fields[1], " field2") == 0); // Leading spaces preserved - assert(strcmp(result1.fields.fields[2], " field3") == 0); // Leading spaces preserved + assert(strcmp(result1.fields.fields[0], " field1 ") == 0); // Leading spaces preserved + assert(strcmp(result1.fields.fields[1], " field2 ") == 0); // Leading spaces preserved + assert(strcmp(result1.fields.fields[2], " field3 ") == 0); // Leading spaces preserved - // Test with quoted fields (should not trim inside quotes) - CSVParseResult result2 = csv_parse_line_inplace("\" field1 \", field2 ", &arena, config, 2); + // Test with trimFields = true + config->trimFields = true; + CSVParseResult result2 = csv_parse_line_inplace(" field1 , field2 , field3 ", &arena, config, 1); assert(result2.success == true); - assert(result2.fields.count == 2); - assert(strcmp(result2.fields.fields[0], " field1 ") == 0); - assert(strcmp(result2.fields.fields[1], " field2") == 0); - - // Test pure trailing whitespace trimming - CSVParseResult result3 = csv_parse_line_inplace("field1 ,field2\t\t,field3 ", &arena, config, 3); + assert(result2.fields.count == 3); + assert(strcmp(result2.fields.fields[0], "field1") == 0); // Leading spaces preserved + assert(strcmp(result2.fields.fields[1], "field2") == 0); // Leading spaces preserved + assert(strcmp(result2.fields.fields[2], "field3") == 0); // Leading spaces preserved + + // Test with quoted fields (should not trim inside quotes) + CSVParseResult result3 = csv_parse_line_inplace("\" field1 \", field2 ", &arena, config, 2); assert(result3.success == true); - assert(result3.fields.count == 3); - assert(strcmp(result3.fields.fields[0], "field1") == 0); // Trailing spaces trimmed - assert(strcmp(result3.fields.fields[1], "field2") == 0); // Trailing tabs trimmed - assert(strcmp(result3.fields.fields[2], "field3") == 0); // Trailing space trimmed + assert(result3.fields.count == 2); + assert(strcmp(result3.fields.fields[0], " field1 ") == 0); + assert(strcmp(result3.fields.fields[1], "field2") == 0); + + // Test pure trailing whitespace trimming with trimFields = true + CSVParseResult result4 = csv_parse_line_inplace("field1 ,field2\t\t,field3 ", &arena, config, 3); + assert(result4.success == true); + assert(result4.fields.count == 3); + assert(strcmp(result4.fields.fields[0], "field1") == 0); // Trailing spaces trimmed + assert(strcmp(result4.fields.fields[1], "field2") == 0); // Trailing tabs trimmed + assert(strcmp(result4.fields.fields[2], "field3") == 0); // Trailing space trimmed arena_destroy(&arena); printf("✓ CSV parser whitespace trimming test passed\n");