get.pme.sh/pnats@v0.0.0-20240304004023-26bb5a137ed0/server/errors_gen.go (about)

     1  //go:build ignore
     2  // +build ignore
     3  
     4  package main
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"log"
    10  	"os"
    11  	"os/exec"
    12  	"regexp"
    13  	"sort"
    14  	"strings"
    15  	"text/template"
    16  
    17  	"get.pme.sh/pnats/server"
    18  )
    19  
    20  var tagRe = regexp.MustCompile("\\{(.+?)}")
    21  
    22  var templ = `
    23  // Generated code, do not edit. See errors.json and run go generate to update
    24  
    25  package server
    26  
    27  import "strings"
    28  
    29  const (
    30  {{- range $i, $error := . }}
    31  {{- if .Comment }}
    32  	// {{ .Constant }} {{ .Comment }} ({{ .Description | print }})
    33  {{- else }}
    34  	// {{ .Constant }} {{ .Description | print }}
    35  {{- end }}
    36  	{{ .Constant }} ErrorIdentifier = {{ .ErrCode }}
    37  {{ end }}
    38  )
    39  
    40  var (
    41  	ApiErrors = map[ErrorIdentifier]*ApiError{
    42  {{- range $i, $error := . }}
    43  		{{ .Constant }}: {Code: {{ .Code }},ErrCode: {{ .ErrCode }},Description: {{ .Description | printf "%q" }}},{{- end }}
    44  	}
    45  
    46  {{- range $i, $error := . }}
    47  {{- if .Deprecates }}
    48  // {{ .Deprecates }} Deprecated by {{ .Constant }} ApiError, use IsNatsError() for comparisons
    49  {{ .Deprecates }} = ApiErrors[{{ .Constant }}]
    50  {{- end }}
    51  {{- end }}
    52  )
    53  
    54  {{- range $i, $error := . }}
    55  // {{ .Constant | funcNameForConstant }} creates a new {{ .Constant }} error: {{ .Description | printf "%q" }}
    56  func {{ .Constant | funcNameForConstant }}({{ .Description | funcArgsForTags }}) *ApiError {
    57      eopts := parseOpts(opts)
    58  	if ae, ok := eopts.err.(*ApiError); ok {
    59  		return ae
    60   	}
    61  {{ if .Description | hasTags }}
    62  	e:=ApiErrors[{{.Constant}}]
    63  	args:=e.toReplacerArgs([]interface{}{ {{.Description | replacerArgsForTags }} })
    64  	return &ApiError{
    65  		Code:        e.Code,
    66  		ErrCode:     e.ErrCode,
    67  		Description: strings.NewReplacer(args...).Replace(e.Description),
    68  	}
    69  {{- else }}
    70  	return ApiErrors[{{.Constant}}]
    71  {{- end }}
    72  }
    73  
    74  {{- end }}
    75  `
    76  
    77  func panicIfErr(err error) {
    78  	if err == nil {
    79  		return
    80  	}
    81  	panic(err)
    82  }
    83  
    84  func goFmt(file string) error {
    85  	c := exec.Command("go", "fmt", file)
    86  	out, err := c.CombinedOutput()
    87  	if err != nil {
    88  		log.Printf("go fmt failed: %s", string(out))
    89  	}
    90  
    91  	return err
    92  }
    93  
    94  func checkIncrements(errs []server.ErrorsData) error {
    95  	sort.Slice(errs, func(i, j int) bool {
    96  		return errs[i].ErrCode < errs[j].ErrCode
    97  	})
    98  
    99  	last := errs[0].ErrCode
   100  	gaps := []uint16{}
   101  
   102  	for i := 1; i < len(errs); i++ {
   103  		if errs[i].ErrCode != last+1 {
   104  			gaps = append(gaps, last)
   105  		}
   106  		last = errs[i].ErrCode
   107  	}
   108  
   109  	if len(gaps) > 0 {
   110  		return fmt.Errorf("gaps found in sequences: %v", gaps)
   111  	}
   112  
   113  	return nil
   114  }
   115  
   116  func checkDupes(errs []server.ErrorsData) error {
   117  	codes := []uint16{}
   118  	highest := uint16(0)
   119  	for _, err := range errs {
   120  		codes = append(codes, err.ErrCode)
   121  		if highest < err.ErrCode {
   122  			highest = err.ErrCode
   123  		}
   124  	}
   125  
   126  	codeKeys := make(map[uint16]bool)
   127  	constKeys := make(map[string]bool)
   128  
   129  	for _, entry := range errs {
   130  		if _, found := codeKeys[entry.ErrCode]; found {
   131  			return fmt.Errorf("duplicate error code %+v, highest code is %d", entry, highest)
   132  		}
   133  
   134  		if _, found := constKeys[entry.Constant]; found {
   135  			return fmt.Errorf("duplicate error constant %+v", entry)
   136  		}
   137  
   138  		codeKeys[entry.ErrCode] = true
   139  		constKeys[entry.Constant] = true
   140  	}
   141  
   142  	return nil
   143  }
   144  
   145  func findTags(d string) []string {
   146  	tags := []string{}
   147  	for _, tag := range tagRe.FindAllStringSubmatch(d, -1) {
   148  		if len(tag) != 2 {
   149  			continue
   150  		}
   151  
   152  		tags = append(tags, tag[1])
   153  	}
   154  
   155  	sort.Strings(tags)
   156  
   157  	return tags
   158  }
   159  
   160  func main() {
   161  	ej, err := os.ReadFile("server/errors.json")
   162  	panicIfErr(err)
   163  
   164  	errs := []server.ErrorsData{}
   165  	panicIfErr(json.Unmarshal(ej, &errs))
   166  
   167  	panicIfErr(checkDupes(errs))
   168  	panicIfErr(checkIncrements(errs))
   169  
   170  	sort.Slice(errs, func(i, j int) bool {
   171  		return errs[i].Constant < errs[j].Constant
   172  	})
   173  
   174  	t := template.New("errors").Funcs(
   175  		template.FuncMap{
   176  			"inc": func(i int) int { return i + 1 },
   177  			"hasTags": func(d string) bool {
   178  				return strings.Contains(d, "{") && strings.Contains(d, "}")
   179  			},
   180  			"replacerArgsForTags": func(d string) string {
   181  				res := []string{}
   182  				for _, tag := range findTags(d) {
   183  					res = append(res, `"{`+tag+`}"`)
   184  					res = append(res, tag)
   185  				}
   186  
   187  				return strings.Join(res, ", ")
   188  			},
   189  			"funcArgsForTags": func(d string) string {
   190  				res := []string{}
   191  				for _, tag := range findTags(d) {
   192  					if tag == "err" {
   193  						res = append(res, "err error")
   194  					} else if tag == "seq" {
   195  						res = append(res, "seq uint64")
   196  					} else {
   197  						res = append(res, fmt.Sprintf("%s interface{}", tag))
   198  					}
   199  				}
   200  
   201  				res = append(res, "opts ...ErrorOption")
   202  
   203  				return strings.Join(res, ", ")
   204  			},
   205  			"funcNameForConstant": func(c string) string {
   206  				res := ""
   207  
   208  				switch {
   209  				case strings.HasSuffix(c, "ErrF"):
   210  					res = fmt.Sprintf("New%sError", strings.TrimSuffix(c, "ErrF"))
   211  				case strings.HasSuffix(c, "Err"):
   212  					res = fmt.Sprintf("New%sError", strings.TrimSuffix(c, "Err"))
   213  				case strings.HasSuffix(c, "ErrorF"):
   214  					res = fmt.Sprintf("New%s", strings.TrimSuffix(c, "F"))
   215  				case strings.HasSuffix(c, "F"):
   216  					res = fmt.Sprintf("New%sError", strings.TrimSuffix(c, "F"))
   217  				default:
   218  					res = fmt.Sprintf("New%s", c)
   219  				}
   220  
   221  				if !strings.HasSuffix(res, "Error") {
   222  					res = fmt.Sprintf("%sError", res)
   223  				}
   224  
   225  				return res
   226  			},
   227  		})
   228  	p, err := t.Parse(templ)
   229  	panicIfErr(err)
   230  
   231  	tf, err := os.CreateTemp("", "")
   232  	panicIfErr(err)
   233  	defer tf.Close()
   234  
   235  	panicIfErr(p.Execute(tf, errs))
   236  
   237  	panicIfErr(os.Rename(tf.Name(), "server/jetstream_errors_generated.go"))
   238  	panicIfErr(goFmt("server/jetstream_errors_generated.go"))
   239  }