From 18f71d8fc897591115043f7f35a57443c41bfcd8 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: Sat, 6 Apr 2019 02:38:32 +0300 Subject: [PATCH] Use config.Map for global config directives --- config/map.go | 92 +++++++++++++++++++++++++++------------------------ maddy.go | 29 ++++++---------- 2 files changed, 58 insertions(+), 63 deletions(-) diff --git a/config/map.go b/config/map.go index 11fdb37..1c0176f 100644 --- a/config/map.go +++ b/config/map.go @@ -13,15 +13,10 @@ type matcher struct { inheritGlobal bool defaultVal func() (interface{}, error) mapper func(*Map, *Node) (interface{}, error) - store reflect.Value + store *reflect.Value } -func (m *matcher) match(map_ *Map, node *Node) error { - val, err := m.mapper(map_, node) - if err != nil { - return err - } - +func (m *matcher) assign(val interface{}) { valRefl := reflect.ValueOf(val) // Convert untyped nil into typed nil. Otherwise it will panic. if !valRefl.IsValid() { @@ -29,7 +24,6 @@ func (m *matcher) match(map_ *Map, node *Node) error { } m.store.Set(valRefl) - return nil } // Map structure implements reflection-based conversion between configuration @@ -41,14 +35,19 @@ type Map struct { // called. curNode *Node + // All values saved by Map during processing. + Values map[string]interface{} + entries map[string]matcher - GlobalCfg map[string]Node - Block *Node + // Values used by Process as default values if inheritGlobal is true. + Globals map[string]interface{} + // Config block used by Process. + Block *Node } -func NewMap(globalCfg map[string]Node, block *Node) *Map { - return &Map{GlobalCfg: globalCfg, Block: block} +func NewMap(globals map[string]interface{}, block *Node) *Map { + return &Map{Globals: globals, Block: block} } // MatchErr returns error with formatted message, if called from defaultVal or @@ -307,27 +306,34 @@ func (m *Map) Float(name string, inheritGlobal, required bool, defaultVal float6 // mapper is a function that should convert configuration directive arguments // into variable value. Both functions may fail with errors, configuration // processing will stop immediately then. +// +// store is where the value returned by mapper should be stored. Can be nil +// (value will be saved only in Map.Values). func (m *Map) Custom(name string, inheritGlobal, required bool, defaultVal func() (interface{}, error), mapper func(*Map, *Node) (interface{}, error), store interface{}) { if m.entries == nil { m.entries = make(map[string]matcher) } - - val := reflect.ValueOf(store).Elem() - if !val.CanSet() { - panic("Map.Custom: store argument must be settable (a pointer)") - } - if _, ok := m.entries[name]; ok { panic("Map.Custom: duplicate matcher") } + var target *reflect.Value + ptr := reflect.ValueOf(store) + if ptr.IsValid() && !ptr.IsNil() { + val := ptr.Elem() + if !val.CanSet() { + panic("Map.Custom: store argument must be settable (a pointer)") + } + target = &val + } + m.entries[name] = matcher{ name: name, inheritGlobal: inheritGlobal, required: required, defaultVal: defaultVal, mapper: mapper, - store: val, + store: target, } } @@ -335,33 +341,39 @@ func (m *Map) Custom(name string, inheritGlobal, required bool, defaultVal func( // // If Map instance was not created using NewMap - Process panics. func (m *Map) Process() (unmatched []Node, err error) { - return m.ProcessWith(m.GlobalCfg, m.Block) + return m.ProcessWith(m.Globals, m.Block.Children) } -// Process maps variables from global configuration and block passedin arguments. -func (m *Map) ProcessWith(globalCfg map[string]Node, block *Node) (unmatched []Node, err error) { - unmatched = make([]Node, 0, len(block.Children)) +// Process maps variables from global configuration and block passed in arguments. +func (m *Map) ProcessWith(globalCfg map[string]interface{}, block []Node) (unmatched []Node, err error) { + unmatched = make([]Node, 0, len(block)) matched := make(map[string]bool) + m.Values = make(map[string]interface{}) - for _, subnode := range block.Children { + for _, subnode := range block { m.curNode = &subnode if matched[subnode.Name] { - return nil, m.MatchErr("duplicate directive") + return nil, m.MatchErr("duplicate directive: %s", subnode.Name) } matcher, ok := m.entries[subnode.Name] if !ok { if !m.allowUnknown { - return nil, m.MatchErr("unexpected directive") + return nil, m.MatchErr("unexpected directive: %s", subnode.Name) } unmatched = append(unmatched, subnode) continue } - if err := matcher.match(m, m.curNode); err != nil { + val, err := matcher.mapper(m, m.curNode) + if err != nil { return nil, err } + m.Values[matcher.name] = val + if matcher.store != nil { + matcher.assign(val) + } matched[subnode.Name] = true } m.curNode = nil @@ -371,38 +383,30 @@ func (m *Map) ProcessWith(globalCfg map[string]Node, block *Node) (unmatched []N continue } - globalNode, ok := globalCfg[matcher.name] + var val interface{} + globalVal, ok := globalCfg[matcher.name] if matcher.inheritGlobal && ok { - m.curNode = &globalNode - if err := matcher.match(m, m.curNode); err != nil { - m.curNode = nil - return nil, err - } - m.curNode = nil + val = globalVal } else if !matcher.required { if matcher.defaultVal == nil { continue } - val, err := matcher.defaultVal() + val, err = matcher.defaultVal() if err != nil { return nil, err } if val == nil { return nil, m.MatchErr("missing required directive: %s", matcher.name) } - - valRefl := reflect.ValueOf(val) - // Convert untyped nil into typed nil. Otherwise it will panic. - if !valRefl.IsValid() { - valRefl = reflect.Zero(matcher.store.Type()) - } - - matcher.store.Set(valRefl) - continue } else { return nil, m.MatchErr("missing required directive: %s", matcher.name) } + + m.Values[matcher.name] = val + if matcher.store != nil { + matcher.assign(val) + } } return unmatched, nil diff --git a/maddy.go b/maddy.go index 293e57a..71d04ca 100644 --- a/maddy.go +++ b/maddy.go @@ -19,26 +19,17 @@ type modInfo struct { func Start(cfg []config.Node) error { instances := make(map[string]modInfo) - globalCfg := make(map[string]config.Node) - - for _, block := range cfg { - switch block.Name { - case "tls", "hostname", "debug": - globalCfg[block.Name] = block - continue - } + globals := config.NewMap(nil, &config.Node{Children: cfg}) + globals.String("hostname", false, false, "", nil) + globals.Custom("tls", false, true, nil, tlsDirective, nil) + globals.Bool("debug", false, &log.DefaultLogger.Debug) + globals.AllowUnknown() + unmatched, err := globals.Process() + if err != nil { + return err } - if _, ok := globalCfg["debug"]; ok { - log.DefaultLogger.Debug = true - } - - for _, block := range cfg { - switch block.Name { - case "hostname", "tls", "debug": - continue - } - + for _, block := range unmatched { var instName string if len(block.Args) == 0 { instName = block.Name @@ -72,7 +63,7 @@ func Start(cfg []config.Node) error { for _, inst := range instances { log.Debugln("module init", inst.instance.Name(), inst.instance.InstanceName()) - if err := inst.instance.Init(config.NewMap(globalCfg, &inst.cfg)); err != nil { + if err := inst.instance.Init(config.NewMap(globals.Values, &inst.cfg)); err != nil { return err } }