// Copyright (c) 2024 David Vogel // // This software is released under the MIT License. // https://opensource.org/licenses/MIT package typst import ( "regexp" "strconv" ) // Error represents a generic typst error. type Error struct { Inner error Raw string // The raw output from stderr. Message string // The parsed error message. } func (e *Error) Error() string { return e.Raw } func (e *Error) Unwrap() error { return e.Inner } // ErrorWithPath represents a typst error that also contains information about its origin (filepath, line and column). type ErrorWithPath struct { Inner error Raw string // The raw error string as returned by the executable. Message string // Error message from typst. Path string // Path of the typst file where the error is located in. Line int // Line number of the error. Column int // Column of the error. } func (e *ErrorWithPath) Error() string { return e.Raw } func (e *ErrorWithPath) Unwrap() error { return e.Inner } var stderrRegex = regexp.MustCompile(`(?s)^error: (?.+?)\n`) var stderrWithPathRegex = regexp.MustCompile(`(?s)^error: (?.+?)\n\s+┌─ (?.+?):(?\d+):(?\d+)\n`) // ParseStderr will parse the given stderr output and return a suitable error object. // Depending on the stderr message, this will return either a typst.Error or a typst.ErrorWithPath error. func ParseStderr(stderr string, inner error) error { if parsed := stderrWithPathRegex.FindStringSubmatch(stderr); parsed != nil { err := ErrorWithPath{ Raw: stderr, Inner: inner, } if i := stderrWithPathRegex.SubexpIndex("error"); i > 0 && i < len(parsed) { err.Message = parsed[i] } if i := stderrWithPathRegex.SubexpIndex("path"); i > 0 && i < len(parsed) { err.Path = parsed[i] } if i := stderrWithPathRegex.SubexpIndex("line"); i > 0 && i < len(parsed) { line, _ := strconv.ParseInt(parsed[i], 10, 0) err.Line = int(line) } if i := stderrWithPathRegex.SubexpIndex("column"); i > 0 && i < len(parsed) { column, _ := strconv.ParseInt(parsed[i], 10, 0) err.Column = int(column) } return &err } if parsed := stderrRegex.FindStringSubmatch(stderr); parsed != nil { err := Error{ Raw: stderr, Inner: inner, } if i := stderrRegex.SubexpIndex("error"); i > 0 && i < len(parsed) { err.Message = parsed[i] } return &err } // Fall back to the raw error message. return &Error{ Raw: stderr, Inner: inner, } }