github.com/linuxboot/fiano@v1.2.0/pkg/guid2english/transformer.go (about)

     1  // Copyright 2019 the LinuxBoot 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 guid2english provides a transform.Transformer which replaces all
     6  // GUIDs in the input with their known English representation.
     7  package guid2english
     8  
     9  import (
    10  	"bytes"
    11  	"regexp"
    12  	"text/template"
    13  
    14  	"github.com/linuxboot/fiano/pkg/guid"
    15  	"github.com/linuxboot/fiano/pkg/knownguids"
    16  	"github.com/linuxboot/fiano/pkg/log"
    17  	"golang.org/x/text/transform"
    18  )
    19  
    20  var guidRegex = regexp.MustCompile(
    21  	"[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}",
    22  )
    23  
    24  var partialguidRegex = regexp.MustCompile(
    25  	"[-a-fA-F0-9]{1,36}$",
    26  )
    27  
    28  // Mapper converts a GUID to a string.
    29  type Mapper interface {
    30  	Map(guid.GUID) []byte
    31  }
    32  
    33  // TemplateMapper implements mapper using Go's text/template package. The
    34  // template can refer to the following variables:
    35  //   * {{.Guid}}: The GUID being mapped
    36  //   * {{.Name}}: The English name of the GUID or "UNKNOWN"
    37  //   * {{.IsKnown}}: Set to true when the English name is not known
    38  type TemplateMapper struct {
    39  	tmpl *template.Template
    40  }
    41  
    42  // NewTemplateMapper creates a new TemplateMapper given a Template.
    43  func NewTemplateMapper(tmpl *template.Template) *TemplateMapper {
    44  	return &TemplateMapper{
    45  		tmpl: tmpl,
    46  	}
    47  }
    48  
    49  // Map implements the Mapper.Map() function.
    50  func (f *TemplateMapper) Map(g guid.GUID) []byte {
    51  	name, isKnown := knownguids.GUIDs[g]
    52  	if !isKnown {
    53  		name = "UNKNOWN"
    54  	}
    55  
    56  	b := &bytes.Buffer{}
    57  	err := f.tmpl.Execute(b, struct {
    58  		GUID    guid.GUID
    59  		Name    string
    60  		IsKnown bool
    61  	}{
    62  		GUID:    g,
    63  		Name:    name,
    64  		IsKnown: isKnown,
    65  	})
    66  	if err != nil {
    67  		// There is likely a bug in the template. We do not want to
    68  		// interrupt the byte stream, so just log the error.
    69  		log.Errorf("Error in template: %v", err)
    70  	}
    71  	return b.Bytes()
    72  }
    73  
    74  // Transformer replaces all the GUIDs using the Mapper interface. For example,
    75  // this can replace all the GUIDs with their's English representation.
    76  type Transformer struct {
    77  	mapper Mapper
    78  }
    79  
    80  // New creates a new Transformer with the given Mapper.
    81  func New(m Mapper) *Transformer {
    82  	return &Transformer{
    83  		mapper: m,
    84  	}
    85  }
    86  
    87  func (t *Transformer) bufferMap(match []byte) []byte {
    88  	// The regex only matches valid GUIDs, so this must parse.
    89  	g, err := guid.Parse(string(match))
    90  	if err != nil {
    91  		return match
    92  	}
    93  	return t.mapper.Map(*g)
    94  }
    95  
    96  // Transform implements transform.Transformer.Transform().
    97  func (t *Transformer) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
    98  	if atEOF {
    99  		// we have the end of file, try to process all at once
   100  		transformed := guidRegex.ReplaceAllFunc(src, t.bufferMap)
   101  		if len(transformed) > len(dst) {
   102  			// we were too optimistic, dst is too short
   103  			d, s, e := t.Transform(dst, src, false)
   104  			if e != transform.ErrShortSrc {
   105  				return d, s, e
   106  			}
   107  			return d, s, transform.ErrShortDst
   108  		}
   109  		copy(dst, transformed)
   110  		return len(transformed), len(src), nil
   111  	}
   112  	loc := guidRegex.FindIndex(src)
   113  	if loc == nil {
   114  		// check if the end potentially contain the beginning of a GUID
   115  		loc := partialguidRegex.FindIndex(src)
   116  		if loc == nil {
   117  			copy(dst, src)
   118  			return len(src), len(src), nil
   119  		}
   120  		copy(dst, src[0:loc[0]])
   121  		return loc[0], loc[0], transform.ErrShortSrc
   122  	}
   123  	copy(dst, src[0:loc[0]])
   124  	mappedGUID := t.bufferMap(src[loc[0]:loc[1]])
   125  	if loc[0]+len(mappedGUID) > len(dst) {
   126  		// mapped buffer does not fit, only send the plain part
   127  		return loc[0], loc[0], transform.ErrShortDst
   128  	}
   129  
   130  	copy(dst[loc[0]:], mappedGUID)
   131  	return loc[0] + len(mappedGUID), loc[1], transform.ErrShortSrc
   132  }
   133  
   134  // Reset implements transform.Transformer.Reset().
   135  func (t *Transformer) Reset() {
   136  }