1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-03-14 01:34:47 +08:00

feat: 增加nginx config 格式化工具

This commit is contained in:
zhengkunwang223 2022-10-24 23:06:49 +08:00 committed by zhengkunwang223
parent 7dad47464d
commit a1ac689a5e
17 changed files with 1232 additions and 0 deletions

View File

@ -99,3 +99,8 @@ func ExistWithStrArray(str string, arr []string) bool {
}
return false
}
func IsNum(s string) bool {
_, err := strconv.ParseFloat(s, 64)
return err == nil
}

View File

@ -0,0 +1,50 @@
package components
type Block struct {
Comment string
Directives []IDirective
}
func (b *Block) GetDirectives() []IDirective {
return b.Directives
}
func (b *Block) GetComment() string {
return b.Comment
}
func (b *Block) FindDirectives(directiveName string) []IDirective {
directives := make([]IDirective, 0)
for _, directive := range b.GetDirectives() {
if directive.GetName() == directiveName {
directives = append(directives, directive)
}
if directive.GetBlock() != nil {
directives = append(directives, directive.GetBlock().FindDirectives(directiveName)...)
}
}
return directives
}
func (b *Block) UpdateDirectives(directiveName string, directive Directive) {
directives := make([]IDirective, len(b.GetDirectives()))
index := -1
for i, dir := range b.GetDirectives() {
if dir.GetName() == directiveName {
index = i
break
}
}
if index > -1 {
directives[index] = &directive
} else {
directives = append(directives, &directive)
}
b.Directives = directives
}
func (b *Block) AddDirectives(directive Directive) {
directives := append(b.GetDirectives(), &directive)
b.Directives = directives
}

View File

@ -0,0 +1,21 @@
package components
type Comment struct {
Detail string
}
func (c *Comment) GetName() string {
return ""
}
func (c *Comment) GetParameters() []string {
return []string{}
}
func (c *Comment) GetBlock() IBlock {
return nil
}
func (c *Comment) GetComment() string {
return c.Detail
}

View File

@ -0,0 +1,28 @@
package components
type Config struct {
*Block
FilePath string
}
func (c *Config) FindDirectives(directiveName string) []IDirective {
return c.Block.FindDirectives(directiveName)
}
func (c *Config) FindUpstreams() []*Upstream {
var upstreams []*Upstream
directives := c.Block.FindDirectives("upstream")
for _, directive := range directives {
upstreams = append(upstreams, directive.(*Upstream))
}
return upstreams
}
func (c *Config) FindServers() []*Server {
var servers []*Server
directives := c.Block.FindDirectives("server")
for _, directive := range directives {
servers = append(servers, directive.(*Server))
}
return servers
}

View File

@ -0,0 +1,24 @@
package components
type Directive struct {
Block IBlock
Name string
Comment string
Parameters []string
}
func (d *Directive) GetComment() string {
return d.Comment
}
func (d *Directive) GetName() string {
return d.Name
}
func (d *Directive) GetParameters() []string {
return d.Parameters
}
func (d *Directive) GetBlock() IBlock {
return d.Block
}

View File

@ -0,0 +1,93 @@
package components
import (
"errors"
)
type Http struct {
Comment string
Servers []*Server
Directives []IDirective
}
func (h *Http) GetComment() string {
return h.Comment
}
func NewHttp(directive IDirective) (*Http, error) {
if block := directive.GetBlock(); block != nil {
http := &Http{
Servers: []*Server{},
Directives: []IDirective{},
Comment: block.GetComment(),
}
for _, directive := range block.GetDirectives() {
if server, ok := directive.(*Server); ok {
http.Servers = append(http.Servers, server)
continue
}
http.Directives = append(http.Directives, directive)
}
return http, nil
}
return nil, errors.New("http directive must have a block")
}
func (h *Http) GetName() string {
return "http"
}
func (h *Http) GetParameters() []string {
return []string{}
}
func (h *Http) GetDirectives() []IDirective {
directives := make([]IDirective, 0)
directives = append(directives, h.Directives...)
for _, directive := range h.Servers {
directives = append(directives, directive)
}
return directives
}
func (h *Http) FindDirectives(directiveName string) []IDirective {
directives := make([]IDirective, 0)
for _, directive := range h.GetDirectives() {
if directive.GetName() == directiveName {
directives = append(directives, directive)
}
if directive.GetBlock() != nil {
directives = append(directives, directive.GetBlock().FindDirectives(directiveName)...)
}
}
return directives
}
func (h *Http) UpdateDirectives(directiveName string, directive Directive) {
directives := make([]IDirective, len(h.GetDirectives()))
index := -1
for i, dir := range h.GetDirectives() {
if dir.GetName() == directiveName {
index = i
break
}
}
if index > -1 {
directives[index] = &directive
} else {
directives = append(directives, &directive)
}
h.Directives = directives
}
func (h *Http) AddDirectives(directive Directive) {
directives := append(h.GetDirectives(), &directive)
h.Directives = directives
}
func (h *Http) GetBlock() IBlock {
return h
}

