github.com/liquid-dev/text@v0.3.3-liquid/internal/language/compose.go (about)

     1  // Copyright 2018 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  
     5  package language
     6  
     7  import (
     8  	"sort"
     9  	"strings"
    10  )
    11  
    12  // A Builder allows constructing a Tag from individual components.
    13  // Its main user is Compose in the top-level language package.
    14  type Builder struct {
    15  	Tag Tag
    16  
    17  	private    string // the x extension
    18  	variants   []string
    19  	extensions []string
    20  }
    21  
    22  // Make returns a new Tag from the current settings.
    23  func (b *Builder) Make() Tag {
    24  	t := b.Tag
    25  
    26  	if len(b.extensions) > 0 || len(b.variants) > 0 {
    27  		sort.Sort(sortVariants(b.variants))
    28  		sort.Strings(b.extensions)
    29  
    30  		if b.private != "" {
    31  			b.extensions = append(b.extensions, b.private)
    32  		}
    33  		n := maxCoreSize + tokenLen(b.variants...) + tokenLen(b.extensions...)
    34  		buf := make([]byte, n)
    35  		p := t.genCoreBytes(buf)
    36  		t.pVariant = byte(p)
    37  		p += appendTokens(buf[p:], b.variants...)
    38  		t.pExt = uint16(p)
    39  		p += appendTokens(buf[p:], b.extensions...)
    40  		t.str = string(buf[:p])
    41  		// We may not always need to remake the string, but when or when not
    42  		// to do so is rather tricky.
    43  		scan := makeScanner(buf[:p])
    44  		t, _ = parse(&scan, "")
    45  		return t
    46  
    47  	} else if b.private != "" {
    48  		t.str = b.private
    49  		t.RemakeString()
    50  	}
    51  	return t
    52  }
    53  
    54  // SetTag copies all the settings from a given Tag. Any previously set values
    55  // are discarded.
    56  func (b *Builder) SetTag(t Tag) {
    57  	b.Tag.LangID = t.LangID
    58  	b.Tag.RegionID = t.RegionID
    59  	b.Tag.ScriptID = t.ScriptID
    60  	// TODO: optimize
    61  	b.variants = b.variants[:0]
    62  	if variants := t.Variants(); variants != "" {
    63  		for _, vr := range strings.Split(variants[1:], "-") {
    64  			b.variants = append(b.variants, vr)
    65  		}
    66  	}
    67  	b.extensions, b.private = b.extensions[:0], ""
    68  	for _, e := range t.Extensions() {
    69  		b.AddExt(e)
    70  	}
    71  }
    72  
    73  // AddExt adds extension e to the tag. e must be a valid extension as returned
    74  // by Tag.Extension. If the extension already exists, it will be discarded,
    75  // except for a -u extension, where non-existing key-type pairs will added.
    76  func (b *Builder) AddExt(e string) {
    77  	if e[0] == 'x' {
    78  		if b.private == "" {
    79  			b.private = e
    80  		}
    81  		return
    82  	}
    83  	for i, s := range b.extensions {
    84  		if s[0] == e[0] {
    85  			if e[0] == 'u' {
    86  				b.extensions[i] += e[1:]
    87  			}
    88  			return
    89  		}
    90  	}
    91  	b.extensions = append(b.extensions, e)
    92  }
    93  
    94  // SetExt sets the extension e to the tag. e must be a valid extension as
    95  // returned by Tag.Extension. If the extension already exists, it will be
    96  // overwritten, except for a -u extension, where the individual key-type pairs
    97  // will be set.
    98  func (b *Builder) SetExt(e string) {
    99  	if e[0] == 'x' {
   100  		b.private = e
   101  		return
   102  	}
   103  	for i, s := range b.extensions {
   104  		if s[0] == e[0] {
   105  			if e[0] == 'u' {
   106  				b.extensions[i] = e + s[1:]
   107  			} else {
   108  				b.extensions[i] = e
   109  			}
   110  			return
   111  		}
   112  	}
   113  	b.extensions = append(b.extensions, e)
   114  }
   115  
   116  // AddVariant adds any number of variants.
   117  func (b *Builder) AddVariant(v ...string) {
   118  	for _, v := range v {
   119  		if v != "" {
   120  			b.variants = append(b.variants, v)
   121  		}
   122  	}
   123  }
   124  
   125  // ClearVariants removes any variants previously added, including those
   126  // copied from a Tag in SetTag.
   127  func (b *Builder) ClearVariants() {
   128  	b.variants = b.variants[:0]
   129  }
   130  
   131  // ClearExtensions removes any extensions previously added, including those
   132  // copied from a Tag in SetTag.
   133  func (b *Builder) ClearExtensions() {
   134  	b.private = ""
   135  	b.extensions = b.extensions[:0]
   136  }
   137  
   138  func tokenLen(token ...string) (n int) {
   139  	for _, t := range token {
   140  		n += len(t) + 1
   141  	}
   142  	return
   143  }
   144  
   145  func appendTokens(b []byte, token ...string) int {
   146  	p := 0
   147  	for _, t := range token {
   148  		b[p] = '-'
   149  		copy(b[p+1:], t)
   150  		p += 1 + len(t)
   151  	}
   152  	return p
   153  }
   154  
   155  type sortVariants []string
   156  
   157  func (s sortVariants) Len() int {
   158  	return len(s)
   159  }
   160  
   161  func (s sortVariants) Swap(i, j int) {
   162  	s[j], s[i] = s[i], s[j]
   163  }
   164  
   165  func (s sortVariants) Less(i, j int) bool {
   166  	return variantIndex[s[i]] < variantIndex[s[j]]
   167  }