summaryrefslogtreecommitdiff
path: root/vendor/golang.org/x/text/currency/query.go
blob: 7bf9430a62cce1678a1673d4cbb4f4d0cd0fb84c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package currency

import (
	"sort"
	"time"

	"golang.org/x/text/language"
)

// QueryIter represents a set of Units. The default set includes all Units that
// are currently in use as legal tender in any Region.
type QueryIter interface {
	// Next returns true if there is a next element available.
	// It must be called before any of the other methods are called.
	Next() bool

	// Unit returns the unit of the current iteration.
	Unit() Unit

	// Region returns the Region for the current iteration.
	Region() language.Region

	// From returns the date from which the unit was used in the region.
	// It returns false if this date is unknown.
	From() (time.Time, bool)

	// To returns the date up till which the unit was used in the region.
	// It returns false if this date is unknown or if the unit is still in use.
	To() (time.Time, bool)

	// IsTender reports whether the unit is a legal tender in the region during
	// the specified date range.
	IsTender() bool
}

// Query represents a set of Units. The default set includes all Units that are
// currently in use as legal tender in any Region.
func Query(options ...QueryOption) QueryIter {
	it := &iter{
		end:  len(regionData),
		date: 0xFFFFFFFF,
	}
	for _, fn := range options {
		fn(it)
	}
	return it
}

// NonTender returns a new query that also includes matching Units that are not
// legal tender.
var NonTender QueryOption = nonTender

func nonTender(i *iter) {
	i.nonTender = true
}

// Historical selects the units for all dates.
var Historical QueryOption = historical

func historical(i *iter) {
	i.date = hist
}

// A QueryOption can be used to change the set of unit information returned by
// a query.
type QueryOption func(*iter)

// Date queries the units that were in use at the given point in history.
func Date(t time.Time) QueryOption {
	d := toDate(t)
	return func(i *iter) {
		i.date = d
	}
}

// Region limits the query to only return entries for the given region.
func Region(r language.Region) QueryOption {
	p, end := len(regionData), len(regionData)
	x := regionToCode(r)
	i := sort.Search(len(regionData), func(i int) bool {
		return regionData[i].region >= x
	})
	if i < len(regionData) && regionData[i].region == x {
		p = i
		for i++; i < len(regionData) && regionData[i].region == x; i++ {
		}
		end = i
	}
	return func(i *iter) {
		i.p, i.end = p, end
	}
}

const (
	hist = 0x00
	now  = 0xFFFFFFFF
)

type iter struct {
	*regionInfo
	p, end    int
	date      uint32
	nonTender bool
}

func (i *iter) Next() bool {
	for ; i.p < i.end; i.p++ {
		i.regionInfo = &regionData[i.p]
		if !i.nonTender && !i.IsTender() {
			continue
		}
		if i.date == hist || (i.from <= i.date && (i.to == 0 || i.date <= i.to)) {
			i.p++
			return true
		}
	}
	return false
}

func (r *regionInfo) Region() language.Region {
	// TODO: this could be much faster.
	var buf [2]byte
	buf[0] = uint8(r.region >> 8)
	buf[1] = uint8(r.region)
	return language.MustParseRegion(string(buf[:]))
}

func (r *regionInfo) Unit() Unit {
	return Unit{r.code &^ nonTenderBit}
}

func (r *regionInfo) IsTender() bool {
	return r.code&nonTenderBit == 0
}

func (r *regionInfo) From() (time.Time, bool) {
	if r.from == 0 {
		return time.Time{}, false
	}
	return fromDate(r.from), true
}

func (r *regionInfo) To() (time.Time, bool) {
	if r.to == 0 {
		return time.Time{}, false
	}
	return fromDate(r.to), true
}