View File

@ -0,0 +1,32 @@
package components
type Location struct {
*Directive
Modifier string
Match string
}
func NewLocation(directive *Directive) *Location {
location := &Location{
Modifier: "",
Match: "",
Directive: directive,
}
if directive.GetBlock() != nil {
directive.Comment = directive.GetBlock().GetComment()
}
if len(directive.Parameters) == 0 {
panic("no enough parameter for location")
}
if len(directive.Parameters) == 1 {
location.Match = directive.Parameters[0]
return location
} else if len(directive.Parameters) == 2 {
location.Modifier = directive.Parameters[0]
location.Match = directive.Parameters[1]
return location
}
return nil
}

View File

@ -0,0 +1,107 @@
package components
import (
"errors"
)
type Server struct {
Comment string
Listens []*ServerListen
Directives []IDirective
}
func NewServer(directive IDirective) (*Server, error) {
server := &Server{}
if block := directive.GetBlock(); block != nil {
server.Comment = block.GetComment()
directives := block.GetDirectives()
for _, dir := range directives {
if dir.GetName() == "listen" {
server.Listens = append(server.Listens, NewServerListen(dir.GetParameters()))
} else {
server.Directives = append(server.Directives, dir)
}
}
return server, nil
}
return nil, errors.New("server directive must have a block")
}
func (s *Server) GetName() string {
return "server"
}
func (s *Server) GetParameters() []string {
return []string{}
}
func (s *Server) GetBlock() IBlock {
return s
}
func (s *Server) GetComment() string {
return s.Comment
}
func (s *Server) GetDirectives() []IDirective {
directives := make([]IDirective, 0)
for _, ls := range s.Listens {
directives = append(directives, ls)
}
directives = append(directives, s.Directives...)
return directives
}
func (s *Server) AddListen(bind string, defaultServer bool, params ...string) {
listen := &ServerListen{
Bind: bind,
Parameters: params,
}
if defaultServer {
listen.DefaultServer = DefaultServer
}
s.Listens = append(s.Listens, listen)
}
func (s *Server) RemoveListenByBind(bind string) {
index := 0
listens := s.Listens
for _, listen := range s.Listens {
if listen.Bind != bind || len(listen.Parameters) > 0 {
listens[index] = listen
index++
}
}
s.Listens = listens
}
func (s *Server) FindDirectives(directiveName string) []IDirective {
directives := make([]IDirective, 0)
for _, directive := range s.Directives {
if directive.GetName() == directiveName {
directives = append(directives, directive)
}
if directive.GetBlock() != nil {
directives = append(directives, directive.GetBlock().FindDirectives(directiveName)...)
}
}
return directives
}
func (s *Server) UpdateDirectives(directiveName string, directive Directive) {
directives := make([]IDirective, 0)
for _, dir := range s.Directives {
if dir.GetName() == directiveName {
directives = append(directives, &directive)
} else {
directives = append(directives, dir)
}
}
s.Directives = directives
}
func (s *Server) AddDirectives(directive Directive) {
directives := append(s.Directives, &directive)
s.Directives = directives
}

View File

