| package ini |
| |
| import ( |
| "fmt" |
| "sort" |
| ) |
| |
| // Visitor is an interface used by walkers that will |
| // traverse an array of ASTs. |
| type Visitor interface { |
| VisitExpr(AST) error |
| VisitStatement(AST) error |
| } |
| |
| // DefaultVisitor is used to visit statements and expressions |
| // and ensure that they are both of the correct format. |
| // In addition, upon visiting this will build sections and populate |
| // the Sections field which can be used to retrieve profile |
| // configuration. |
| type DefaultVisitor struct { |
| scope string |
| Sections Sections |
| } |
| |
| // NewDefaultVisitor return a DefaultVisitor |
| func NewDefaultVisitor() *DefaultVisitor { |
| return &DefaultVisitor{ |
| Sections: Sections{ |
| container: map[string]Section{}, |
| }, |
| } |
| } |
| |
| // VisitExpr visits expressions... |
| func (v *DefaultVisitor) VisitExpr(expr AST) error { |
| t := v.Sections.container[v.scope] |
| if t.values == nil { |
| t.values = values{} |
| } |
| |
| switch expr.Kind { |
| case ASTKindExprStatement: |
| opExpr := expr.GetRoot() |
| switch opExpr.Kind { |
| case ASTKindEqualExpr: |
| children := opExpr.GetChildren() |
| if len(children) <= 1 { |
| return NewParseError("unexpected token type") |
| } |
| |
| rhs := children[1] |
| |
| if rhs.Root.Type() != TokenLit { |
| return NewParseError("unexpected token type") |
| } |
| |
| key := EqualExprKey(opExpr) |
| v, err := newValue(rhs.Root.ValueType, rhs.Root.base, rhs.Root.Raw()) |
| if err != nil { |
| return err |
| } |
| |
| t.values[key] = v |
| default: |
| return NewParseError(fmt.Sprintf("unsupported expression %v", expr)) |
| } |
| default: |
| return NewParseError(fmt.Sprintf("unsupported expression %v", expr)) |
| } |
| |
| v.Sections.container[v.scope] = t |
| return nil |
| } |
| |
| // VisitStatement visits statements... |
| func (v *DefaultVisitor) VisitStatement(stmt AST) error { |
| switch stmt.Kind { |
| case ASTKindCompletedSectionStatement: |
| child := stmt.GetRoot() |
| if child.Kind != ASTKindSectionStatement { |
| return NewParseError(fmt.Sprintf("unsupported child statement: %T", child)) |
| } |
| |
| name := string(child.Root.Raw()) |
| v.Sections.container[name] = Section{} |
| v.scope = name |
| default: |
| return NewParseError(fmt.Sprintf("unsupported statement: %s", stmt.Kind)) |
| } |
| |
| return nil |
| } |
| |
| // Sections is a map of Section structures that represent |
| // a configuration. |
| type Sections struct { |
| container map[string]Section |
| } |
| |
| // GetSection will return section p. If section p does not exist, |
| // false will be returned in the second parameter. |
| func (t Sections) GetSection(p string) (Section, bool) { |
| v, ok := t.container[p] |
| return v, ok |
| } |
| |
| // values represents a map of union values. |
| type values map[string]Value |
| |
| // List will return a list of all sections that were successfully |
| // parsed. |
| func (t Sections) List() []string { |
| keys := make([]string, len(t.container)) |
| i := 0 |
| for k := range t.container { |
| keys[i] = k |
| i++ |
| } |
| |
| sort.Strings(keys) |
| return keys |
| } |
| |
| // Section contains a name and values. This represent |
| // a sectioned entry in a configuration file. |
| type Section struct { |
| Name string |
| values values |
| } |
| |
| // Has will return whether or not an entry exists in a given section |
| func (t Section) Has(k string) bool { |
| _, ok := t.values[k] |
| return ok |
| } |
| |
| // ValueType will returned what type the union is set to. If |
| // k was not found, the NoneType will be returned. |
| func (t Section) ValueType(k string) (ValueType, bool) { |
| v, ok := t.values[k] |
| return v.Type, ok |
| } |
| |
| // Bool returns a bool value at k |
| func (t Section) Bool(k string) bool { |
| return t.values[k].BoolValue() |
| } |
| |
| // Int returns an integer value at k |
| func (t Section) Int(k string) int64 { |
| return t.values[k].IntValue() |
| } |
| |
| // Float64 returns a float value at k |
| func (t Section) Float64(k string) float64 { |
| return t.values[k].FloatValue() |
| } |
| |
| // String returns the string value at k |
| func (t Section) String(k string) string { |
| _, ok := t.values[k] |
| if !ok { |
| return "" |
| } |
| return t.values[k].StringValue() |
| } |