| // 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) |
| } |