// registry-api-descriptor-template uses the APIDescriptor defined in the
// api/v2 package to execute templates passed to the command line.
//
// For example, to generate a new API specification, one would execute the
// following command from the repo root:
//
//	$ registry-api-descriptor-template docs/spec/api.md.tmpl > docs/spec/api.md
//
// The templates are passed in the api/v2.APIDescriptor object. Please see the
// package documentation for fields available on that object. The template
// syntax is from Go's standard library text/template package. For information
// on Go's template syntax, please see golang.org/pkg/text/template.
package main

import (
	"log"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"text/template"

	"github.com/distribution/distribution/v3/registry/api/errcode"
	v2 "github.com/distribution/distribution/v3/registry/api/v2"
)

var spaceRegex = regexp.MustCompile(`\n\s*`)

func main() {
	if len(os.Args) != 2 {
		log.Fatalln("please specify a template to execute.")
	}

	path := os.Args[1]
	filename := filepath.Base(path)

	funcMap := template.FuncMap{
		"removenewlines": func(s string) string {
			return spaceRegex.ReplaceAllString(s, " ")
		},
		"statustext":    http.StatusText,
		"prettygorilla": prettyGorillaMuxPath,
	}

	tmpl := template.Must(template.New(filename).Funcs(funcMap).ParseFiles(path))

	data := struct {
		RouteDescriptors []v2.RouteDescriptor
		ErrorDescriptors []errcode.ErrorDescriptor
	}{
		RouteDescriptors: v2.APIDescriptor.RouteDescriptors,
		ErrorDescriptors: append(errcode.GetErrorCodeGroup("registry.api.v2"),
			// The following are part of the specification but provided by errcode default.
			errcode.ErrorCodeUnauthorized.Descriptor(),
			errcode.ErrorCodeDenied.Descriptor(),
			errcode.ErrorCodeUnsupported.Descriptor()),
	}

	if err := tmpl.Execute(os.Stdout, data); err != nil {
		log.Fatalln(err)
	}
}

// prettyGorillaMuxPath removes the regular expressions from a gorilla/mux
// route string, making it suitable for documentation.
func prettyGorillaMuxPath(s string) string {
	// Stateful parser that removes regular expressions from gorilla
	// routes. It correctly handles balanced bracket pairs.

	var output string
	var label string
	var level int

start:
	if s[0] == '{' {
		s = s[1:]
		level++
		goto capture
	}

	output += string(s[0])
	s = s[1:]

	goto end
capture:
	switch s[0] {
	case '{':
		level++
	case '}':
		level--

		if level == 0 {
			s = s[1:]
			goto label
		}
	case ':':
		s = s[1:]
		goto skip
	default:
		label += string(s[0])
	}
	s = s[1:]
	goto capture
skip:
	switch s[0] {
	case '{':
		level++
	case '}':
		level--
	}
	s = s[1:]

	if level == 0 {
		goto label
	}

	goto skip
label:
	if label != "" {
		output += "<" + label + ">"
		label = ""
	}
end:
	if s != "" {
		goto start
	}

	return output
}
