68 lines
1.7 KiB
Go
68 lines
1.7 KiB
Go
package parser
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha1"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"git.kleiax.de/homepage/field"
|
|
)
|
|
|
|
type PuzzleBank struct {
|
|
ParserHelper
|
|
}
|
|
|
|
func (pb *PuzzleBank) Parse(data []byte) error {
|
|
if pb == nil {
|
|
return errors.New("nil puzzle bank parser")
|
|
}
|
|
|
|
var parsed []*field.Field
|
|
for index, rawLine := range bytes.Split(data, []byte("\n")) {
|
|
lineNumber := index + 1
|
|
line := strings.TrimSpace(string(rawLine))
|
|
if line == "" {
|
|
continue
|
|
}
|
|
|
|
columns := strings.Fields(line)
|
|
if len(columns) != 3 {
|
|
return fmt.Errorf("line %d: %w: expected hash, puzzle and rating", lineNumber, ErrInvalidInput)
|
|
}
|
|
if len(columns[0]) != 12 {
|
|
return fmt.Errorf("line %d: %w: hash has %d characters, expected 12", lineNumber, ErrInvalidInput, len(columns[0]))
|
|
}
|
|
if _, err := hex.DecodeString(columns[0]); err != nil {
|
|
return fmt.Errorf("line %d: %w: invalid hash: %v", lineNumber, ErrInvalidInput, err)
|
|
}
|
|
digest := sha1.Sum([]byte(columns[1]))
|
|
if columns[0] != hex.EncodeToString(digest[:])[:12] {
|
|
return fmt.Errorf("line %d: %w: hash does not match puzzle", lineNumber, ErrInvalidInput)
|
|
}
|
|
|
|
rating, err := strconv.ParseFloat(columns[2], 64)
|
|
if err != nil {
|
|
return fmt.Errorf("line %d: %w: invalid rating %q: %v", lineNumber, ErrInvalidInput, columns[2], err)
|
|
}
|
|
if rating < 0 || math.IsNaN(rating) || math.IsInf(rating, 0) {
|
|
return fmt.Errorf("line %d: %w: rating must be a finite non-negative number", lineNumber, ErrInvalidInput)
|
|
}
|
|
parsedField, err := classicField(columns[1], rating)
|
|
if err != nil {
|
|
return fmt.Errorf("line %d: %w", lineNumber, err)
|
|
}
|
|
parsed = append(parsed, parsedField)
|
|
}
|
|
|
|
if len(parsed) == 0 {
|
|
return ErrNoPuzzles
|
|
}
|
|
pb.fields = parsed
|
|
return nil
|
|
}
|