1package ini
  2
  3import (
  4	"fmt"
  5	"strings"
  6)
  7
  8func parse(tokens []lineToken, path string) Sections {
  9	parser := &parser{
 10		path:     path,
 11		sections: NewSections(),
 12	}
 13	parser.parse(tokens)
 14	return parser.sections
 15}
 16
 17type parser struct {
 18	csection, ckey string   // current state
 19	path           string   // source file path
 20	sections       Sections // parse result
 21}
 22
 23func (p *parser) parse(tokens []lineToken) {
 24	for _, otok := range tokens {
 25		switch tok := otok.(type) {
 26		case *lineTokenProfile:
 27			p.handleProfile(tok)
 28		case *lineTokenProperty:
 29			p.handleProperty(tok)
 30		case *lineTokenSubProperty:
 31			p.handleSubProperty(tok)
 32		case *lineTokenContinuation:
 33			p.handleContinuation(tok)
 34		}
 35	}
 36}
 37
 38func (p *parser) handleProfile(tok *lineTokenProfile) {
 39	name := tok.Name
 40	if tok.Type != "" {
 41		name = fmt.Sprintf("%s %s", tok.Type, tok.Name)
 42	}
 43	p.ckey = ""
 44	p.csection = name
 45	if _, ok := p.sections.container[name]; !ok {
 46		p.sections.container[name] = NewSection(name)
 47	}
 48}
 49
 50func (p *parser) handleProperty(tok *lineTokenProperty) {
 51	if p.csection == "" {
 52		return // LEGACY: don't error on "global" properties
 53	}
 54
 55	p.ckey = tok.Key
 56	if _, ok := p.sections.container[p.csection].values[tok.Key]; ok {
 57		section := p.sections.container[p.csection]
 58		section.Logs = append(p.sections.container[p.csection].Logs,
 59			fmt.Sprintf(
 60				"For profile: %v, overriding %v value, with a %v value found in a duplicate profile defined later in the same file %v. \n",
 61				p.csection, tok.Key, tok.Key, p.path,
 62			),
 63		)
 64		p.sections.container[p.csection] = section
 65	}
 66
 67	p.sections.container[p.csection].values[tok.Key] = Value{
 68		str: tok.Value,
 69	}
 70	p.sections.container[p.csection].SourceFile[tok.Key] = p.path
 71}
 72
 73func (p *parser) handleSubProperty(tok *lineTokenSubProperty) {
 74	if p.csection == "" {
 75		return // LEGACY: don't error on "global" properties
 76	}
 77
 78	if p.ckey == "" || p.sections.container[p.csection].values[p.ckey].str != "" {
 79		// This is an "orphaned" subproperty, either because it's at
 80		// the beginning of a section or because the last property's
 81		// value isn't empty. Either way we're lenient here and
 82		// "promote" this to a normal property.
 83		p.handleProperty(&lineTokenProperty{
 84			Key:   tok.Key,
 85			Value: strings.TrimSpace(trimPropertyComment(tok.Value)),
 86		})
 87		return
 88	}
 89
 90	if p.sections.container[p.csection].values[p.ckey].mp == nil {
 91		p.sections.container[p.csection].values[p.ckey] = Value{
 92			mp: map[string]string{},
 93		}
 94	}
 95	p.sections.container[p.csection].values[p.ckey].mp[tok.Key] = tok.Value
 96}
 97
 98func (p *parser) handleContinuation(tok *lineTokenContinuation) {
 99	if p.ckey == "" {
100		return
101	}
102
103	value, _ := p.sections.container[p.csection].values[p.ckey]
104	if value.str != "" && value.mp == nil {
105		value.str = fmt.Sprintf("%s\n%s", value.str, tok.Value)
106	}
107
108	p.sections.container[p.csection].values[p.ckey] = value
109}