github.com/rusq/gomojimoji@v0.0.1/mojimoji.go (about) 1 // Package mojimoji is a port of mojimoji package to Go. 2 // Original: https://github.com/studio-ousia/mojimoji 3 package gomojimoji 4 5 import ( 6 "strings" 7 "unicode/utf8" 8 ) 9 10 type options struct { 11 ascii bool 12 digit bool 13 kana bool 14 } 15 16 func (oo *options) apply(opt ...Option) *options { 17 for _, fn := range opt { 18 fn(oo) 19 } 20 return oo 21 } 22 23 // ASCII enables or disables conversion of ASCII runes (A-Za-z). 24 func ASCII(enable bool) Option { 25 return func(o *options) { 26 o.ascii = enable 27 } 28 } 29 30 // Digits enables or disables conversion of digit runes (0-9). 31 func Digits(enable bool) Option { 32 return func(o *options) { 33 o.digit = enable 34 } 35 } 36 37 // Kana enables or disables conversion of Kana runes. 38 func Kana(enable bool) Option { 39 return func(o *options) { 40 o.kana = enable 41 } 42 } 43 44 // Option is the option function signature. 45 type Option func(*options) 46 47 // ZenToHan converts text to half-width runes. By default all runes are 48 // converted, optionally caller can switch off rune-set by passing [Option]. 49 func ZenToHan(text string, opt ...Option) string { 50 var options = options{ 51 ascii: true, 52 digit: true, 53 kana: true, 54 } 55 options.apply(opt...) 56 57 var buf strings.Builder 58 for _, c := range text { 59 if r, ok := tabASCIIzh[c]; options.ascii && ok { 60 buf.WriteRune(r) 61 } else if r, ok := tabKANAzh[c]; options.kana && ok { 62 buf.WriteRune(r) 63 } else if r, ok := tabDIGITzh[c]; options.digit && ok { 64 buf.WriteRune(r) 65 } else if r, ok := tabKANATENzh[c]; options.kana && ok { 66 buf.WriteRune(r) 67 buf.WriteRune('゙') 68 } else if r, ok := tabKANAMARUzh[c]; options.kana && ok { 69 buf.WriteRune(r) 70 buf.WriteRune('゚') 71 } else { 72 buf.WriteRune(c) 73 } 74 } 75 return buf.String() 76 } 77 78 // HanToZen converts text to full-width runes. By default all runes are 79 // converted, optionally caller can switch off rune-set by passing [Option]. 80 func HanToZen(text string, opt ...Option) string { 81 var options = options{ 82 ascii: true, 83 digit: true, 84 kana: true, 85 } 86 options.apply(opt...) 87 88 var buf = make([]rune, utf8.RuneCountInString(text)) 89 var ( 90 prev rune 91 pos int 92 ) 93 for _, c := range text { 94 if r, ok := tabASCIIhz[c]; options.ascii && ok { 95 buf[pos] = r 96 } else if r, ok := tabDigitHz[c]; options.digit && ok { 97 buf[pos] = r 98 } else if r, ok := tabKanaTenHz[prev]; options.kana && c == '゙' && ok { 99 pos-- 100 buf[pos] = r 101 } else if r, ok := tabKanaMaruHz[prev]; options.kana && c == '゚' && ok { 102 pos-- 103 buf[pos] = r 104 } else if r, ok := tabKanaHz[c]; options.kana && ok { 105 buf[pos] = r 106 } else { 107 buf[pos] = c 108 } 109 prev = c 110 pos++ 111 } 112 return string(buf[:pos]) 113 }