@ -0,0 +1,68 @@
package components
import (
"github.com/1Panel-dev/1Panel/backend/utils/common"
"strings"
)
const DefaultServer = "default_server"
type ServerListen struct {
Bind string
DefaultServer string
Parameters []string
Comment string
}
func NewServerListen(params []string) *ServerListen {
server := &ServerListen{
Parameters: []string{},
}
for _, param := range params {
if isBind(param) {
server.Bind = param
} else if param == DefaultServer {
server.DefaultServer = DefaultServer
} else {
server.Parameters = append(server.Parameters, param)
}
}
return server
}
func isBind(param string) bool {
if common.IsNum(param) {
return true
}
if strings.Contains(param, "*") || strings.Contains(param, ":") || strings.Contains(param, ".") {
return true
}
return false
}
func (sl *ServerListen) GetName() string {
return "listen"
}
func (sl *ServerListen) GetBlock() IBlock {
return nil
}
func (sl *ServerListen) GetParameters() []string {
params := []string{sl.Bind}
params = append(params, sl.DefaultServer)
params = append(params, sl.Parameters...)
return params
}
func (sl *ServerListen) GetComment() string {
return sl.Comment
}
func (sl *ServerListen) AddDefaultServer() {
sl.DefaultServer = DefaultServer
}
func (sl *ServerListen) RemoveDefaultServe() {
sl.DefaultServer = ""
}

View File

@ -0,0 +1,24 @@
package components
type IBlock interface {
GetDirectives() []IDirective
FindDirectives(directiveName string) []IDirective
UpdateDirectives(directiveName string, directive Directive)
AddDirectives(directive Directive)
GetComment() string
}
type IDirective interface {
GetName() string
GetParameters() []string
GetBlock() IBlock
GetComment() string
}
type FileDirective interface {
isFileDirective()
}
type IncludeDirective interface {
FileDirective
}

View File

@ -0,0 +1,94 @@
package components
import (
"errors"
)
type Upstream struct {
UpstreamName string
UpstreamServers []*UpstreamServer
Directives []IDirective
Comment string
}
func (us *Upstream) GetName() string {
return "upstream"
}
func (us *Upstream) GetParameters() []string {
return []string{us.UpstreamName}
}
func (us *Upstream) GetBlock() IBlock {
return us
}
func (us *Upstream) GetComment() string {
return us.Comment
}
func (us *Upstream) GetDirectives() []IDirective {
directives := make([]IDirective, 0)
directives = append(directives, us.Directives...)
for _, uss := range us.UpstreamServers {
directives = append(directives, uss)
}
return directives
}
func NewUpstream(directive IDirective) (*Upstream, error) {
parameters := directive.GetParameters()
us := &Upstream{
UpstreamName: parameters[0],
}
if block := directive.GetBlock(); block != nil {
us.Comment = block.GetComment()
for _, d := range block.GetDirectives() {
if d.GetName() == "server" {
us.UpstreamServers = append(us.UpstreamServers, NewUpstreamServer(d))
} else {
us.Directives = append(us.Directives, d)
}
}
return us, nil
}
return nil, errors.New("missing upstream block")
}
func (us *Upstream) AddServer(server *UpstreamServer) {
us.UpstreamServers = append(us.UpstreamServers, server)
}
func (us *Upstream) FindDirectives(directiveName string) []IDirective {
directives := make([]IDirective, 0)
for _, directive := range us.Directives {
if directive.GetName() == directiveName {
directives = append(directives, directive)
}
if directive.GetBlock() != nil {
directives = append(directives, directive.GetBlock().FindDirectives(directiveName)...)
}
}
return directives
}
func (us *Upstream) UpdateDirectives(directiveName string, directive Directive) {
directives := make([]IDirective, 0)
for _, dir := range us.GetDirectives() {
if dir.GetName() == directiveName {
directives = append(directives, &directive)
} else {
directives = append(directives, dir)
}
}
us.Directives = directives
}
func (us *Upstream) AddDirectives(directive Directive) {
directives := append(us.GetDirectives(), &directive)
us.Directives = directives
}

View File

