github.com/tomwright/dasel@v1.27.3/storage/parser.go (about) 1 package storage 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "path/filepath" 8 "strings" 9 ) 10 11 var readParsersByExtension = map[string]ReadParser{} 12 var writeParsersByExtension = map[string]WriteParser{} 13 var readParsersByName = map[string]ReadParser{} 14 var writeParsersByName = map[string]WriteParser{} 15 16 func registerReadParser(names []string, extensions []string, parser ReadParser) { 17 for _, n := range names { 18 readParsersByName[n] = parser 19 } 20 for _, e := range extensions { 21 readParsersByExtension[e] = parser 22 } 23 } 24 25 func registerWriteParser(names []string, extensions []string, parser WriteParser) { 26 for _, n := range names { 27 writeParsersByName[n] = parser 28 } 29 for _, e := range extensions { 30 writeParsersByExtension[e] = parser 31 } 32 } 33 34 // UnknownParserErr is returned when an invalid parser name is given. 35 type UnknownParserErr struct { 36 Parser string 37 } 38 39 // Error returns the error message. 40 func (e UnknownParserErr) Error() string { 41 return fmt.Sprintf("unknown parser: %s", e.Parser) 42 } 43 44 // ReadParser can be used to convert bytes to data. 45 type ReadParser interface { 46 // FromBytes returns some data that is represented by the given bytes. 47 FromBytes(byteData []byte) (interface{}, error) 48 } 49 50 // WriteParser can be used to convert data to bytes. 51 type WriteParser interface { 52 // ToBytes returns a slice of bytes that represents the given value. 53 ToBytes(value interface{}, options ...ReadWriteOption) ([]byte, error) 54 } 55 56 // Parser can be used to load and save files from/to disk. 57 type Parser interface { 58 ReadParser 59 WriteParser 60 } 61 62 // NewReadParserFromFilename returns a ReadParser from the given filename. 63 func NewReadParserFromFilename(filename string) (ReadParser, error) { 64 ext := strings.ToLower(filepath.Ext(filename)) 65 p, ok := readParsersByExtension[ext] 66 if !ok { 67 return nil, &UnknownParserErr{Parser: ext} 68 } 69 return p, nil 70 } 71 72 // NewReadParserFromString returns a ReadParser from the given parser name. 73 func NewReadParserFromString(parser string) (ReadParser, error) { 74 p, ok := readParsersByName[parser] 75 if !ok { 76 return nil, &UnknownParserErr{Parser: parser} 77 } 78 return p, nil 79 } 80 81 // NewWriteParserFromFilename returns a WriteParser from the given filename. 82 func NewWriteParserFromFilename(filename string) (WriteParser, error) { 83 ext := strings.ToLower(filepath.Ext(filename)) 84 p, ok := writeParsersByExtension[ext] 85 if !ok { 86 return nil, &UnknownParserErr{Parser: ext} 87 } 88 return p, nil 89 } 90 91 // NewWriteParserFromString returns a WriteParser from the given parser name. 92 func NewWriteParserFromString(parser string) (WriteParser, error) { 93 p, ok := writeParsersByName[parser] 94 if !ok { 95 return nil, &UnknownParserErr{Parser: parser} 96 } 97 return p, nil 98 } 99 100 // LoadFromFile loads data from the given file. 101 func LoadFromFile(filename string, p ReadParser) (interface{}, error) { 102 f, err := os.Open(filename) 103 if err != nil { 104 return nil, fmt.Errorf("could not open file: %w", err) 105 } 106 return Load(p, f) 107 } 108 109 // Load loads data from the given io.Reader. 110 func Load(p ReadParser, reader io.Reader) (interface{}, error) { 111 byteData, err := io.ReadAll(reader) 112 if err != nil { 113 return nil, fmt.Errorf("could not read data: %w", err) 114 } 115 return p.FromBytes(byteData) 116 } 117 118 // Write writes the value to the given io.Writer. 119 func Write(p WriteParser, value interface{}, originalValue interface{}, writer io.Writer, options ...ReadWriteOption) error { 120 switch typed := originalValue.(type) { 121 case OriginalRequired: 122 if typed.OriginalRequired() { 123 value = originalValue 124 } 125 } 126 byteData, err := p.ToBytes(value, options...) 127 if err != nil { 128 return fmt.Errorf("could not get byte data for file: %w", err) 129 } 130 if _, err := writer.Write(byteData); err != nil { 131 return fmt.Errorf("could not write data: %w", err) 132 } 133 return nil 134 } 135 136 // OriginalRequired can be used in conjunction with RealValue to allow parsers to be more intelligent 137 // with the data they read/write. 138 type OriginalRequired interface { 139 // OriginalRequired tells dasel if the parser requires the original value when converting to bytes. 140 OriginalRequired() bool 141 } 142 143 // RealValue can be used in conjunction with OriginalRequired to allow parsers to be more intelligent 144 // with the data they read/write. 145 type RealValue interface { 146 // RealValue returns the real value that dasel should use when processing data. 147 RealValue() interface{} 148 } 149 150 type originalRequired struct { 151 } 152 153 // OriginalRequired tells dasel if the parser requires the original value when converting to bytes. 154 func (d originalRequired) OriginalRequired() bool { 155 return true 156 } 157 158 // SingleDocument is a parser result that contains a single document. 159 type SingleDocument interface { 160 Document() interface{} 161 } 162 163 // MultiDocument is a parser result that contains multiple documents. 164 type MultiDocument interface { 165 Documents() []interface{} 166 } 167 168 // BasicSingleDocument represents a single document file. 169 type BasicSingleDocument struct { 170 originalRequired 171 Value interface{} 172 } 173 174 // RealValue returns the real value that dasel should use when processing data. 175 func (d *BasicSingleDocument) RealValue() interface{} { 176 return d.Value 177 } 178 179 // Document returns the document that should be written to output. 180 func (d *BasicSingleDocument) Document() interface{} { 181 return d.Value 182 } 183 184 // BasicMultiDocument represents a multi-document file. 185 type BasicMultiDocument struct { 186 originalRequired 187 Values []interface{} 188 } 189 190 // RealValue returns the real value that dasel should use when processing data. 191 func (d *BasicMultiDocument) RealValue() interface{} { 192 return d.Values 193 } 194 195 // Documents returns the documents that should be written to output. 196 func (d *BasicMultiDocument) Documents() []interface{} { 197 return d.Values 198 }