golang.org/x/text@v0.14.0/currency/query.go (about)

     1  // Copyright 2016 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package currency
     6  
     7  import (
     8  	"sort"
     9  	"time"
    10  
    11  	"golang.org/x/text/language"
    12  )
    13  
    14  // QueryIter represents a set of Units. The default set includes all Units that
    15  // are currently in use as legal tender in any Region.
    16  type QueryIter interface {
    17  	// Next returns true if there is a next element available.
    18  	// It must be called before any of the other methods are called.
    19  	Next() bool
    20  
    21  	// Unit returns the unit of the current iteration.
    22  	Unit() Unit
    23  
    24  	// Region returns the Region for the current iteration.
    25  	Region() language.Region
    26  
    27  	// From returns the date from which the unit was used in the region.
    28  	// It returns false if this date is unknown.
    29  	From() (time.Time, bool)
    30  
    31  	// To returns the date up till which the unit was used in the region.
    32  	// It returns false if this date is unknown or if the unit is still in use.
    33  	To() (time.Time, bool)
    34  
    35  	// IsTender reports whether the unit is a legal tender in the region during
    36  	// the specified date range.
    37  	IsTender() bool
    38  }
    39  
    40  // Query represents a set of Units. The default set includes all Units that are
    41  // currently in use as legal tender in any Region.
    42  func Query(options ...QueryOption) QueryIter {
    43  	it := &iter{
    44  		end:  len(regionData),
    45  		date: 0xFFFFFFFF,
    46  	}
    47  	for _, fn := range options {
    48  		fn(it)
    49  	}
    50  	return it
    51  }
    52  
    53  // NonTender returns a new query that also includes matching Units that are not
    54  // legal tender.
    55  var NonTender QueryOption = nonTender
    56  
    57  func nonTender(i *iter) {
    58  	i.nonTender = true
    59  }
    60  
    61  // Historical selects the units for all dates.
    62  var Historical QueryOption = historical
    63  
    64  func historical(i *iter) {
    65  	i.date = hist
    66  }
    67  
    68  // A QueryOption can be used to change the set of unit information returned by
    69  // a query.
    70  type QueryOption func(*iter)
    71  
    72  // Date queries the units that were in use at the given point in history.
    73  func Date(t time.Time) QueryOption {
    74  	d := toDate(t)
    75  	return func(i *iter) {
    76  		i.date = d
    77  	}
    78  }
    79  
    80  // Region limits the query to only return entries for the given region.
    81  func Region(r language.Region) QueryOption {
    82  	p, end := len(regionData), len(regionData)
    83  	x := regionToCode(r)
    84  	i := sort.Search(len(regionData), func(i int) bool {
    85  		return regionData[i].region >= x
    86  	})
    87  	if i < len(regionData) && regionData[i].region == x {
    88  		p = i
    89  		for i++; i < len(regionData) && regionData[i].region == x; i++ {
    90  		}
    91  		end = i
    92  	}
    93  	return func(i *iter) {
    94  		i.p, i.end = p, end
    95  	}
    96  }
    97  
    98  const (
    99  	hist = 0x00
   100  	now  = 0xFFFFFFFF
   101  )
   102  
   103  type iter struct {
   104  	*regionInfo
   105  	p, end    int
   106  	date      uint32
   107  	nonTender bool
   108  }
   109  
   110  func (i *iter) Next() bool {
   111  	for ; i.p < i.end; i.p++ {
   112  		i.regionInfo = &regionData[i.p]
   113  		if !i.nonTender && !i.IsTender() {
   114  			continue
   115  		}
   116  		if i.date == hist || (i.from <= i.date && (i.to == 0 || i.date <= i.to)) {
   117  			i.p++
   118  			return true
   119  		}
   120  	}
   121  	return false
   122  }
   123  
   124  func (r *regionInfo) Region() language.Region {
   125  	// TODO: this could be much faster.
   126  	var buf [2]byte
   127  	buf[0] = uint8(r.region >> 8)
   128  	buf[1] = uint8(r.region)
   129  	return language.MustParseRegion(string(buf[:]))
   130  }
   131  
   132  func (r *regionInfo) Unit() Unit {
   133  	return Unit{r.code &^ nonTenderBit}
   134  }
   135  
   136  func (r *regionInfo) IsTender() bool {
   137  	return r.code&nonTenderBit == 0
   138  }
   139  
   140  func (r *regionInfo) From() (time.Time, bool) {
   141  	if r.from == 0 {
   142  		return time.Time{}, false
   143  	}
   144  	return fromDate(r.from), true
   145  }
   146  
   147  func (r *regionInfo) To() (time.Time, bool) {
   148  	if r.to == 0 {
   149  		return time.Time{}, false
   150  	}
   151  	return fromDate(r.to), true
   152  }