@ -0,0 +1,77 @@
package components
import (
"fmt"
"sort"
"strings"
)
type UpstreamServer struct {
Comment string
Address string
Flags []string
Parameters map[string]string
}
func (uss *UpstreamServer) GetName() string {
return "server"
}
func (uss *UpstreamServer) GetBlock() IBlock {
return nil
}
func (uss *UpstreamServer) GetParameters() []string {
return uss.GetDirective().Parameters
}
func (uss *UpstreamServer) GetComment() string {
return uss.Comment
}
func (uss *UpstreamServer) GetDirective() *Directive {
directive := &Directive{
Name: "server",
Parameters: make([]string, 0),
Block: nil,
}
directive.Parameters = append(directive.Parameters, uss.Address)
paramNames := make([]string, 0)
for k := range uss.Parameters {
paramNames = append(paramNames, k)
}
sort.Strings(paramNames)
for _, k := range paramNames {
directive.Parameters = append(directive.Parameters, fmt.Sprintf("%s=%s", k, uss.Parameters[k]))
}
directive.Parameters = append(directive.Parameters, uss.Flags...)
return directive
}
func NewUpstreamServer(directive IDirective) *UpstreamServer {
uss := &UpstreamServer{
Comment: directive.GetComment(),
Flags: make([]string, 0),
Parameters: make(map[string]string, 0),
}
for i, parameter := range directive.GetParameters() {
if i == 0 {
uss.Address = parameter
continue
}
if strings.Contains(parameter, "=") {
s := strings.SplitN(parameter, "=", 2)
uss.Parameters[s[0]] = s[1]
} else {
uss.Flags = append(uss.Flags, parameter)
}
}
return uss
}

View File

@ -0,0 +1,132 @@
package nginx
import (
"bytes"
"fmt"
components "github.com/1Panel-dev/1Panel/backend/utils/nginx/components"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
)
var (
//NoIndentStyle default style
NoIndentStyle = &Style{
SortDirectives: false,
StartIndent: 0,
Indent: 0,
}
//IndentedStyle default style
IndentedStyle = &Style{
SortDirectives: false,
StartIndent: 0,
Indent: 4,
}
//NoIndentSortedStyle default style
NoIndentSortedStyle = &Style{
SortDirectives: true,
StartIndent: 0,
Indent: 0,
}
//NoIndentSortedSpaceStyle default style
NoIndentSortedSpaceStyle = &Style{
SortDirectives: true,
SpaceBeforeBlocks: true,
StartIndent: 0,
Indent: 0,
}
)
type Style struct {
SortDirectives bool
SpaceBeforeBlocks bool
StartIndent int
Indent int
}
func NewStyle() *Style {
style := &Style{
SortDirectives: false,
StartIndent: 0,
Indent: 4,
}
return style
}
func (s *Style) Iterate() *Style {
newStyle := &Style{
SortDirectives: s.SortDirectives,
SpaceBeforeBlocks: s.SpaceBeforeBlocks,
StartIndent: s.StartIndent + s.Indent,
Indent: s.Indent,
}
return newStyle
}
func DumpDirective(d components.IDirective, style *Style) string {
var buf bytes.Buffer
if style.SpaceBeforeBlocks && d.GetBlock() != nil {
buf.WriteString("\n")
}
buf.WriteString(fmt.Sprintf("%s%s", strings.Repeat(" ", style.StartIndent), d.GetName()))
if len(d.GetParameters()) > 0 {
buf.WriteString(fmt.Sprintf(" %s", strings.Join(d.GetParameters(), " ")))
}
if d.GetBlock() == nil {
if d.GetName() != "" {
buf.WriteRune(';')
buf.WriteString(" ")
}
if d.GetComment() != "" {
buf.WriteString(d.GetComment())
}
} else {
buf.WriteString(" {")
if d.GetComment() != "" {
buf.WriteString(" ")
buf.WriteString(d.GetComment())
}
buf.WriteString("\n")
buf.WriteString(DumpBlock(d.GetBlock(), style.Iterate()))
buf.WriteString(fmt.Sprintf("\n%s}", strings.Repeat(" ", style.StartIndent)))
}
return buf.String()
}
func DumpBlock(b components.IBlock, style *Style) string {
var buf bytes.Buffer
directives := b.GetDirectives()
if style.SortDirectives {
sort.SliceStable(directives, func(i, j int) bool {
return directives[i].GetName() < directives[j].GetName()
})
}
for i, directive := range directives {
buf.WriteString(DumpDirective(directive, style))
if i != len(directives)-1 {
buf.WriteString("\n")
}
}
return buf.String()
}
func DumpConfig(c *components.Config, style *Style) string {
return DumpBlock(c.Block, style)
}
func WriteConfig(c *components.Config, style *Style) error {
dir, _ := filepath.Split(c.FilePath)
err := os.MkdirAll(dir, 0755)
if err != nil {
return err
}
return ioutil.WriteFile(c.FilePath, []byte(DumpConfig(c, style)), 0644)
}

