1// Copyright 2015 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
5package precis
6
7import (
8	"unicode"
9	"unicode/utf8"
10
11	"golang.org/x/text/transform"
12)
13
14type nickAdditionalMapping struct {
15	// TODO: This transformer needs to be stateless somehow…
16	notStart  bool
17	prevSpace bool
18}
19
20func (t *nickAdditionalMapping) Reset() {
21	t.prevSpace = false
22	t.notStart = false
23}
24
25func (t *nickAdditionalMapping) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
26	// RFC 8266 §2.1.  Rules
27	//
28	// 2.  Additional Mapping Rule: The additional mapping rule consists of
29	//     the following sub-rules.
30	//
31	//     a.  Map any instances of non-ASCII space to SPACE (U+0020); a
32	//         non-ASCII space is any Unicode code point having a general
33	//         category of "Zs", naturally with the exception of SPACE
34	//         (U+0020).  (The inclusion of only ASCII space prevents
35	//         confusion with various non-ASCII space code points, many of
36	//         which are difficult to reproduce across different input
37	//         methods.)
38	//
39	//     b.  Remove any instances of the ASCII space character at the
40	//         beginning or end of a nickname (e.g., "stpeter " is mapped to
41	//         "stpeter").
42	//
43	//     c.  Map interior sequences of more than one ASCII space character
44	//         to a single ASCII space character (e.g., "St  Peter" is
45	//         mapped to "St Peter").
46	for nSrc < len(src) {
47		r, size := utf8.DecodeRune(src[nSrc:])
48		if size == 0 { // Incomplete UTF-8 encoding
49			if !atEOF {
50				return nDst, nSrc, transform.ErrShortSrc
51			}
52			size = 1
53		}
54		if unicode.Is(unicode.Zs, r) {
55			t.prevSpace = true
56		} else {
57			if t.prevSpace && t.notStart {
58				dst[nDst] = ' '
59				nDst += 1
60			}
61			if size != copy(dst[nDst:], src[nSrc:nSrc+size]) {
62				nDst += size
63				return nDst, nSrc, transform.ErrShortDst
64			}
65			nDst += size
66			t.prevSpace = false
67			t.notStart = true
68		}
69		nSrc += size
70	}
71	return nDst, nSrc, nil
72}
73