mirror of
				https://github.com/coredns/coredns.git
				synced 2025-11-03 18:53:13 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			174 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			174 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
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"`
 | 
						|
}
 |