1// Copyright (c) 2020 Denis Tingajkin 2// 3// SPDX-License-Identifier: Apache-2.0 4// 5// Licensed under the Apache License, Version 2.0 (the "License"); 6// you may not use this file except in compliance with the License. 7// You may obtain a copy of the License at: 8// 9// http://www.apache.org/licenses/LICENSE-2.0 10// 11// Unless required by applicable law or agreed to in writing, software 12// distributed under the License is distributed on an "AS IS" BASIS, 13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14// See the License for the specific language governing permissions and 15// limitations under the License. 16 17package goheader 18 19import ( 20 "errors" 21 "fmt" 22 "regexp" 23 "strings" 24) 25 26type Calculable interface { 27 Calculate(map[string]Value) error 28 Get() string 29} 30 31type Value interface { 32 Calculable 33 Read(*Reader) Issue 34} 35 36func calculateValue(calculable Calculable, values map[string]Value) (string, error) { 37 sb := strings.Builder{} 38 r := calculable.Get() 39 var endIndex int 40 var startIndex int 41 for startIndex = strings.Index(r, "{{"); startIndex >= 0; startIndex = strings.Index(r, "{{") { 42 _, _ = sb.WriteString(r[:startIndex]) 43 endIndex = strings.Index(r, "}}") 44 if endIndex < 0 { 45 return "", errors.New("missed value ending") 46 } 47 subVal := strings.ToLower(strings.TrimSpace(r[startIndex+2 : endIndex])) 48 if val := values[subVal]; val != nil { 49 if err := val.Calculate(values); err != nil { 50 return "", err 51 } 52 sb.WriteString(val.Get()) 53 } else { 54 return "", fmt.Errorf("unknown value name %v", subVal) 55 } 56 endIndex += 2 57 r = r[endIndex:] 58 } 59 _, _ = sb.WriteString(r) 60 return sb.String(), nil 61} 62 63type ConstValue struct { 64 RawValue string 65} 66 67func (c *ConstValue) Calculate(values map[string]Value) error { 68 v, err := calculateValue(c, values) 69 if err != nil { 70 return err 71 } 72 c.RawValue = v 73 return nil 74} 75 76func (c *ConstValue) Get() string { 77 return c.RawValue 78} 79 80func (c *ConstValue) Read(s *Reader) Issue { 81 l := s.Location() 82 p := s.Position() 83 for _, ch := range c.Get() { 84 if ch != s.Peek() { 85 s.SetPosition(p) 86 f := s.ReadWhile(func(r rune) bool { 87 return r != '\n' 88 }) 89 return NewIssueWithLocation(fmt.Sprintf("Expected:%v, Actual: %v", c.Get(), f), l) 90 } 91 s.Next() 92 } 93 return nil 94} 95 96type RegexpValue struct { 97 RawValue string 98} 99 100func (r *RegexpValue) Calculate(values map[string]Value) error { 101 v, err := calculateValue(r, values) 102 if err != nil { 103 return err 104 } 105 r.RawValue = v 106 return nil 107} 108 109func (r *RegexpValue) Get() string { 110 return r.RawValue 111} 112 113func (r *RegexpValue) Read(s *Reader) Issue { 114 l := s.Location() 115 p := regexp.MustCompile(r.Get()) 116 pos := s.Position() 117 str := s.Finish() 118 s.SetPosition(pos) 119 indexes := p.FindAllIndex([]byte(str), -1) 120 if len(indexes) == 0 { 121 return NewIssueWithLocation(fmt.Sprintf("Pattern %v doesn't match.", p.String()), l) 122 } 123 s.SetPosition(pos + indexes[0][1]) 124 return nil 125} 126 127var _ Value = &ConstValue{} 128var _ Value = &RegexpValue{} 129