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  }