View File

@ -0,0 +1,14 @@
package nginx
import (
"github.com/1Panel-dev/1Panel/backend/utils/nginx/components"
"github.com/1Panel-dev/1Panel/backend/utils/nginx/parser"
)
func GetConfig(path string) (*components.Config, error) {
p, err := parser.NewParser(path)
if err != nil {
return nil, err
}
return p.Parse(), nil
}

View File

@ -0,0 +1,82 @@
package flag
import (
"fmt"
)
type Type int
const (
EOF Type = iota
Eol
Keyword
QuotedString
Variable
BlockStart
BlockEnd
Semicolon
Comment
Illegal
Regex
)
var (
FlagName = map[Type]string{
QuotedString: "QuotedString",
EOF: "Eof",
Keyword: "Keyword",
Variable: "Variable",
BlockStart: "BlockStart",
BlockEnd: "BlockEnd",
Semicolon: "Semicolon",
Comment: "Comment",
Illegal: "Illegal",
Regex: "Regex",
}
)
func (tt Type) String() string {
return FlagName[tt]
}
type Flag struct {
Type Type
Literal string
Line int
Column int
}
func (t Flag) String() string {
return fmt.Sprintf("{Type:%s,Literal:\"%s\",Line:%d,Column:%d}", t.Type, t.Literal, t.Line, t.Column)
}
func (t Flag) Lit(literal string) Flag {
t.Literal = literal
return t
}
func (t Flag) EqualTo(t2 Flag) bool {
return t.Type == t2.Type && t.Literal == t2.Literal
}
type Flags []Flag
func (fs Flags) EqualTo(flags Flags) bool {
if len(fs) != len(flags) {
return false
}
for i, t := range fs {
if !t.EqualTo(flags[i]) {
return false
}
}
return true
}
func (t Flag) Is(typ Type) bool {
return t.Type == typ
}
func (t Flag) IsParameterEligible() bool {
return t.Is(Keyword) || t.Is(QuotedString) || t.Is(Variable) || t.Is(Regex)
}

View File

