| package stringprep |
| |
| import ( |
| "golang.org/x/text/unicode/norm" |
| ) |
| |
| // Profile represents a stringprep profile. |
| type Profile struct { |
| Mappings []Mapping |
| Normalize bool |
| Prohibits []Set |
| CheckBiDi bool |
| } |
| |
| var errProhibited = "prohibited character" |
| |
| // Prepare transforms an input string to an output string following |
| // the rules defined in the profile as defined by RFC-3454. |
| func (p Profile) Prepare(s string) (string, error) { |
| // Optimistically, assume output will be same length as input |
| temp := make([]rune, 0, len(s)) |
| |
| // Apply maps |
| for _, r := range s { |
| rs, ok := p.applyMaps(r) |
| if ok { |
| temp = append(temp, rs...) |
| } else { |
| temp = append(temp, r) |
| } |
| } |
| |
| // Normalize |
| var out string |
| if p.Normalize { |
| out = norm.NFKC.String(string(temp)) |
| } else { |
| out = string(temp) |
| } |
| |
| // Check prohibited |
| for _, r := range out { |
| if p.runeIsProhibited(r) { |
| return "", Error{Msg: errProhibited, Rune: r} |
| } |
| } |
| |
| // Check BiDi allowed |
| if p.CheckBiDi { |
| if err := passesBiDiRules(out); err != nil { |
| return "", err |
| } |
| } |
| |
| return out, nil |
| } |
| |
| func (p Profile) applyMaps(r rune) ([]rune, bool) { |
| for _, m := range p.Mappings { |
| rs, ok := m.Map(r) |
| if ok { |
| return rs, true |
| } |
| } |
| return nil, false |
| } |
| |
| func (p Profile) runeIsProhibited(r rune) bool { |
| for _, s := range p.Prohibits { |
| if s.Contains(r) { |
| return true |
| } |
| } |
| return false |
| } |