blob: 5a73be9e548d73aa4c3ad2d8499e68863fb41b65 [file] [log] [blame]
Don Newton379ae252019-04-01 12:17:06 -04001package stringprep
2
3import (
4 "golang.org/x/text/unicode/norm"
5)
6
7// Profile represents a stringprep profile.
8type Profile struct {
9 Mappings []Mapping
10 Normalize bool
11 Prohibits []Set
12 CheckBiDi bool
13}
14
15var errProhibited = "prohibited character"
16
17// Prepare transforms an input string to an output string following
18// the rules defined in the profile as defined by RFC-3454.
19func (p Profile) Prepare(s string) (string, error) {
20 // Optimistically, assume output will be same length as input
21 temp := make([]rune, 0, len(s))
22
23 // Apply maps
24 for _, r := range s {
25 rs, ok := p.applyMaps(r)
26 if ok {
27 temp = append(temp, rs...)
28 } else {
29 temp = append(temp, r)
30 }
31 }
32
33 // Normalize
34 var out string
35 if p.Normalize {
36 out = norm.NFKC.String(string(temp))
37 } else {
38 out = string(temp)
39 }
40
41 // Check prohibited
42 for _, r := range out {
43 if p.runeIsProhibited(r) {
44 return "", Error{Msg: errProhibited, Rune: r}
45 }
46 }
47
48 // Check BiDi allowed
49 if p.CheckBiDi {
50 if err := passesBiDiRules(out); err != nil {
51 return "", err
52 }
53 }
54
55 return out, nil
56}
57
58func (p Profile) applyMaps(r rune) ([]rune, bool) {
59 for _, m := range p.Mappings {
60 rs, ok := m.Map(r)
61 if ok {
62 return rs, true
63 }
64 }
65 return nil, false
66}
67
68func (p Profile) runeIsProhibited(r rune) bool {
69 for _, s := range p.Prohibits {
70 if s.Contains(r) {
71 return true
72 }
73 }
74 return false
75}