@ -0,0 +1,209 @@
package parser
import (
"bufio"
"bytes"
"github.com/1Panel-dev/1Panel/backend/utils/nginx/parser/flag"
"io"
)
type lexer struct {
reader *bufio.Reader
file string
line int
column int
Latest flag.Flag
}
func lex(content string) *lexer {
return newLexer(bytes.NewBuffer([]byte(content)))
}
func newLexer(r io.Reader) *lexer {
return &lexer{
line: 1,
reader: bufio.NewReader(r),
}
}
func (s *lexer) scan() flag.Flag {
s.Latest = s.getNextFlag()
return s.Latest
}
//func (s *lexer) all() flag.Flags {
// tokens := make([]flag.Flag, 0)
// for {
// v := s.scan()
// if v.Type == flag.EOF || v.Type == -1 {
// break
// }
// tokens = append(tokens, v)
// }
// return tokens
//}
func (s *lexer) getNextFlag() flag.Flag {
retoFlag:
ch := s.peek()
switch {
case isSpace(ch):
s.skipWhitespace()
goto retoFlag
case isEOF(ch):
return s.NewToken(flag.EOF).Lit(string(s.read()))
case ch == ';':
return s.NewToken(flag.Semicolon).Lit(string(s.read()))
case ch == '{':
return s.NewToken(flag.BlockStart).Lit(string(s.read()))
case ch == '}':
return s.NewToken(flag.BlockEnd).Lit(string(s.read()))
case ch == '#':
return s.scanComment()
case ch == '$':
return s.scanVariable()
case isQuote(ch):
return s.scanQuotedString(ch)
default:
return s.scanKeyword()
}
}
func (s *lexer) peek() rune {
r, _, _ := s.reader.ReadRune()
_ = s.reader.UnreadRune()
return r
}
type runeCheck func(rune) bool
func (s *lexer) readUntil(until runeCheck) string {
var buf bytes.Buffer
buf.WriteRune(s.read())
for {
if ch := s.peek(); isEOF(ch) {
break
} else if until(ch) {
break
} else {
buf.WriteRune(s.read())
}
}
return buf.String()
}
func (s *lexer) NewToken(tokenType flag.Type) flag.Flag {
return flag.Flag{
Type: tokenType,
Line: s.line,
Column: s.column,
}
}
func (s *lexer) readWhile(while runeCheck) string {
var buf bytes.Buffer
buf.WriteRune(s.read())
for {
if ch := s.peek(); while(ch) {
buf.WriteRune(s.read())
} else {
break
}
}
return buf.String()
}
func (s *lexer) skipWhitespace() {
s.readWhile(isSpace)
}
func (s *lexer) scanComment() flag.Flag {
return s.NewToken(flag.Comment).Lit(s.readUntil(isEndOfLine))
}
func (s *lexer) scanQuotedString(delimiter rune) flag.Flag {
var buf bytes.Buffer
tok := s.NewToken(flag.QuotedString)
buf.WriteRune(s.read()) //consume delimiter
for {
ch := s.read()
if ch == rune(flag.EOF) {
panic("unexpected end of file while scanning a string, maybe an unclosed quote?")
}
if ch == '\\' {
if needsEscape(s.peek(), delimiter) {
switch s.read() {
case 'n':
buf.WriteRune('\n')
case 'r':
buf.WriteRune('\r')
case 't':
buf.WriteRune('\t')
case '\\':
buf.WriteRune('\\')
case delimiter:
buf.WriteRune(delimiter)
}
continue
}
}
buf.WriteRune(ch)
if ch == delimiter {
break
}
}
return tok.Lit(buf.String())
}
func (s *lexer) scanKeyword() flag.Flag {
return s.NewToken(flag.Keyword).Lit(s.readUntil(isKeywordTerminator))
}
func (s *lexer) scanVariable() flag.Flag {
return s.NewToken(flag.Variable).Lit(s.readUntil(isKeywordTerminator))
}
func (s *lexer) read() rune {
ch, _, err := s.reader.ReadRune()
if err != nil {
return rune(flag.EOF)
}
if ch == '\n' {
s.column = 1
s.line++
} else {
s.column++
}
return ch
}
func isQuote(ch rune) bool {
return ch == '"' || ch == '\'' || ch == '`'
}
func isKeywordTerminator(ch rune) bool {
return isSpace(ch) || isEndOfLine(ch) || ch == '{' || ch == ';'
}
func needsEscape(ch, delimiter rune) bool {
return ch == delimiter || ch == 'n' || ch == 't' || ch == '\\' || ch == 'r'
}
func isSpace(ch rune) bool {
return ch == ' ' || ch == '\t' || isEndOfLine(ch)
}
func isEOF(ch rune) bool {
return ch == rune(flag.EOF)
}
func isEndOfLine(ch rune) bool {
return ch == '\r' || ch == '\n'
}

View File

