Line data Source code
1 : // Package datadrivenutil defines facilities to improve ergonomics around 2 : // parsing datadriven test input. 3 : package datadrivenutil 4 : 5 : import ( 6 : "encoding/hex" 7 : "fmt" 8 : "strconv" 9 : "strings" 10 : "unicode" 11 : ) 12 : 13 : // Lines wraps a string, providing facilities for parsing individual lines. 14 : type Lines string 15 : 16 : // Next returns the string up to the next newline. The receiver is modified to 17 : // point to the string's contents immediately after the newline. 18 1 : func (l *Lines) Next() (line Line) { 19 1 : i := strings.IndexByte(string(*l), '\n') 20 1 : if i == -1 { 21 1 : line = Line(*l) 22 1 : *l = "" 23 1 : } else { 24 1 : line = Line((*l)[:i]) 25 1 : *l = (*l)[i+1:] 26 1 : } 27 1 : return line 28 : } 29 : 30 : // A Line is a string with no newlines. 31 : type Line string 32 : 33 : // Fields breaks the line into fields delimited by whitespace and any runes 34 : // passed into the function. 35 1 : func (l Line) Fields(delims ...rune) Fields { 36 1 : return Fields(strings.FieldsFunc(string(l), func(r rune) bool { 37 1 : if unicode.IsSpace(r) { 38 1 : return true 39 1 : } 40 1 : for _, delim := range delims { 41 1 : if delim == r { 42 1 : return true 43 1 : } 44 : } 45 1 : return false 46 : })) 47 : } 48 : 49 : // Fields wraps a []string with facilities for parsing out values. 50 : type Fields []string 51 : 52 : // String implements fmt.Stringer. 53 0 : func (fs Fields) String() string { 54 0 : return strings.Join(fs, " ") 55 0 : } 56 : 57 : // HasValue searches for a field that is exactly the provided string. 58 1 : func (fs Fields) HasValue(value string) bool { 59 1 : for i := range fs { 60 1 : if fs[i] == value { 61 1 : return true 62 1 : } 63 : } 64 1 : return false 65 : } 66 : 67 : // Index returns the field at index i, or the empty string if there are i or 68 : // fewer fields. 69 1 : func (fs Fields) Index(i int) Value { 70 1 : if len(fs) <= i { 71 0 : return "" 72 0 : } 73 1 : return Value(fs[i]) 74 : } 75 : 76 : // KeyValue looks for a field containing a key=value pair with the provided key. 77 : // If not found, KeyValue returns false for the second return value. 78 1 : func (fs Fields) KeyValue(key string) (Value, bool) { 79 1 : for i := range fs { 80 1 : if len(fs[i]) >= len(key) && strings.HasPrefix(fs[i], key) && fs[i][len(key)] == '=' { 81 1 : return Value(fs[i][len(key)+1:]), true 82 1 : } 83 : } 84 0 : return "", false 85 : } 86 : 87 : // MustKeyValue is like KeyValue but panics if the field is not found. 88 1 : func (fs Fields) MustKeyValue(key string) Value { 89 1 : f, ok := fs.KeyValue(key) 90 1 : if !ok { 91 0 : panic(fmt.Sprintf("unable to find required key-value pair %q", key)) 92 : } 93 1 : return f 94 : } 95 : 96 : // HexBytes parses all the fields as hex-encoded bytes, returning the 97 : // concatenated decoded bytes. It panics if any of the fields fail to parse as 98 : // hex-encoded bytes. 99 0 : func (fs Fields) HexBytes() []byte { 100 0 : var b []byte 101 0 : for _, f := range fs { 102 0 : b = append(b, Value(f).HexBytes()...) 103 0 : } 104 0 : return b 105 : } 106 : 107 : // A Value represents a single string of unknown structure. A Value is sometimes 108 : // used to represent an entire element of a Fields and other times a substring 109 : // of an individual field. This blurring of semantics is convenient for 110 : // parsing. 111 : type Value string 112 : 113 : // Str returns the value as a string. 114 1 : func (v Value) Str() string { return string(v) } 115 : 116 : // Bytes returns the value as a byte slice. 117 1 : func (v Value) Bytes() []byte { return []byte(v) } 118 : 119 : // Int parses the value as an int. It panics if the value fails to decode as an 120 : // integer. 121 1 : func (v Value) Int() int { 122 1 : vi, err := strconv.Atoi(string(v)) 123 1 : if err != nil { 124 0 : panic(err) 125 : } 126 1 : return vi 127 : } 128 : 129 : // Uint64 parses the value as an uint64. It panics if the value fails to decode 130 : // as an uint64. 131 1 : func (v Value) Uint64() uint64 { 132 1 : vi, err := strconv.ParseUint(string(v), 10, 64) 133 1 : if err != nil { 134 0 : panic(err) 135 : } 136 1 : return vi 137 : } 138 : 139 : // HexBytes decodes the value as hex-encoded bytes. It panics if the value fails 140 : // to decode as hex-encoded. 141 0 : func (v Value) HexBytes() []byte { 142 0 : b, err := hex.DecodeString(string(v)) 143 0 : if err != nil { 144 0 : panic(err) 145 : } 146 0 : return b 147 : }