blob: 7dadef454c4b327454c1975f6ea9e5f2e49bad31 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package main
import (
"fmt"
"io"
"os"
"os/exec"
"strings"
"gopkg.in/yaml.v3"
)
const PackageName = "ierror"
type Field struct {
Name string `yaml:"name"`
Type string `yaml:"type"`
}
type ErrorDef struct {
Name string `yaml:"name"`
Format string `yaml:"format"`
Code uint32 `yaml:"code"`
Fields []Field `yaml:"fields"`
}
const OutputFile = "errors_gen.go"
func main() {
data, err := os.ReadFile("./errors.yaml")
if err != nil {
panic(err)
}
var errors []ErrorDef
if err = yaml.Unmarshal(data, &errors); err != nil {
panic(err)
}
f, err := os.Create(OutputFile)
if err != nil {
panic(err)
}
_, _ = fmt.Fprint(f, `// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
`)
printGeneratedNotice(f)
types(f, errors)
sentinelErrors(f, errors)
errorCodes(f, errors)
codeString(f, errors)
fromCode(f, errors)
if err = f.Close(); err != nil {
panic(err)
}
// gofmt the generated file
if err = exec.Command("gofmt", "-w", OutputFile).Run(); err != nil {
panic("failed to gofmt: " + err.Error())
}
}
func printGeneratedNotice(f io.Writer) {
_, err := fmt.Fprintln(f, `// Code generated by generator/main.go; DO NOT EDIT.`)
if err != nil {
panic(err)
}
}
func types(f io.Writer, errors []ErrorDef) {
_, err := fmt.Fprintf(f, `
package %s
import "fmt"
type IggyError interface {
error
Code() Code
}
`, PackageName)
if err != nil {
panic(err)
}
for _, e := range errors {
if len(e.Fields) != 0 &&
(strings.Contains(e.Format, "%v") ||
strings.Contains(e.Format, "%s") ||
strings.Contains(e.Format, "%d")) {
// error with fields and need to use fmt.Sprintf.
_, _ = fmt.Fprintf(f, "type %s struct {\n", e.Name)
for _, field := range e.Fields {
_, _ = fmt.Fprintf(f, " %s %s\n", field.Name, field.Type)
}
_, _ = fmt.Fprintln(f, "}")
// Error() function
args := make([]string, len(e.Fields))
for i, field := range e.Fields {
args[i] = "e." + field.Name
}
_, _ = fmt.Fprintf(f, `func (e %s) Error() string {
return fmt.Sprintf("%s", %s)
}
`, e.Name, e.Format, strings.Join(args, ", "))
} else {
_, err = fmt.Fprintf(f, "type %s struct{}\n", e.Name)
if err != nil {
panic(err)
}
// Error() function
_, err = fmt.Fprintf(f, "func (e %s) Error() string { return \"%s\" }\n", e.Name, e.Format)
if err != nil {
panic(err)
}
}
// Code() function
_, _ = fmt.Fprintf(f, "func (e %s) Code() Code { return %d }\n", e.Name, e.Code)
// Is() function
_, _ = fmt.Fprintf(f, `func (e %s) Is(target error) bool {
_, ok := target.(%s)
return ok
}
`, e.Name, e.Name)
}
}
func sentinelErrors(f io.Writer, errors []ErrorDef) {
_, _ = fmt.Fprintln(f, "var (")
for _, e := range errors {
_, _ = fmt.Fprintf(f, " Err%s = %s{}\n", e.Name, e.Name)
}
_, _ = fmt.Fprint(f, ")\n\n")
}
func errorCodes(f io.Writer, errors []ErrorDef) {
// define enum
_, _ = fmt.Fprint(f, "type Code uint32\n\n")
_, _ = fmt.Fprintln(f, "const (")
for _, e := range errors {
_, _ = fmt.Fprintf(f, " %sCode Code = %d\n", e.Name, e.Code)
}
_, _ = fmt.Fprintln(f, ")")
_, _ = fmt.Fprintln(f)
}
// codeString generates the String() method for the Code type.
func codeString(f io.Writer, errors []ErrorDef) {
_, _ = fmt.Fprintln(f, "func (c Code) String() string {")
_, _ = fmt.Fprintln(f, " switch c {")
for _, e := range errors {
_, _ = fmt.Fprintf(f, " case %sCode:\n", e.Name)
_, _ = fmt.Fprintf(f, " return \"%s\"\n", e.Name)
}
_, _ = fmt.Fprintln(f, " default:")
_, _ = fmt.Fprintln(f, " return \"Unknown error code\"")
_, _ = fmt.Fprintln(f, " }")
_, _ = fmt.Fprintln(f, "}")
_, _ = fmt.Fprintln(f)
}
// fromCode generates the FromCode function that maps a Code to an IggyError.
func fromCode(f io.Writer, errors []ErrorDef) {
_, _ = fmt.Fprintf(f, "func FromCode(code Code) IggyError {\n")
_, _ = fmt.Fprintln(f, " switch code {")
for _, e := range errors {
_, _ = fmt.Fprintf(f, " case %sCode:\n", e.Name)
_, _ = fmt.Fprintf(f, " return Err%s\n", e.Name)
}
_, _ = fmt.Fprintln(f, " default:")
_, _ = fmt.Fprintln(f, " return ErrError")
_, _ = fmt.Fprintln(f, " }")
_, _ = fmt.Fprintln(f, "}")
_, _ = fmt.Fprintln(f)
}