While empty patches with only a header were parsable, the parser
discarded the preamble content. This meant callers had to handle this
case specially. Now, if we reach the end of the input without finding a
file, Parse() returns the full content of the patch as the preamble.
Fragment is now TextFragment to distinguish from a future
BinaryFragment. Also rename FragmentLine to Line, since the
text-orientation is implied by the name.
This allows callers to provide patches with commit or email headers and
then retrieve that leading content for additional parsing without
having to identify where the first file in the patch starts.
For now, this uses JSON to print objects on error, which is hard to
debug. It should probably use something like google/go-cmp instead,
because these objects are now too large for direct comparison.
To make output better, also add some (temporary?) String() functions and
fix an assumption about the smallest fragment header I discovered was
wrong while looking at sample patches.
The parser now returns an EOF on the first call to Next() if the input
is empty and increments the line number before returning EOF for the
first time.
Binary patch support is still unimplemented, but the functions are
stubbed and the overall structure seems to make sense. This also renames
the existing fragment functions to have "Text" in their names (for
clarity) and moves the parsing of fragment lines to a new "chunk"
function, which will match how binary parsing works.
After considering fragment parsing, it made sense to change the
invariant estabilished in the previous commit. Specifically, parser
functions now assume they are call on the first line of their object and
return with the parser on the first line after their object. This means
code can call parse function immediately after each other without
advancing the parser in between.
It's possible this will change back later on... there seem to be
annoying edge cases with either choice, but I think making the functions
consistent is important.
Instead of checking that a line has a certain prefix before calling a
header parsing function, the functions now check this and return nil
objects when called on the wrong line type. If the line passes this
basic check but is still invalid, and error is returned as before.
At the moment, we need only to read three lines, but now the value is
easy to adjust as needed. I've only seen the Git implementation read two
lines ahead so far, but it has the whole input in memory and may read
more in other places.
Call Next() to advance the parser state until it returns a non-nil
error, then check if the error is io.EOF. This makes EOF handling easier
and also means that Line() and PeekLine() can be called multiple times
without changing state.
The next step is to update parse functions to return an internal marker
error if they are called on an invalid line. This should improve
correctness and remove the duplication of testing a condition and then
calling a parse function, which checks the same condition.
Fix parseGitHeaderIndex to allow abbreviated OIDs and add a note about
this to the README. The behavior might change again later, based on some
experimentation with Git.