func (p *Parser) Set(scope string, sectionType parser.Section, sectionName string, attribute string, data common.ParserData, index ...int) error {
	p.lock()
	defer p.unLock()
	setIndex := -1
	if len(index) > 0 && index[0] > -1 {
		setIndex = index[0]
	}
	psrs, ok := p.Parsers[scope]
	if !ok {
		return errors.ErrScopeMissing
	}
	st, ok := psrs[sectionType]
	if !ok {
		return errors.ErrSectionMissing
	}
	section, ok := st[sectionName]
	if !ok {
		return errors.ErrSectionMissing
	}
	return section.Set(attribute, data, setIndex)
}
Set sets attribute from defaults section, can be nil to disable/remove
package spoe
import (
	"fmt"
	"io"
	"io/ioutil"
	"sort"
	"strings"
	"sync"
	"github.com/gofrs/flock"
	"github.com/google/renameio"
	parser "github.com/haproxytech/config-parser/v4"
	"github.com/haproxytech/config-parser/v4/common"
	"github.com/haproxytech/config-parser/v4/errors"
	"github.com/haproxytech/config-parser/v4/spoe/parsers"
	"github.com/haproxytech/config-parser/v4/spoe/types"
)
type Parser struct {
	Parsers map[string]map[parser.Section]map[string]*parser.Parsers
	mutex   *sync.Mutex
}
func (p *Parser) lock() {
	p.mutex.Lock()
}
func (p *Parser) unLock() {
	p.mutex.Unlock()
}
func (p *Parser) Get(scope string, sectionType parser.Section, sectionName string, attribute string, createIfNotExist ...bool) (common.ParserData, error) {
	p.lock()
	defer p.unLock()
	psrs, ok := p.Parsers[scope]
	if !ok {
		return nil, errors.ErrScopeMissing
	}
	st, ok := psrs[sectionType]
	if !ok {
		return nil, errors.ErrSectionMissing
	}
	section, ok := st[sectionName]
	if !ok {
		return nil, errors.ErrSectionMissing
	}
	createNew := false
	if len(createIfNotExist) > 0 && createIfNotExist[0] {
		createNew = true
	}
	return section.Get(attribute, createNew)
}
func (p *Parser) GetOne(scope string, sectionType parser.Section, sectionName string, attribute string, index ...int) (common.ParserData, error) {
	p.lock()
	defer p.unLock()
	setIndex := -1
	if len(index) > 0 && index[0] > -1 {
		setIndex = index[0]
	}
	psrs, ok := p.Parsers[scope]
	if !ok {
		return nil, errors.ErrScopeMissing
	}
	st, ok := psrs[sectionType]
	if !ok {
		return nil, errors.ErrSectionMissing
	}
	section, ok := st[sectionName]
	if !ok {
		return nil, errors.ErrSectionMissing
	}
	return section.GetOne(attribute, setIndex)
}
func (p *Parser) SectionsGet(scope string, sectionType parser.Section) ([]string, error) {
	p.lock()
	defer p.unLock()
	psrs, ok := p.Parsers[scope]
	if !ok {
		return nil, errors.ErrScopeMissing
	}
	st, ok := psrs[sectionType]
	if !ok {
		return nil, errors.ErrSectionMissing
	}
	result := make([]string, len(st))
	index := 0
	for sectionName := range st {
		result[index] = sectionName
		index++
	}
	return result, nil
}
func (p *Parser) ScopeDelete(scope string) error {
	p.lock()
	defer p.unLock()
	_, ok := p.Parsers[scope]
	if !ok {
		return errors.ErrScopeMissing
	}
	delete(p.Parsers, scope)
	return nil
}
func (p *Parser) SectionsDelete(scope string, sectionType parser.Section, sectionName string) error {
	p.lock()
	defer p.unLock()
	psrs, ok := p.Parsers[scope]
	if !ok {
		return errors.ErrScopeMissing
	}
	_, ok = psrs[sectionType]
	if !ok {
		return errors.ErrSectionMissing
	}
	delete(psrs[sectionType], sectionName)
	return nil
}
func (p *Parser) ScopeCreate(scope string) error {
	p.lock()
	defer p.unLock()
	_, ok := p.Parsers[scope]
	if ok {
		return errors.ErrScopeAlreadyExists
	}
	par := map[parser.Section]map[string]*parser.Parsers{}
	p.Parsers[scope] = par
	par[parser.Comments] = map[string]*parser.Parsers{
		parser.CommentsSectionName: getStartParser(),
	}
	par[parser.SPOEAgent] = map[string]*parser.Parsers{}
	par[parser.SPOEGroup] = map[string]*parser.Parsers{}
	par[parser.SPOEMessage] = map[string]*parser.Parsers{}
	return nil
}
func (p *Parser) SectionsCreate(scope string, sectionType parser.Section, sectionName string) error {
	p.lock()
	defer p.unLock()
	psrs, ok := p.Parsers[scope]
	if !ok {
		return errors.ErrScopeMissing
	}
	st, ok := psrs[sectionType]
	if !ok {
		return errors.ErrSectionMissing
	}
	_, ok = st[sectionName]
	if ok {
		return errors.ErrSectionAlreadyExists
	}
	parsers := parser.ConfiguredParsers{
		State:    "",
		Active:   p.Parsers[scope][parser.Comments][parser.CommentsSectionName],
		Comments: p.Parsers[scope][parser.Comments][parser.CommentsSectionName],
	}
	previousLine := []string{scope}
	parts := []string{string(sectionType), sectionName}
	comment := ""
	p.ProcessLine(fmt.Sprintf("%s %s", sectionType, sectionName), parts, previousLine, comment, parsers)
	return nil
}