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  }