package main import ( "bytes" "encoding/json" "fmt" "log" "os" "strings" "text/template" "unicode" ) var schemaReferenceTemplate = `= JSON schema reference NOTE: This page was auto-generated with spruce-docgen. == {{ .Title }} {{ .Description }} [%header,cols=3*] |=== |Field |Type |Description {{- range $key, $property := .Properties }} {{ print "" }} |{{ $key }} |{{ type $property }} |{{ $property.Description }} {{- end -}}{{ print "" }} |=== {{ print "" }} {{- range $i, $schema := .Defs }} === {{ capitalise $i }} {{ print "" }} [%header,cols=3*] |=== |Field |Type |Description {{- range $key, $property := $schema.Properties }} {{ print "" }} |{{ $key }} |{{ type $property }} |{{ $property.Description }} {{- end -}}{{ print "" }} |=== {{ print "" }} {{- end }} ` // schema minimally represents the JSON schema format. type schema struct { Title string `json:"title"` Description string `json:"description"` Type string `json:"type"` Properties map[string]*schema `json:"properties"` Items *schema `json:"items"` Required []string `json:"required"` Ref string `json:"$ref"` Defs map[string]*schema `json:"$defs"` AdditionalProperties *schema `json:"additionalProperties"` } func (s *schema) UnmarshalJSON(data []byte) error { if bytes.Equal(data, []byte("true")) || bytes.Equal(data, []byte("false")) { *s = schema{} } else { type rawSchema schema var res rawSchema if err := json.Unmarshal(data, &res); err != nil { return fmt.Errorf("unable to unmarshal to rawSchema; %w", err) } *s = schema(res) } return nil } func main() { schemaFile := "./schema/cv.schema.json" file, err := os.Open(schemaFile) if err != nil { log.Fatal(err) } defer file.Close() decoder := json.NewDecoder(file) var data schema if err := decoder.Decode(&data); err != nil { log.Panic(err) } funcMap := template.FuncMap{ "capitalise": title, "type": getType, } t := template.Must(template.New("asciidoc").Funcs(funcMap).Parse(schemaReferenceTemplate)) if err = t.Execute(os.Stdout, data); err != nil { log.Panic(err) } } func title(str string) string { runes := []rune(str) runes[0] = unicode.ToUpper(runes[0]) return string(runes) } const ( typeArray = "array" typeObject = "object" ) func getType(data schema) string { if data.Type != "" && data.Type != typeArray && data.Type != typeObject { return data.Type } if data.Type == typeArray { switch { case data.Items == nil: return "list(UNKNOWN)" case data.Items.Type != "": return "list(" + data.Items.Type + ")" case data.Items.Ref != "": return "list(<<" + title(refType(data.Items.Ref)) + ">>)" default: return "list(UNKNOWN)" } } if data.Type == "" && data.Ref != "" { return "<<" + title(refType(data.Ref)) + ">>" } if data.Type == typeObject { if data.AdditionalProperties.Type != "" { return "map(" + data.AdditionalProperties.Type + ")" } return typeObject } return "UNKNOWN" } func refType(str string) string { prefix := "#/$defs/" if !strings.HasPrefix(str, prefix) { return "UNKNOWN" } return strings.TrimPrefix(str, prefix) }