diff --git a/fetch.go b/fetch.go index 0ed041b..04d6b12 100644 --- a/fetch.go +++ b/fetch.go @@ -103,6 +103,21 @@ func (p *configParser) SectionsGet(sectionType Section) ([]string, error) { return result, nil } +// SectionAttributesGet lists all attributes in the section of certain type and name +func (p *configParser) SectionAttributesGet(sectionType Section, sectionName string, existingOnly bool) ([]string, error) { + p.lock() + defer p.unLock() + st, ok := p.Parsers[sectionType] + if !ok { + return nil, errors.ErrSectionMissing + } + section, ok := st[sectionName] + if !ok { + return nil, errors.ErrSectionMissing + } + return section.GetAttributes(existingOnly), nil +} + // SectionsDelete deletes one section of sectionType func (p *configParser) SectionsDelete(sectionType Section, sectionName string) error { p.lock() diff --git a/parser-type.go b/parser-type.go index 38c89b5..7de069f 100644 --- a/parser-type.go +++ b/parser-type.go @@ -19,6 +19,7 @@ package parser import ( "github.com/haproxytech/config-parser/v5/common" "github.com/haproxytech/config-parser/v5/errors" + "github.com/haproxytech/config-parser/v5/parsers/extra" ) type ParserInterface interface { //nolint:revive @@ -44,6 +45,29 @@ type Parsers struct { DefaultSectionName string } +func (p *Parsers) GetAttributes(existingOnly bool) []string { + keys := make([]string, len(p.Parsers)) + i := 0 + for _, section := range p.ParserSequence { + attribute := string(section) + if attribute == "" { + continue + } + if _, ok := p.Parsers[attribute].(*extra.Section); ok { + continue + } + if existingOnly { + val, err := p.Parsers[attribute].Get(false) + if err != nil || val == nil { + continue + } + } + keys[i] = attribute + i++ + } + return keys[:i] +} + func (p *Parsers) Get(attribute string, createIfNotExist ...bool) (common.ParserData, error) { createNew := false if len(createIfNotExist) > 0 && createIfNotExist[0] { diff --git a/parser.go b/parser.go index b1f25c0..a6d630e 100644 --- a/parser.go +++ b/parser.go @@ -69,6 +69,7 @@ type Parser interface { GetPreComments(sectionType Section, sectionName string, attribute string) ([]string, error) GetOne(sectionType Section, sectionName string, attribute string, index ...int) (common.ParserData, error) SectionsGet(sectionType Section) ([]string, error) + SectionAttributesGet(sectionType Section, sectionName string, existingOnly bool) ([]string, error) SectionsDelete(sectionType Section, sectionName string) error SectionsCreate(sectionType Section, sectionName string) error SectionsDefaultsFromGet(sectionType Section, sectionName string) (string, error) diff --git a/tests/configs/parse_get_attributes_test.go b/tests/configs/parse_get_attributes_test.go new file mode 100644 index 0000000..e9ac0a2 --- /dev/null +++ b/tests/configs/parse_get_attributes_test.go @@ -0,0 +1,73 @@ +/* +Copyright 2019 HAProxy Technologies + +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 configs //nolint:testpackage + +import ( + "slices" + "strings" + "testing" + + parser "github.com/haproxytech/config-parser/v5" + "github.com/haproxytech/config-parser/v5/options" +) + +func TestParseGetAttributesExistingOnly(t *testing.T) { + tests := []struct { + Name, Config string + }{ + {"configBasic1", configBasic1}, + } + for _, config := range tests { + t.Run(config.Name, func(t *testing.T) { + p, err := parser.New(options.String(config.Config)) + if err != nil { + t.Fatalf(err.Error()) + } + result, err := p.SectionAttributesGet(parser.Frontends, "http", true) + if err != nil { + t.Fatalf("err should be nil %v", err) + } + if 0 != slices.Compare(result, []string{ + "mode", "bind", "default_backend", + }) { + t.Fatalf("retrieved attributes do not match %s", strings.Join(result, ", ")) + } + }) + } +} + +func TestParseGetAttributesComplete(t *testing.T) { + tests := []struct { + Name, Config string + }{ + {"configBasic1", configBasic1}, + } + for _, config := range tests { + t.Run(config.Name, func(t *testing.T) { + p, err := parser.New(options.String(config.Config)) + if err != nil { + t.Fatalf(err.Error()) + } + result, err := p.SectionAttributesGet(parser.Backends, "default_backend", false) + if err != nil { + t.Fatalf("err should be nil %v", err) + } + if !slices.Contains(result, "option tcp-smart-connect") { + t.Fatalf("should contain registered attribute 'option tcp-smart-connect'") + } + }) + } +}