1// Copyright 2015 go-swagger maintainers
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//    http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package strfmt
16
17import (
18	"encoding/json"
19	"fmt"
20	"regexp"
21	"strconv"
22	"strings"
23	"time"
24)
25
26func init() {
27	d := Duration(0)
28	// register this format in the default registry
29	Default.Add("duration", &d, IsDuration)
30}
31
32var (
33	timeUnits = [][]string{
34		{"ns", "nano"},
35		{"us", "µs", "micro"},
36		{"ms", "milli"},
37		{"s", "sec"},
38		{"m", "min"},
39		{"h", "hr", "hour"},
40		{"d", "day"},
41		{"w", "wk", "week"},
42	}
43
44	timeMultiplier = map[string]time.Duration{
45		"ns": time.Nanosecond,
46		"us": time.Microsecond,
47		"ms": time.Millisecond,
48		"s":  time.Second,
49		"m":  time.Minute,
50		"h":  time.Hour,
51		"d":  24 * time.Hour,
52		"w":  7 * 24 * time.Hour,
53	}
54
55	durationMatcher = regexp.MustCompile(`((\d+)\s*([A-Za-zµ]+))`)
56)
57
58// IsDuration returns true if the provided string is a valid duration
59func IsDuration(str string) bool {
60	_, err := ParseDuration(str)
61	return err == nil
62}
63
64// Duration represents a duration
65//
66// Duration stores a period of time as a nanosecond count, with the largest
67// repesentable duration being approximately 290 years.
68//
69// swagger:strfmt duration
70type Duration time.Duration
71
72// MarshalText turns this instance into text
73func (d Duration) MarshalText() ([]byte, error) {
74	return []byte(time.Duration(d).String()), nil
75}
76
77// UnmarshalText hydrates this instance from text
78func (d *Duration) UnmarshalText(data []byte) error { // validation is performed later on
79	dd, err := ParseDuration(string(data))
80	if err != nil {
81		return err
82	}
83	*d = Duration(dd)
84	return nil
85}
86
87// ParseDuration parses a duration from a string, compatible with scala duration syntax
88func ParseDuration(cand string) (time.Duration, error) {
89	if dur, err := time.ParseDuration(cand); err == nil {
90		return dur, nil
91	}
92
93	var dur time.Duration
94	ok := false
95	for _, match := range durationMatcher.FindAllStringSubmatch(cand, -1) {
96
97		factor, err := strconv.Atoi(match[2]) // converts string to int
98		if err != nil {
99			return 0, err
100		}
101		unit := strings.ToLower(strings.TrimSpace(match[3]))
102
103		for _, variants := range timeUnits {
104			last := len(variants) - 1
105			multiplier := timeMultiplier[variants[0]]
106
107			for i, variant := range variants {
108				if (last == i && strings.HasPrefix(unit, variant)) || strings.EqualFold(variant, unit) {
109					ok = true
110					dur += time.Duration(factor) * multiplier
111				}
112			}
113		}
114	}
115
116	if ok {
117		return dur, nil
118	}
119	return 0, fmt.Errorf("unable to parse %s as duration", cand)
120}
121
122// Scan reads a Duration value from database driver type.
123func (d *Duration) Scan(raw interface{}) error {
124	switch v := raw.(type) {
125	// TODO: case []byte: // ?
126	case int64:
127		*d = Duration(v)
128	case float64:
129		*d = Duration(int64(v))
130	case nil:
131		*d = Duration(0)
132	default:
133		return fmt.Errorf("cannot sql.Scan() strfmt.Duration from: %#v", v)
134	}
135
136	return nil
137}
138
139// String converts this duration to a string
140func (d Duration) String() string {
141	return time.Duration(d).String()
142}
143
144// MarshalJSON returns the Duration as JSON
145func (d Duration) MarshalJSON() ([]byte, error) {
146	return json.Marshal(time.Duration(d).String())
147}
148
149// UnmarshalJSON sets the Duration from JSON
150func (d *Duration) UnmarshalJSON(data []byte) error {
151	if string(data) == jsonNull {
152		return nil
153	}
154
155	var dstr string
156	if err := json.Unmarshal(data, &dstr); err != nil {
157		return err
158	}
159	tt, err := ParseDuration(dstr)
160	if err != nil {
161		return err
162	}
163	*d = Duration(tt)
164	return nil
165}
166
167// DeepCopyInto copies the receiver and writes its value into out.
168func (d *Duration) DeepCopyInto(out *Duration) {
169	*out = *d
170}
171
172// DeepCopy copies the receiver into a new Duration.
173func (d *Duration) DeepCopy() *Duration {
174	if d == nil {
175		return nil
176	}
177	out := new(Duration)
178	d.DeepCopyInto(out)
179	return out
180}
181