package shared
import (
"fmt"
"os"
"strings"
)
type Error struct {
message string
loc Location
}
func NewError(loc Location, message string, a ...any) Error {
return Error{
message: fmt.Sprintf(message, a...),
loc: loc,
}
}
func (err Error) Error() string {
f, e := os.ReadFile(err.loc.FilePath)
if e != nil {
return err.message
}
lines := strings.Split(string(f), "\n")
lineIdx := err.loc.LC.Line - 1
if lineIdx < 0 || lineIdx >= len(lines) {
return err.message
}
start := max(0, lineIdx-2)
end := min(len(lines)-1, lineIdx+2)
var b strings.Builder
fmt.Fprintf(&b, "%s:%d:%d\n", err.loc.FilePath, err.loc.LC.Line, err.loc.LC.Col)
const red = "\x1b[31m"
const reset = "\x1b[0m"
for i := start; i <= end; i++ {
ln := i + 1
if i == lineIdx {
fmt.Fprintf(&b, "%s%4d |%s %s %s(col %d)%s\n",
red, ln, reset, lines[i], red, err.loc.LC.Col, reset)
} else {
fmt.Fprintf(&b, "%4d | %s\n", ln, lines[i])
}
}
fmt.Fprintf(&b, "\n%s", err.message)
return b.String()
}
type LineCol struct {
Line int
Col int
}
type Location struct {
LC LineCol
FilePath string
}
func (l Location) String() string {
return fmt.Sprintf("%s:%d:%d", l.FilePath, l.LC.Line, l.LC.Col)
}
type Pair[T1 any, T2 any] struct {
L T1
R T2
}