aboutsummaryrefslogtreecommitdiff
path: root/vendor/golang.org/x/text/secure/precis/options.go
blob: 26143db759e150913e2ca5f37bbffd8bf1b83147 (plain) (blame)
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
153
154
155
156
157
// Copyright 2015 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 precis

import (
	"golang.org/x/text/cases"
	"golang.org/x/text/language"
	"golang.org/x/text/runes"
	"golang.org/x/text/transform"
	"golang.org/x/text/unicode/norm"
)

// An Option is used to define the behavior and rules of a Profile.
type Option func(*options)

type options struct {
	// Preparation options
	foldWidth bool

	// Enforcement options
	asciiLower    bool
	cases         transform.SpanningTransformer
	disallow      runes.Set
	norm          transform.SpanningTransformer
	additional    []func() transform.SpanningTransformer
	width         transform.SpanningTransformer
	disallowEmpty bool
	bidiRule      bool
	repeat        bool

	// Comparison options
	ignorecase bool
}

func getOpts(o ...Option) (res options) {
	for _, f := range o {
		f(&res)
	}
	// Using a SpanningTransformer, instead of norm.Form prevents an allocation
	// down the road.
	if res.norm == nil {
		res.norm = norm.NFC
	}
	return
}

var (
	// The IgnoreCase option causes the profile to perform a case insensitive
	// comparison during the PRECIS comparison step.
	IgnoreCase Option = ignoreCase

	// The FoldWidth option causes the profile to map non-canonical wide and
	// narrow variants to their decomposition mapping. This is useful for
	// profiles that are based on the identifier class which would otherwise
	// disallow such characters.
	FoldWidth Option = foldWidth

	// The DisallowEmpty option causes the enforcement step to return an error if
	// the resulting string would be empty.
	DisallowEmpty Option = disallowEmpty

	// The BidiRule option causes the Bidi Rule defined in RFC 5893 to be
	// applied.
	BidiRule Option = bidiRule
)

var (
	ignoreCase = func(o *options) {
		o.ignorecase = true
	}
	foldWidth = func(o *options) {
		o.foldWidth = true
	}
	disallowEmpty = func(o *options) {
		o.disallowEmpty = true
	}
	bidiRule = func(o *options) {
		o.bidiRule = true
	}
	repeat = func(o *options) {
		o.repeat = true
	}
)

// TODO: move this logic to package transform

type spanWrap struct{ transform.Transformer }

func (s spanWrap) Span(src []byte, atEOF bool) (n int, err error) {
	return 0, transform.ErrEndOfSpan
}

// TODO: allow different types? For instance:
//     func() transform.Transformer
//     func() transform.SpanningTransformer
//     func([]byte) bool  // validation only
//
// Also, would be great if we could detect if a transformer is reentrant.

// The AdditionalMapping option defines the additional mapping rule for the
// Profile by applying Transformer's in sequence.
func AdditionalMapping(t ...func() transform.Transformer) Option {
	return func(o *options) {
		for _, f := range t {
			sf := func() transform.SpanningTransformer {
				return f().(transform.SpanningTransformer)
			}
			if _, ok := f().(transform.SpanningTransformer); !ok {
				sf = func() transform.SpanningTransformer {
					return spanWrap{f()}
				}
			}
			o.additional = append(o.additional, sf)
		}
	}
}

// The Norm option defines a Profile's normalization rule. Defaults to NFC.
func Norm(f norm.Form) Option {
	return func(o *options) {
		o.norm = f
	}
}

// The FoldCase option defines a Profile's case mapping rule. Options can be
// provided to determine the type of case folding used.
func FoldCase(opts ...cases.Option) Option {
	return func(o *options) {
		o.asciiLower = true
		o.cases = cases.Fold(opts...)
	}
}

// The LowerCase option defines a Profile's case mapping rule. Options can be
// provided to determine the type of case folding used.
func LowerCase(opts ...cases.Option) Option {
	return func(o *options) {
		o.asciiLower = true
		if len(opts) == 0 {
			o.cases = cases.Lower(language.Und, cases.HandleFinalSigma(false))
			return
		}

		opts = append([]cases.Option{cases.HandleFinalSigma(false)}, opts...)
		o.cases = cases.Lower(language.Und, opts...)
	}
}

// The Disallow option further restricts a Profile's allowed characters beyond
// what is disallowed by the underlying string class.
func Disallow(set runes.Set) Option {
	return func(o *options) {
		o.disallow = set
	}
}