github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/git/foreachref/format.go (about) 1 // Copyright 2023 The GitBundle Inc. All rights reserved. 2 // Copyright 2017 The Gitea Authors. All rights reserved. 3 // Use of this source code is governed by a MIT-style 4 // license that can be found in the LICENSE file. 5 6 package foreachref 7 8 import ( 9 "encoding/hex" 10 "fmt" 11 "io" 12 "strings" 13 ) 14 15 var ( 16 nullChar = []byte("\x00") 17 dualNullChar = []byte("\x00\x00") 18 ) 19 20 // Format supports specifying and parsing an output format for 'git 21 // for-each-ref'. See See git-for-each-ref(1) for available fields. 22 type Format struct { 23 // fieldNames hold %(fieldname)s to be passed to the '--format' flag of 24 // for-each-ref. See git-for-each-ref(1) for available fields. 25 fieldNames []string 26 27 // fieldDelim is the character sequence that is used to separate fields 28 // for each reference. fieldDelim and refDelim should be selected to not 29 // interfere with each other and to not be present in field values. 30 fieldDelim []byte 31 // fieldDelimStr is a string representation of fieldDelim. Used to save 32 // us from repetitive reallocation whenever we need the delimiter as a 33 // string. 34 fieldDelimStr string 35 // refDelim is the character sequence used to separate reference from 36 // each other in the output. fieldDelim and refDelim should be selected 37 // to not interfere with each other and to not be present in field 38 // values. 39 refDelim []byte 40 } 41 42 // NewFormat creates a forEachRefFormat using the specified fieldNames. See 43 // git-for-each-ref(1) for available fields. 44 func NewFormat(fieldNames ...string) Format { 45 return Format{ 46 fieldNames: fieldNames, 47 fieldDelim: nullChar, 48 fieldDelimStr: string(nullChar), 49 refDelim: dualNullChar, 50 } 51 } 52 53 // Flag returns a for-each-ref --format flag value that captures the fieldNames. 54 func (f Format) Flag() string { 55 var formatFlag strings.Builder 56 for i, field := range f.fieldNames { 57 // field key and field value 58 formatFlag.WriteString(fmt.Sprintf("%s %%(%s)", field, field)) 59 60 if i < len(f.fieldNames)-1 { 61 // note: escape delimiters to allow control characters as 62 // delimiters. For example, '%00' for null character or '%0a' 63 // for newline. 64 formatFlag.WriteString(f.hexEscaped(f.fieldDelim)) 65 } 66 } 67 formatFlag.WriteString(f.hexEscaped(f.refDelim)) 68 return formatFlag.String() 69 } 70 71 // Parser returns a Parser capable of parsing 'git for-each-ref' output produced 72 // with this Format. 73 func (f Format) Parser(r io.Reader) *Parser { 74 return NewParser(r, f) 75 } 76 77 // hexEscaped produces hex-escpaed characters from a string. For example, "\n\0" 78 // would turn into "%0a%00". 79 func (f Format) hexEscaped(delim []byte) string { 80 escaped := "" 81 for i := 0; i < len(delim); i++ { 82 escaped += "%" + hex.EncodeToString([]byte{delim[i]}) 83 } 84 return escaped 85 }