A field that opens a quote and never closes it is the first genuinely malformed input your parser can meet. Today you add a positioned error type and report an unterminated quoted field at the exact place it began.
Report an unterminated quoted field as an error naming the line and column where the quote opened.
Every input so far has been valid, so Parse could return just the table. A quoted
field changes that: the moment a field opens with a quote, it has promised a closing
quote, and if the input ends first the file is malformed. This is the first error
your parser must report, and reporting it well means saying where. So Parse
grows a second return value, an error, and you introduce a small positioned error
type that carries a line, a column, and a message. Every earlier call site now
also gets back a nil error for valid input, which is the one change this lesson makes
to code you already wrote.
To report a position you have to track one, so keep a running line and column as you consume runes: start both at 1, advance the column on each rune, and reset the column to 1 and bump the line each time an unquoted newline ends a record. When you enter a quoted field, remember the line and column of that opening quote. If the input runs out while you are still inside the quote, that remembered position is exactly what the error should name, because that opening quote is the thing the user needs to find and fix. Pinning the error to the opening quote, not to the end of the file, is what makes the message actionable.
// Parse now returns (records, error). A positioned error carries where it happened:type ParseError struct { Line, Column int; Msg string }func (e *ParseError) Error() string { /* "line L, column C: M" */ }// track Line/Column as you read runes; remember where the current quote OPENED// if input ends while still in QUOTED mode -> return that opening position