encoder.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. package exporter
  2. import (
  3. "bytes"
  4. "compress/gzip"
  5. "encoding"
  6. "github.com/opencost/opencost/core/pkg/util/json"
  7. )
  8. // Encoder[T] is a generic interface for encoding an instance of a T type into a byte slice.
  9. type Encoder[T any] interface {
  10. Encode(*T) ([]byte, error)
  11. // FileExt returns the file extension for the encoded data. This can be used by a pathing strategy
  12. // to append the file extension when exporting the data. Returning an empty string will typically
  13. // omit the file extension completely.
  14. FileExt() string
  15. }
  16. // BinaryMarshalerPtr[T] is a generic constraint to ensure types passed to the encoder implement
  17. // encoding.BinaryMarshaler and are pointers to T.
  18. type BinaryMarshalerPtr[T any] interface {
  19. encoding.BinaryMarshaler
  20. *T
  21. }
  22. // BingenEncoder[T, U] is a generic encoder that uses the BinaryMarshaler interface to encode data.
  23. // It supports any type T that implements the encoding.BinaryMarshaler interface.
  24. type BingenEncoder[T any, U BinaryMarshalerPtr[T]] struct{}
  25. // NewBingenEncoder creates an `Encoder[T]` implementation which supports binary encoding for the `T`
  26. // type.
  27. func NewBingenEncoder[T any, U BinaryMarshalerPtr[T]]() Encoder[T] {
  28. return new(BingenEncoder[T, U])
  29. }
  30. // Encode encodes the provided data of type T into a byte slice using the BinaryMarshaler interface.
  31. func (b *BingenEncoder[T, U]) Encode(data *T) ([]byte, error) {
  32. var bingenData U = data
  33. return bingenData.MarshalBinary()
  34. }
  35. // FileExt returns the file extension for the encoded data. In this case, it returns an empty string
  36. // to indicate that there is no specific file extension for the binary encoded data.
  37. func (b *BingenEncoder[T, U]) FileExt() string {
  38. return ""
  39. }
  40. // JSONEncoder[T] is a generic encoder that uses the JSON encoding format to encode data.
  41. type JSONEncoder[T any] struct{}
  42. // NewJSONEncoder creates an `Encoder[T]` implementation which supports JSON encoding for the `T`
  43. // type.
  44. func NewJSONEncoder[T any]() Encoder[T] {
  45. return new(JSONEncoder[T])
  46. }
  47. // Encode encodes the provided data of type T into a byte slice using JSON encoding.
  48. func (j *JSONEncoder[T]) Encode(data *T) ([]byte, error) {
  49. return json.Marshal(data)
  50. }
  51. // FileExt returns the file extension for the encoded data. In this case, it returns "json" to indicate
  52. // that the data is in JSON format.
  53. func (j *JSONEncoder[T]) FileExt() string {
  54. return "json"
  55. }
  56. type GZipEncoder[T any] struct {
  57. encoder Encoder[T]
  58. }
  59. // NewGZipEncoder creates a new GZip encoder which wraps the provided encoder.
  60. // The encoder is used to encode the data before compressing it with GZip.
  61. func NewGZipEncoder[T any](encoder Encoder[T]) Encoder[T] {
  62. return &GZipEncoder[T]{
  63. encoder: encoder,
  64. }
  65. }
  66. // Encode encodes the provided data of type T into a byte slice using JSON encoding.
  67. func (gz *GZipEncoder[T]) Encode(data *T) ([]byte, error) {
  68. encoded, err := gz.encoder.Encode(data)
  69. if err != nil {
  70. return nil, err
  71. }
  72. var buf bytes.Buffer
  73. gzWriter, err := gzip.NewWriterLevel(&buf, gzip.BestCompression)
  74. if err != nil {
  75. return nil, err
  76. }
  77. gzWriter.Write(encoded)
  78. gzWriter.Close()
  79. return buf.Bytes(), nil
  80. }
  81. // FileExt returns the file extension for the encoded data. In this case, it returns the wrapped encoder's
  82. // file extension with ".gz" appended to indicate that the data is compressed with GZip.
  83. func (gz *GZipEncoder[T]) FileExt() string {
  84. return gz.encoder.FileExt() + ".gz"
  85. }