1package blocksconvert
2
3import (
4	"compress/gzip"
5	"fmt"
6	"io"
7	"regexp"
8	"strconv"
9	"strings"
10	"time"
11
12	"github.com/golang/snappy"
13)
14
15// Plan file describes which series must be included in a block for given user and day.
16// It consists of JSON objects, each written on its own line.
17// Plan file starts with single header, many plan entries and single footer.
18
19type PlanEntry struct {
20	// Header
21	User     string `json:"user,omitempty"`
22	DayIndex int    `json:"day_index,omitempty"`
23
24	// Entries
25	SeriesID string   `json:"sid,omitempty"`
26	Chunks   []string `json:"cs,omitempty"`
27
28	// Footer
29	Complete bool `json:"complete,omitempty"`
30}
31
32func (pe *PlanEntry) Reset() {
33	*pe = PlanEntry{}
34}
35
36// Returns true and "base name" or false and empty string.
37func IsPlanFilename(name string) (bool, string) {
38	switch {
39	case strings.HasSuffix(name, ".plan.gz"):
40		return true, name[:len(name)-len(".plan.gz")]
41
42	case strings.HasSuffix(name, ".plan.snappy"):
43		return true, name[:len(name)-len(".plan.snappy")]
44
45	case strings.HasSuffix(name, ".plan"):
46		return true, name[:len(name)-len(".plan")]
47	}
48
49	return false, ""
50}
51
52func PreparePlanFileReader(planFile string, in io.Reader) (io.Reader, error) {
53	switch {
54	case strings.HasSuffix(planFile, ".snappy"):
55		return snappy.NewReader(in), nil
56
57	case strings.HasSuffix(planFile, ".gz"):
58		return gzip.NewReader(in)
59	}
60
61	return in, nil
62}
63
64func StartingFilename(planBaseName string, t time.Time) string {
65	return fmt.Sprintf("%s.starting.%d", planBaseName, t.Unix())
66}
67
68func ProgressFilename(planBaseName string, t time.Time) string {
69	return fmt.Sprintf("%s.inprogress.%d", planBaseName, t.Unix())
70}
71
72var progress = regexp.MustCompile(`^(.+)\.(starting|progress|inprogress)\.(\d+)$`)
73
74func IsProgressFilename(name string) (bool, string, time.Time) {
75	m := progress.FindStringSubmatch(name)
76	if len(m) == 0 {
77		return false, "", time.Time{}
78	}
79
80	ts, err := strconv.ParseInt(m[3], 10, 64)
81	if err != nil {
82		return false, "", time.Time{}
83	}
84
85	return true, m[1], time.Unix(ts, 0)
86}
87
88func FinishedFilename(planBaseName string, id string) string {
89	return fmt.Sprintf("%s.finished.%s", planBaseName, id)
90}
91
92var finished = regexp.MustCompile(`^(.+)\.finished\.([a-zA-Z0-9]+)$`)
93
94func IsFinishedFilename(name string) (bool, string, string) {
95	m := finished.FindStringSubmatch(name)
96	if len(m) == 0 {
97		return false, "", ""
98	}
99
100	return true, m[1], m[2]
101}
102
103func ErrorFilename(planBaseName string) string {
104	return planBaseName + ".error"
105}
106
107func IsErrorFilename(name string) (bool, string) {
108	if strings.HasSuffix(name, ".error") {
109		return true, name[:len(name)-len(".error")]
110	}
111	return false, ""
112}
113