mirror of
https://github.com/StackExchange/dnscontrol.git
synced 2025-01-12 18:37:54 +08:00
1e337abcdf
Co-authored-by: Jeffrey Cafferata <jeffrey@jcid.nl> Co-authored-by: Tom Limoncelli <tlimoncelli@stackoverflow.com>
219 lines
5.5 KiB
Go
219 lines
5.5 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"golang.org/x/text/cases"
|
|
"golang.org/x/text/language"
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
func join(parts ...string) string {
|
|
return strings.Join(parts, string(os.PathSeparator))
|
|
}
|
|
|
|
func fixRuns(s string) string {
|
|
lines := strings.Split(s, "\n")
|
|
var out []string
|
|
for _, line := range lines {
|
|
if len(line) == 0 {
|
|
if len(out) > 0 && len(out[len(out)-1]) == 0 {
|
|
continue
|
|
}
|
|
}
|
|
out = append(out, line)
|
|
}
|
|
return strings.Join(out, "\n")
|
|
}
|
|
|
|
var delimiterRegex = regexp.MustCompile(`(?m)^---\n`)
|
|
|
|
func parseFrontMatter(content string) (map[string]interface{}, string, error) {
|
|
delimiterIndices := delimiterRegex.FindAllStringIndex(content, 2)
|
|
startIndex := delimiterIndices[0][0]
|
|
endIndex := delimiterIndices[1][0]
|
|
yamlString := content[startIndex+4 : endIndex]
|
|
var frontMatter map[string]interface{}
|
|
err := yaml.Unmarshal([]byte(yamlString), &frontMatter)
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
return frontMatter, content[endIndex+4:], nil
|
|
}
|
|
|
|
var returnTypes = map[string]string{
|
|
"domain": "DomainModifier",
|
|
"global": "void",
|
|
"record": "RecordModifier",
|
|
}
|
|
|
|
func generateFunctionTypes() (string, error) {
|
|
funcs := []Function{}
|
|
|
|
srcRoot := join("docs", "_functions")
|
|
types, err := os.ReadDir(srcRoot)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
for _, t := range types {
|
|
if !t.IsDir() {
|
|
return "", errors.New("not a directory: " + join(srcRoot, t.Name()))
|
|
}
|
|
tPath := join(srcRoot, t.Name())
|
|
funcNames, err := os.ReadDir(tPath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
for _, f := range funcNames {
|
|
fPath := join(tPath, f.Name())
|
|
if f.IsDir() {
|
|
return "", errors.New("not a file: " + fPath)
|
|
}
|
|
// println("Processing", fPath)
|
|
content, err := os.ReadFile(fPath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
frontMatter, body, err := parseFrontMatter(string(content))
|
|
if err != nil {
|
|
println("Error parsing front matter in", fPath)
|
|
return "", err
|
|
}
|
|
if frontMatter["ts_ignore"] == true {
|
|
continue
|
|
}
|
|
|
|
body = body + "\n"
|
|
body = strings.ReplaceAll(body, "{{site.github.url}}", "https://dnscontrol.org/")
|
|
body = strings.ReplaceAll(body, "{% capture example %}", "")
|
|
body = strings.ReplaceAll(body, "{% capture example2 %}", "")
|
|
body = strings.ReplaceAll(body, "{% endcapture %}", "")
|
|
body = strings.ReplaceAll(body, "{% include example.html content=example %}", "")
|
|
body = strings.ReplaceAll(body, "{% include example.html content=example2 %}", "")
|
|
body = strings.ReplaceAll(body, "](#", "](https://dnscontrol.org/js#")
|
|
body = fixRuns(body)
|
|
|
|
paramNames := []string{}
|
|
if frontMatter["parameters"] != nil {
|
|
for _, p := range frontMatter["parameters"].([]interface{}) {
|
|
paramNames = append(paramNames, p.(string))
|
|
}
|
|
}
|
|
|
|
suppliedParamTypes := map[string]string{}
|
|
if frontMatter["parameter_types"] != nil {
|
|
rawTypes := frontMatter["parameter_types"].(map[string]interface{})
|
|
for k, v := range rawTypes {
|
|
suppliedParamTypes[k] = v.(string)
|
|
}
|
|
}
|
|
|
|
params := []Param{}
|
|
for _, p := range paramNames {
|
|
// start with supplied type, fall back to defaultParamType
|
|
paramType := suppliedParamTypes[p]
|
|
if paramType == "" {
|
|
println("WARNING:", fPath+":", "no type for parameter ", "'"+p+"'")
|
|
paramType = "unknown"
|
|
}
|
|
params = append(params, Param{Name: p, Type: paramType})
|
|
}
|
|
|
|
returnType := returnTypes[t.Name()]
|
|
if frontMatter["ts_return"] != nil {
|
|
returnType = frontMatter["ts_return"].(string)
|
|
} else if frontMatter["return"] != nil {
|
|
returnType = frontMatter["return"].(string)
|
|
}
|
|
|
|
if len(params) == 0 {
|
|
if frontMatter["ts_is_function"] != true {
|
|
params = nil
|
|
}
|
|
}
|
|
|
|
funcs = append(funcs, Function{
|
|
Name: frontMatter["name"].(string),
|
|
Params: params,
|
|
ObjectParam: frontMatter["parameters_object"] == true,
|
|
Deprecated: frontMatter["deprecated"] == true,
|
|
ReturnType: returnType,
|
|
Description: strings.TrimSpace(body),
|
|
})
|
|
}
|
|
}
|
|
|
|
content := ""
|
|
for _, f := range funcs {
|
|
content += f.String()
|
|
}
|
|
return content, nil
|
|
}
|
|
|
|
type Function struct {
|
|
Name string
|
|
Params []Param
|
|
ObjectParam bool
|
|
Deprecated bool
|
|
ReturnType string
|
|
Description string
|
|
}
|
|
|
|
type Param struct {
|
|
Name string
|
|
Type string
|
|
}
|
|
|
|
var caser = cases.Title(language.AmericanEnglish)
|
|
|
|
func (f Function) formatParams() string {
|
|
var params []string
|
|
for _, p := range f.Params {
|
|
name := p.Name
|
|
if strings.HasSuffix(name, "...") {
|
|
name = "..." + name[:len(name)-3]
|
|
}
|
|
if strings.Contains(name, " ") {
|
|
name = strings.ReplaceAll(caser.String(name), " ", "")
|
|
name = strings.ToLower(name[:1]) + name[1:]
|
|
}
|
|
|
|
typeName := p.Type
|
|
if strings.HasSuffix(typeName, "?") {
|
|
typeName = typeName[:len(typeName)-1]
|
|
name += "?"
|
|
}
|
|
|
|
params = append(params, fmt.Sprintf("%s: %s", name, typeName))
|
|
}
|
|
if f.ObjectParam {
|
|
return "opts: { " + strings.Join(params, "; ") + " }"
|
|
} else {
|
|
return strings.Join(params, ", ")
|
|
}
|
|
}
|
|
|
|
func (f Function) docs() string {
|
|
content := f.Description
|
|
if f.Deprecated {
|
|
content += "\n\n@deprecated"
|
|
}
|
|
content += "\n\n@see https://dnscontrol.org/js#" + f.Name
|
|
return "/**\n * " + strings.ReplaceAll(content, "\n", "\n * ") + "\n */"
|
|
}
|
|
|
|
func (f Function) formatMain() string {
|
|
if f.Params == nil {
|
|
return fmt.Sprintf("declare const %s: %s", f.Name, f.ReturnType)
|
|
}
|
|
return fmt.Sprintf("declare function %s(%s): %s", f.Name, f.formatParams(), f.ReturnType)
|
|
}
|
|
|
|
func (f Function) String() string {
|
|
return fmt.Sprintf("%s\n%s;\n\n", f.docs(), f.formatMain())
|
|
}
|