// 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 }