mirror of
https://github.com/coredns/coredns.git
synced 2025-11-01 18:53:43 -04:00
First commit
This commit is contained in:
173
core/caddyfile/json.go
Normal file
173
core/caddyfile/json.go
Normal file
@@ -0,0 +1,173 @@
|
||||
package caddyfile
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/miekg/coredns/core/parse"
|
||||
)
|
||||
|
||||
const filename = "Caddyfile"
|
||||
|
||||
// ToJSON converts caddyfile to its JSON representation.
|
||||
func ToJSON(caddyfile []byte) ([]byte, error) {
|
||||
var j Caddyfile
|
||||
|
||||
serverBlocks, err := parse.ServerBlocks(filename, bytes.NewReader(caddyfile), false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, sb := range serverBlocks {
|
||||
block := ServerBlock{Body: [][]interface{}{}}
|
||||
|
||||
// Fill up host list
|
||||
for _, host := range sb.HostList() {
|
||||
block.Hosts = append(block.Hosts, host)
|
||||
}
|
||||
|
||||
// Extract directives deterministically by sorting them
|
||||
var directives = make([]string, len(sb.Tokens))
|
||||
for dir := range sb.Tokens {
|
||||
directives = append(directives, dir)
|
||||
}
|
||||
sort.Strings(directives)
|
||||
|
||||
// Convert each directive's tokens into our JSON structure
|
||||
for _, dir := range directives {
|
||||
disp := parse.NewDispenserTokens(filename, sb.Tokens[dir])
|
||||
for disp.Next() {
|
||||
block.Body = append(block.Body, constructLine(&disp))
|
||||
}
|
||||
}
|
||||
|
||||
// tack this block onto the end of the list
|
||||
j = append(j, block)
|
||||
}
|
||||
|
||||
result, err := json.Marshal(j)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// constructLine transforms tokens into a JSON-encodable structure;
|
||||
// but only one line at a time, to be used at the top-level of
|
||||
// a server block only (where the first token on each line is a
|
||||
// directive) - not to be used at any other nesting level.
|
||||
func constructLine(d *parse.Dispenser) []interface{} {
|
||||
var args []interface{}
|
||||
|
||||
args = append(args, d.Val())
|
||||
|
||||
for d.NextArg() {
|
||||
if d.Val() == "{" {
|
||||
args = append(args, constructBlock(d))
|
||||
continue
|
||||
}
|
||||
args = append(args, d.Val())
|
||||
}
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
// constructBlock recursively processes tokens into a
|
||||
// JSON-encodable structure. To be used in a directive's
|
||||
// block. Goes to end of block.
|
||||
func constructBlock(d *parse.Dispenser) [][]interface{} {
|
||||
block := [][]interface{}{}
|
||||
|
||||
for d.Next() {
|
||||
if d.Val() == "}" {
|
||||
break
|
||||
}
|
||||
block = append(block, constructLine(d))
|
||||
}
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
// FromJSON converts JSON-encoded jsonBytes to Caddyfile text
|
||||
func FromJSON(jsonBytes []byte) ([]byte, error) {
|
||||
var j Caddyfile
|
||||
var result string
|
||||
|
||||
err := json.Unmarshal(jsonBytes, &j)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for sbPos, sb := range j {
|
||||
if sbPos > 0 {
|
||||
result += "\n\n"
|
||||
}
|
||||
for i, host := range sb.Hosts {
|
||||
if i > 0 {
|
||||
result += ", "
|
||||
}
|
||||
result += host
|
||||
}
|
||||
result += jsonToText(sb.Body, 1)
|
||||
}
|
||||
|
||||
return []byte(result), nil
|
||||
}
|
||||
|
||||
// jsonToText recursively transforms a scope of JSON into plain
|
||||
// Caddyfile text.
|
||||
func jsonToText(scope interface{}, depth int) string {
|
||||
var result string
|
||||
|
||||
switch val := scope.(type) {
|
||||
case string:
|
||||
if strings.ContainsAny(val, "\" \n\t\r") {
|
||||
result += `"` + strings.Replace(val, "\"", "\\\"", -1) + `"`
|
||||
} else {
|
||||
result += val
|
||||
}
|
||||
case int:
|
||||
result += strconv.Itoa(val)
|
||||
case float64:
|
||||
result += fmt.Sprintf("%v", val)
|
||||
case bool:
|
||||
result += fmt.Sprintf("%t", val)
|
||||
case [][]interface{}:
|
||||
result += " {\n"
|
||||
for _, arg := range val {
|
||||
result += strings.Repeat("\t", depth) + jsonToText(arg, depth+1) + "\n"
|
||||
}
|
||||
result += strings.Repeat("\t", depth-1) + "}"
|
||||
case []interface{}:
|
||||
for i, v := range val {
|
||||
if block, ok := v.([]interface{}); ok {
|
||||
result += "{\n"
|
||||
for _, arg := range block {
|
||||
result += strings.Repeat("\t", depth) + jsonToText(arg, depth+1) + "\n"
|
||||
}
|
||||
result += strings.Repeat("\t", depth-1) + "}"
|
||||
continue
|
||||
}
|
||||
result += jsonToText(v, depth)
|
||||
if i < len(val)-1 {
|
||||
result += " "
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Caddyfile encapsulates a slice of ServerBlocks.
|
||||
type Caddyfile []ServerBlock
|
||||
|
||||
// ServerBlock represents a server block.
|
||||
type ServerBlock struct {
|
||||
Hosts []string `json:"hosts"`
|
||||
Body [][]interface{} `json:"body"`
|
||||
}
|
||||
Reference in New Issue
Block a user