Advent-of-Code/aocutils/utils.go
2025-12-06 11:32:42 +02:00

139 lines
2.4 KiB
Go

package aocutils
import (
"bufio"
"bytes"
"context"
"io"
"iter"
)
func SplitComma(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if i := bytes.IndexByte(data, ','); i >= 0 {
return i + 1, data[0:i], nil
}
if atEOF {
return len(data), data, nil
}
return 0, nil, nil
}
func SplitString(s string) bufio.SplitFunc {
sep := []byte(s)
return func(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if i := bytes.Index(data, sep); i >= 0 {
return i + len(sep), data[0:i], nil
}
if atEOF {
return len(data), data, nil
}
return 0, nil, nil
}
}
func ReadString(r io.Reader) (string, error) {
buff, err := io.ReadAll(r)
if err != nil {
return "", err
}
return string(buff), nil
}
type Logger interface {
Printf(string, ...interface{})
Println(...interface{})
}
type Context struct {
Body io.Reader
Logger Logger
Context context.Context
}
func (c *Context) Scanner(split ...bufio.SplitFunc) *bufio.Scanner {
s := bufio.NewScanner(c.Body)
if len(split) == 1 {
s.Split(split[0])
}
if len(split) > 2 {
panic("too many split line handlers")
}
return s
}
func (c *Context) BodyString() (string, error) {
return ReadString(c.Body)
}
func (c *Context) BodyBytes() ([]byte, error) {
return io.ReadAll(c.Body)
}
func (c *Context) Printf(format string, args ...interface{}) {
if c.Logger != nil {
c.Logger.Printf(format, args...)
}
}
func (c *Context) Println(args ...interface{}) {
if c.Logger != nil {
c.Logger.Println(args...)
}
}
type CustomScanner[T scannable, K any] struct {
val K
err error
inner scannable
mapFunc func(T) (K, error)
}
func (s *CustomScanner[T, K]) From(t T) {
s.inner = t
}
func (s *CustomScanner[T, K]) MapFunc(f func(t T) (K, error)) {
s.mapFunc = f
}
func (s *CustomScanner[T, K]) Value() K {
return s.val
}
func (s *CustomScanner[T, K]) Values() iter.Seq[K] {
return func(yield func(K) bool) {
for s.Scan() {
if !yield(s.Value()) {
return
}
}
}
}
type scannable interface {
Scan() bool
}
func (s *CustomScanner[T, K]) Scan() bool {
if s.err != nil {
return false
}
if !s.inner.Scan() {
return false
}
s.val, s.err = s.mapFunc(s.inner.(T))
if s.err != nil {
return false
}
return true
}
func (s *CustomScanner[T, K]) Err() error {
return s.err
}