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