Vendor nftables library, add utils.SupportsIPTables and utils.SupportsNFTables
Signed-off-by: Dan Winship <danwinship@redhat.com>
This commit is contained in:

committed by
Casey Callendrello

parent
a6d6efa5ca
commit
729dd23c40
581
vendor/sigs.k8s.io/knftables/objects.go
generated
vendored
Normal file
581
vendor/sigs.k8s.io/knftables/objects.go
generated
vendored
Normal file
@ -0,0 +1,581 @@
|
||||
/*
|
||||
Copyright 2023 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package knftables
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func parseInt(numbersOnly string) *int {
|
||||
i64, _ := strconv.ParseInt(numbersOnly, 10, 64)
|
||||
i := int(i64)
|
||||
return &i
|
||||
}
|
||||
|
||||
func parseUint(numbersOnly string) *uint64 {
|
||||
ui64, _ := strconv.ParseUint(numbersOnly, 10, 64)
|
||||
return &ui64
|
||||
}
|
||||
|
||||
// getComment parses a match for the commentGroup regexp (below). To distinguish between empty comment and no comment,
|
||||
// we capture comment with double quotes.
|
||||
func getComment(commentGroup string) *string {
|
||||
if commentGroup == "" {
|
||||
return nil
|
||||
}
|
||||
noQuotes := strings.Trim(commentGroup, "\"")
|
||||
return &noQuotes
|
||||
}
|
||||
|
||||
var commentGroup = `(".*")`
|
||||
var noSpaceGroup = `([^ ]*)`
|
||||
var numberGroup = `([0-9]*)`
|
||||
|
||||
// Object implementation for Table
|
||||
func (table *Table) validate(verb verb) error {
|
||||
switch verb {
|
||||
case addVerb, createVerb, flushVerb:
|
||||
if table.Handle != nil {
|
||||
return fmt.Errorf("cannot specify Handle in %s operation", verb)
|
||||
}
|
||||
case deleteVerb:
|
||||
// Handle can be nil or non-nil
|
||||
default:
|
||||
return fmt.Errorf("%s is not implemented for tables", verb)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (table *Table) writeOperation(verb verb, ctx *nftContext, writer io.Writer) {
|
||||
// Special case for delete-by-handle
|
||||
if verb == deleteVerb && table.Handle != nil {
|
||||
fmt.Fprintf(writer, "delete table %s handle %d", ctx.family, *table.Handle)
|
||||
return
|
||||
}
|
||||
|
||||
// All other cases refer to the table by name
|
||||
fmt.Fprintf(writer, "%s table %s %s", verb, ctx.family, ctx.table)
|
||||
if verb == addVerb || verb == createVerb {
|
||||
if table.Comment != nil && !ctx.noObjectComments {
|
||||
fmt.Fprintf(writer, " { comment %q ; }", *table.Comment)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(writer, "\n")
|
||||
}
|
||||
|
||||
var tableRegexp = regexp.MustCompile(fmt.Sprintf(
|
||||
`(?:{ comment %s ; })?`, commentGroup))
|
||||
|
||||
func (table *Table) parse(line string) error {
|
||||
match := tableRegexp.FindStringSubmatch(line)
|
||||
if match == nil {
|
||||
return fmt.Errorf("failed parsing table add command")
|
||||
}
|
||||
table.Comment = getComment(match[1])
|
||||
return nil
|
||||
}
|
||||
|
||||
// Object implementation for Chain
|
||||
func (chain *Chain) validate(verb verb) error {
|
||||
if chain.Hook == nil {
|
||||
if chain.Type != nil || chain.Priority != nil {
|
||||
return fmt.Errorf("regular chain %q must not specify Type or Priority", chain.Name)
|
||||
}
|
||||
if chain.Device != nil {
|
||||
return fmt.Errorf("regular chain %q must not specify Device", chain.Name)
|
||||
}
|
||||
} else {
|
||||
if chain.Type == nil || chain.Priority == nil {
|
||||
return fmt.Errorf("base chain %q must specify Type and Priority", chain.Name)
|
||||
}
|
||||
}
|
||||
|
||||
switch verb {
|
||||
case addVerb, createVerb, flushVerb:
|
||||
if chain.Name == "" {
|
||||
return fmt.Errorf("no name specified for chain")
|
||||
}
|
||||
if chain.Handle != nil {
|
||||
return fmt.Errorf("cannot specify Handle in %s operation", verb)
|
||||
}
|
||||
case deleteVerb:
|
||||
if chain.Name == "" && chain.Handle == nil {
|
||||
return fmt.Errorf("must specify either name or handle")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("%s is not implemented for chains", verb)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (chain *Chain) writeOperation(verb verb, ctx *nftContext, writer io.Writer) {
|
||||
// Special case for delete-by-handle
|
||||
if verb == deleteVerb && chain.Handle != nil {
|
||||
fmt.Fprintf(writer, "delete chain %s %s handle %d", ctx.family, ctx.table, *chain.Handle)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "%s chain %s %s %s", verb, ctx.family, ctx.table, chain.Name)
|
||||
if verb == addVerb || verb == createVerb {
|
||||
if chain.Type != nil || (chain.Comment != nil && !ctx.noObjectComments) {
|
||||
fmt.Fprintf(writer, " {")
|
||||
|
||||
if chain.Type != nil {
|
||||
fmt.Fprintf(writer, " type %s hook %s", *chain.Type, *chain.Hook)
|
||||
if chain.Device != nil {
|
||||
fmt.Fprintf(writer, " device %q", *chain.Device)
|
||||
}
|
||||
|
||||
// Parse the priority to a number if we can, because older
|
||||
// versions of nft don't accept certain named priorities
|
||||
// in all contexts (eg, "dstnat" priority in the "output"
|
||||
// hook).
|
||||
if priority, err := ParsePriority(ctx.family, string(*chain.Priority)); err == nil {
|
||||
fmt.Fprintf(writer, " priority %d ;", priority)
|
||||
} else {
|
||||
fmt.Fprintf(writer, " priority %s ;", *chain.Priority)
|
||||
}
|
||||
}
|
||||
if chain.Comment != nil && !ctx.noObjectComments {
|
||||
fmt.Fprintf(writer, " comment %q ;", *chain.Comment)
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, " }")
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "\n")
|
||||
}
|
||||
|
||||
// groups in []: [1]%s(?: {(?: type [2]%s hook [3]%s(?: device "[4]%s")(?: priority [5]%s ;))(?: comment [6]%s ;) })
|
||||
var chainRegexp = regexp.MustCompile(fmt.Sprintf(
|
||||
`%s(?: {(?: type %s hook %s(?: device "%s")?(?: priority %s ;))?(?: comment %s ;)? })?`,
|
||||
noSpaceGroup, noSpaceGroup, noSpaceGroup, noSpaceGroup, noSpaceGroup, commentGroup))
|
||||
|
||||
func (chain *Chain) parse(line string) error {
|
||||
match := chainRegexp.FindStringSubmatch(line)
|
||||
if match == nil {
|
||||
return fmt.Errorf("failed parsing chain add command")
|
||||
}
|
||||
chain.Name = match[1]
|
||||
chain.Comment = getComment(match[6])
|
||||
if match[2] != "" {
|
||||
chain.Type = (*BaseChainType)(&match[2])
|
||||
}
|
||||
if match[3] != "" {
|
||||
chain.Hook = (*BaseChainHook)(&match[3])
|
||||
}
|
||||
if match[4] != "" {
|
||||
chain.Device = &match[4]
|
||||
}
|
||||
if match[5] != "" {
|
||||
chain.Priority = (*BaseChainPriority)(&match[5])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Object implementation for Rule
|
||||
func (rule *Rule) validate(verb verb) error {
|
||||
if rule.Chain == "" {
|
||||
return fmt.Errorf("no chain name specified for rule")
|
||||
}
|
||||
|
||||
if rule.Index != nil && rule.Handle != nil {
|
||||
return fmt.Errorf("cannot specify both Index and Handle")
|
||||
}
|
||||
|
||||
switch verb {
|
||||
case addVerb, insertVerb:
|
||||
if rule.Rule == "" {
|
||||
return fmt.Errorf("no rule specified")
|
||||
}
|
||||
case replaceVerb:
|
||||
if rule.Rule == "" {
|
||||
return fmt.Errorf("no rule specified")
|
||||
}
|
||||
if rule.Handle == nil {
|
||||
return fmt.Errorf("must specify Handle with %s", verb)
|
||||
}
|
||||
case deleteVerb:
|
||||
if rule.Handle == nil {
|
||||
return fmt.Errorf("must specify Handle with %s", verb)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("%s is not implemented for rules", verb)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rule *Rule) writeOperation(verb verb, ctx *nftContext, writer io.Writer) {
|
||||
fmt.Fprintf(writer, "%s rule %s %s %s", verb, ctx.family, ctx.table, rule.Chain)
|
||||
if rule.Index != nil {
|
||||
fmt.Fprintf(writer, " index %d", *rule.Index)
|
||||
} else if rule.Handle != nil {
|
||||
fmt.Fprintf(writer, " handle %d", *rule.Handle)
|
||||
}
|
||||
|
||||
switch verb {
|
||||
case addVerb, insertVerb, replaceVerb:
|
||||
fmt.Fprintf(writer, " %s", rule.Rule)
|
||||
|
||||
if rule.Comment != nil {
|
||||
fmt.Fprintf(writer, " comment %q", *rule.Comment)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "\n")
|
||||
}
|
||||
|
||||
// groups in []: [1]%s(?: index [2]%s)?(?: handle [3]%s)? [4]([^"]*)(?: comment [5]%s)?$
|
||||
var ruleRegexp = regexp.MustCompile(fmt.Sprintf(
|
||||
`%s(?: index %s)?(?: handle %s)? ([^"]*)(?: comment %s)?$`,
|
||||
noSpaceGroup, numberGroup, numberGroup, commentGroup))
|
||||
|
||||
func (rule *Rule) parse(line string) error {
|
||||
match := ruleRegexp.FindStringSubmatch(line)
|
||||
if match == nil {
|
||||
return fmt.Errorf("failed parsing rule add command")
|
||||
}
|
||||
rule.Chain = match[1]
|
||||
rule.Rule = match[4]
|
||||
rule.Comment = getComment(match[5])
|
||||
if match[2] != "" {
|
||||
rule.Index = parseInt(match[2])
|
||||
}
|
||||
if match[3] != "" {
|
||||
rule.Handle = parseInt(match[3])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Object implementation for Set
|
||||
func (set *Set) validate(verb verb) error {
|
||||
switch verb {
|
||||
case addVerb, createVerb:
|
||||
if (set.Type == "" && set.TypeOf == "") || (set.Type != "" && set.TypeOf != "") {
|
||||
return fmt.Errorf("set must specify either Type or TypeOf")
|
||||
}
|
||||
if set.Handle != nil {
|
||||
return fmt.Errorf("cannot specify Handle in %s operation", verb)
|
||||
}
|
||||
fallthrough
|
||||
case flushVerb:
|
||||
if set.Name == "" {
|
||||
return fmt.Errorf("no name specified for set")
|
||||
}
|
||||
case deleteVerb:
|
||||
if set.Name == "" && set.Handle == nil {
|
||||
return fmt.Errorf("must specify either name or handle")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("%s is not implemented for sets", verb)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (set *Set) writeOperation(verb verb, ctx *nftContext, writer io.Writer) {
|
||||
// Special case for delete-by-handle
|
||||
if verb == deleteVerb && set.Handle != nil {
|
||||
fmt.Fprintf(writer, "delete set %s %s handle %d", ctx.family, ctx.table, *set.Handle)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "%s set %s %s %s", verb, ctx.family, ctx.table, set.Name)
|
||||
if verb == addVerb || verb == createVerb {
|
||||
fmt.Fprintf(writer, " {")
|
||||
|
||||
if set.Type != "" {
|
||||
fmt.Fprintf(writer, " type %s ;", set.Type)
|
||||
} else {
|
||||
fmt.Fprintf(writer, " typeof %s ;", set.TypeOf)
|
||||
}
|
||||
|
||||
if len(set.Flags) != 0 {
|
||||
fmt.Fprintf(writer, " flags ")
|
||||
for i := range set.Flags {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(writer, ",")
|
||||
}
|
||||
fmt.Fprintf(writer, "%s", set.Flags[i])
|
||||
}
|
||||
fmt.Fprintf(writer, " ;")
|
||||
}
|
||||
|
||||
if set.Timeout != nil {
|
||||
fmt.Fprintf(writer, " timeout %ds ;", int64(set.Timeout.Seconds()))
|
||||
}
|
||||
if set.GCInterval != nil {
|
||||
fmt.Fprintf(writer, " gc-interval %ds ;", int64(set.GCInterval.Seconds()))
|
||||
}
|
||||
if set.Size != nil {
|
||||
fmt.Fprintf(writer, " size %d ;", *set.Size)
|
||||
}
|
||||
if set.Policy != nil {
|
||||
fmt.Fprintf(writer, " policy %s ;", *set.Policy)
|
||||
}
|
||||
if set.AutoMerge != nil && *set.AutoMerge {
|
||||
fmt.Fprintf(writer, " auto-merge ;")
|
||||
}
|
||||
|
||||
if set.Comment != nil && !ctx.noObjectComments {
|
||||
fmt.Fprintf(writer, " comment %q ;", *set.Comment)
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, " }")
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "\n")
|
||||
}
|
||||
|
||||
func (set *Set) parse(line string) error {
|
||||
match := setRegexp.FindStringSubmatch(line)
|
||||
if match == nil {
|
||||
return fmt.Errorf("failed parsing set add command")
|
||||
}
|
||||
set.Name, set.Type, set.TypeOf, set.Flags, set.Timeout, set.GCInterval,
|
||||
set.Size, set.Policy, set.Comment, set.AutoMerge = parseMapAndSetProps(match)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Object implementation for Map
|
||||
func (mapObj *Map) validate(verb verb) error {
|
||||
switch verb {
|
||||
case addVerb, createVerb:
|
||||
if (mapObj.Type == "" && mapObj.TypeOf == "") || (mapObj.Type != "" && mapObj.TypeOf != "") {
|
||||
return fmt.Errorf("map must specify either Type or TypeOf")
|
||||
}
|
||||
if mapObj.Handle != nil {
|
||||
return fmt.Errorf("cannot specify Handle in %s operation", verb)
|
||||
}
|
||||
fallthrough
|
||||
case flushVerb:
|
||||
if mapObj.Name == "" {
|
||||
return fmt.Errorf("no name specified for map")
|
||||
}
|
||||
case deleteVerb:
|
||||
if mapObj.Name == "" && mapObj.Handle == nil {
|
||||
return fmt.Errorf("must specify either name or handle")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("%s is not implemented for maps", verb)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mapObj *Map) writeOperation(verb verb, ctx *nftContext, writer io.Writer) {
|
||||
// Special case for delete-by-handle
|
||||
if verb == deleteVerb && mapObj.Handle != nil {
|
||||
fmt.Fprintf(writer, "delete map %s %s handle %d", ctx.family, ctx.table, *mapObj.Handle)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "%s map %s %s %s", verb, ctx.family, ctx.table, mapObj.Name)
|
||||
if verb == addVerb || verb == createVerb {
|
||||
fmt.Fprintf(writer, " {")
|
||||
|
||||
if mapObj.Type != "" {
|
||||
fmt.Fprintf(writer, " type %s ;", mapObj.Type)
|
||||
} else {
|
||||
fmt.Fprintf(writer, " typeof %s ;", mapObj.TypeOf)
|
||||
}
|
||||
|
||||
if len(mapObj.Flags) != 0 {
|
||||
fmt.Fprintf(writer, " flags ")
|
||||
for i := range mapObj.Flags {
|
||||
if i > 0 {
|
||||
fmt.Fprintf(writer, ",")
|
||||
}
|
||||
fmt.Fprintf(writer, "%s", mapObj.Flags[i])
|
||||
}
|
||||
fmt.Fprintf(writer, " ;")
|
||||
}
|
||||
|
||||
if mapObj.Timeout != nil {
|
||||
fmt.Fprintf(writer, " timeout %ds ;", int64(mapObj.Timeout.Seconds()))
|
||||
}
|
||||
if mapObj.GCInterval != nil {
|
||||
fmt.Fprintf(writer, " gc-interval %ds ;", int64(mapObj.GCInterval.Seconds()))
|
||||
}
|
||||
if mapObj.Size != nil {
|
||||
fmt.Fprintf(writer, " size %d ;", *mapObj.Size)
|
||||
}
|
||||
if mapObj.Policy != nil {
|
||||
fmt.Fprintf(writer, " policy %s ;", *mapObj.Policy)
|
||||
}
|
||||
|
||||
if mapObj.Comment != nil && !ctx.noObjectComments {
|
||||
fmt.Fprintf(writer, " comment %q ;", *mapObj.Comment)
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, " }")
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "\n")
|
||||
}
|
||||
|
||||
func (mapObj *Map) parse(line string) error {
|
||||
match := mapRegexp.FindStringSubmatch(line)
|
||||
if match == nil {
|
||||
return fmt.Errorf("failed parsing map add command")
|
||||
}
|
||||
mapObj.Name, mapObj.Type, mapObj.TypeOf, mapObj.Flags, mapObj.Timeout, mapObj.GCInterval,
|
||||
mapObj.Size, mapObj.Policy, mapObj.Comment, _ = parseMapAndSetProps(match)
|
||||
return nil
|
||||
}
|
||||
|
||||
var autoMergeProp = `( auto-merge ;)?`
|
||||
|
||||
// groups in []: [1]%s {(?: [2](type|typeof) [3]([^;]*)) ;(?: flags [4]([^;]*) ;)?(?: timeout [5]%ss ;)?(?: gc-interval [6]%ss ;)?(?: size [7]%s ;)?(?: policy [8]%s ;)?[9]%s(?: comment [10]%s ;)? }
|
||||
var mapOrSet = `%s {(?: (type|typeof) ([^;]*)) ;(?: flags ([^;]*) ;)?(?: timeout %ss ;)?(?: gc-interval %ss ;)?(?: size %s ;)?(?: policy %s ;)?%s(?: comment %s ;)? }`
|
||||
var mapRegexp = regexp.MustCompile(fmt.Sprintf(mapOrSet, noSpaceGroup, numberGroup, numberGroup, noSpaceGroup, noSpaceGroup, "", commentGroup))
|
||||
var setRegexp = regexp.MustCompile(fmt.Sprintf(mapOrSet, noSpaceGroup, numberGroup, numberGroup, noSpaceGroup, noSpaceGroup, autoMergeProp, commentGroup))
|
||||
|
||||
func parseMapAndSetProps(match []string) (name string, typeProp string, typeOf string, flags []SetFlag,
|
||||
timeout *time.Duration, gcInterval *time.Duration, size *uint64, policy *SetPolicy, comment *string, autoMerge *bool) {
|
||||
name = match[1]
|
||||
// set and map have different number of match groups, but comment is always the last
|
||||
comment = getComment(match[len(match)-1])
|
||||
if match[2] == "type" {
|
||||
typeProp = match[3]
|
||||
} else {
|
||||
typeOf = match[3]
|
||||
}
|
||||
if match[4] != "" {
|
||||
flags = parseSetFlags(match[4])
|
||||
}
|
||||
if match[5] != "" {
|
||||
timeoutObj, _ := time.ParseDuration(match[5] + "s")
|
||||
timeout = &timeoutObj
|
||||
}
|
||||
if match[6] != "" {
|
||||
gcIntervalObj, _ := time.ParseDuration(match[6] + "s")
|
||||
gcInterval = &gcIntervalObj
|
||||
}
|
||||
if match[7] != "" {
|
||||
size = parseUint(match[7])
|
||||
}
|
||||
if match[8] != "" {
|
||||
policy = (*SetPolicy)(&match[8])
|
||||
}
|
||||
if len(match) > 10 {
|
||||
// set
|
||||
if match[9] != "" {
|
||||
autoMergeObj := true
|
||||
autoMerge = &autoMergeObj
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func parseSetFlags(s string) []SetFlag {
|
||||
var res []SetFlag
|
||||
for _, flag := range strings.Split(s, ",") {
|
||||
res = append(res, SetFlag(flag))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Object implementation for Element
|
||||
func (element *Element) validate(verb verb) error {
|
||||
if element.Map == "" && element.Set == "" {
|
||||
return fmt.Errorf("no set/map name specified for element")
|
||||
} else if element.Set != "" && element.Map != "" {
|
||||
return fmt.Errorf("element specifies both a set name and a map name")
|
||||
}
|
||||
|
||||
if len(element.Key) == 0 {
|
||||
return fmt.Errorf("no key specified for element")
|
||||
}
|
||||
if element.Set != "" && len(element.Value) != 0 {
|
||||
return fmt.Errorf("map value specified for set element")
|
||||
}
|
||||
|
||||
switch verb {
|
||||
case addVerb, createVerb:
|
||||
if element.Map != "" && len(element.Value) == 0 {
|
||||
return fmt.Errorf("no map value specified for map element")
|
||||
}
|
||||
case deleteVerb:
|
||||
default:
|
||||
return fmt.Errorf("%s is not implemented for elements", verb)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (element *Element) writeOperation(verb verb, ctx *nftContext, writer io.Writer) {
|
||||
name := element.Set
|
||||
if name == "" {
|
||||
name = element.Map
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, "%s element %s %s %s { %s", verb, ctx.family, ctx.table, name,
|
||||
strings.Join(element.Key, " . "))
|
||||
|
||||
if verb == addVerb || verb == createVerb {
|
||||
if element.Comment != nil {
|
||||
fmt.Fprintf(writer, " comment %q", *element.Comment)
|
||||
}
|
||||
|
||||
if len(element.Value) != 0 {
|
||||
fmt.Fprintf(writer, " : %s", strings.Join(element.Value, " . "))
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(writer, " }\n")
|
||||
}
|
||||
|
||||
// groups in []: [1]%s { [2]([^:"]*)(?: comment [3]%s)? : [4](.*) }
|
||||
var mapElementRegexp = regexp.MustCompile(fmt.Sprintf(
|
||||
`%s { ([^"]*)(?: comment %s)? : (.*) }`, noSpaceGroup, commentGroup))
|
||||
|
||||
// groups in []: [1]%s { [2]([^:"]*)(?: comment [3]%s)? }
|
||||
var setElementRegexp = regexp.MustCompile(fmt.Sprintf(
|
||||
`%s { ([^"]*)(?: comment %s)? }`, noSpaceGroup, commentGroup))
|
||||
|
||||
func (element *Element) parse(line string) error {
|
||||
// try to match map element first, since it has more groups, and if it matches, then we can be sure
|
||||
// this is map element.
|
||||
match := mapElementRegexp.FindStringSubmatch(line)
|
||||
if match == nil {
|
||||
match = setElementRegexp.FindStringSubmatch(line)
|
||||
if match == nil {
|
||||
return fmt.Errorf("failed parsing element add command")
|
||||
}
|
||||
}
|
||||
element.Comment = getComment(match[3])
|
||||
mapOrSetName := match[1]
|
||||
element.Key = append(element.Key, strings.Split(match[2], " . ")...)
|
||||
if len(match) == 5 {
|
||||
// map regex matched
|
||||
element.Map = mapOrSetName
|
||||
element.Value = append(element.Value, strings.Split(match[4], " . ")...)
|
||||
} else {
|
||||
element.Set = mapOrSetName
|
||||
}
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user