|  | // 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 = ®ionData[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 | 
|  | } |