@ -0,0 +1,172 @@
package parser
import (
"bufio"
"fmt"
components "github.com/1Panel-dev/1Panel/backend/utils/nginx/components"
"github.com/1Panel-dev/1Panel/backend/utils/nginx/parser/flag"
"os"
)
type Parser struct {
lexer *lexer
currentToken flag.Flag
followingToken flag.Flag
blockWrappers map[string]func(*components.Directive) components.IDirective
directiveWrappers map[string]func(*components.Directive) components.IDirective
}
func NewStringParser(str string) *Parser {
return NewParserFromLexer(lex(str))
}
func NewParser(filePath string) (*Parser, error) {
f, err := os.Open(filePath)
if err != nil {
return nil, err
}
l := newLexer(bufio.NewReader(f))
l.file = filePath
p := NewParserFromLexer(l)
return p, nil
}
func NewParserFromLexer(lexer *lexer) *Parser {
parser := &Parser{
lexer: lexer,
}
parser.nextToken()
parser.nextToken()
parser.blockWrappers = map[string]func(*components.Directive) components.IDirective{
"http": func(directive *components.Directive) components.IDirective {
return parser.wrapHttp(directive)
},
"server": func(directive *components.Directive) components.IDirective {
return parser.wrapServer(directive)
},
"location": func(directive *components.Directive) components.IDirective {
return parser.wrapLocation(directive)
},
"upstream": func(directive *components.Directive) components.IDirective {
return parser.wrapUpstream(directive)
},
}
parser.directiveWrappers = map[string]func(*components.Directive) components.IDirective{
"server": func(directive *components.Directive) components.IDirective {
return parser.parseUpstreamServer(directive)
},
}
return parser
}
func (p *Parser) nextToken() {
p.currentToken = p.followingToken
p.followingToken = p.lexer.scan()
}
func (p *Parser) curTokenIs(t flag.Type) bool {
return p.currentToken.Type == t
}
func (p *Parser) followingTokenIs(t flag.Type) bool {
return p.followingToken.Type == t
}
func (p *Parser) Parse() *components.Config {
return &components.Config{
FilePath: p.lexer.file,
Block: p.parseBlock(),
}
}
func (p *Parser) parseBlock() *components.Block {
context := &components.Block{
Comment: "",
Directives: make([]components.IDirective, 0),
}
parsingloop:
for {
switch {
case p.curTokenIs(flag.EOF) || p.curTokenIs(flag.BlockEnd):
break parsingloop
case p.curTokenIs(flag.Keyword):
context.Directives = append(context.Directives, p.parseStatement())
case p.curTokenIs(flag.Comment):
context.Directives = append(context.Directives, &components.Comment{
Detail: p.currentToken.Literal,
})
}
p.nextToken()
}
return context
}
func (p *Parser) parseStatement() components.IDirective {
d := &components.Directive{
Name: p.currentToken.Literal,
}
for p.nextToken(); p.currentToken.IsParameterEligible(); p.nextToken() {
d.Parameters = append(d.Parameters, p.currentToken.Literal)
}
if p.curTokenIs(flag.Semicolon) {
if dw, ok := p.directiveWrappers[d.Name]; ok {
return dw(d)
}
if p.followingTokenIs(flag.Comment) && p.currentToken.Line == p.followingToken.Line {
d.Comment = p.followingToken.Literal
p.nextToken()
}
return d
}
if p.curTokenIs(flag.BlockStart) {
inLineComment := ""
if p.followingTokenIs(flag.Comment) && p.currentToken.Line == p.followingToken.Line {
inLineComment = p.followingToken.Literal
p.nextToken()
p.nextToken()
}
block := p.parseBlock()
block.Comment = inLineComment
d.Block = block
if bw, ok := p.blockWrappers[d.Name]; ok {
return bw(d)
}
return d
}
panic(fmt.Errorf("unexpected token %s (%s) on line %d, column %d", p.currentToken.Type.String(), p.currentToken.Literal, p.currentToken.Line, p.currentToken.Column))
}
func (p *Parser) wrapLocation(directive *components.Directive) *components.Location {
return components.NewLocation(directive)
}
func (p *Parser) wrapServer(directive *components.Directive) *components.Server {
s, _ := components.NewServer(directive)
return s
}
func (p *Parser) wrapUpstream(directive *components.Directive) *components.Upstream {
s, _ := components.NewUpstream(directive)
return s
}
func (p *Parser) wrapHttp(directive *components.Directive) *components.Http {
h, _ := components.NewHttp(directive)
return h
}
func (p *Parser) parseUpstreamServer(directive *components.Directive) *components.UpstreamServer {
return components.NewUpstreamServer(directive)
}