diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100644 index 0000000..1333e15 --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,27 @@ +# This workflow will build a golang project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go + +name: Go + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: "1.22" + + - name: Build + run: go build -v ./... + + - name: Test + run: go test -v -cover ./... diff --git a/.gitignore b/.gitignore index 66fd13c..acc436c 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ # Dependency directories (remove the comment below to include it) # vendor/ + +cover.html diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index bcda1cd..0000000 --- a/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: go - -go: - - 1.17.x - -before_install: - - go get -t -v ./... - -script: - - go test -race -coverprofile=coverage.txt -covermode=atomic diff --git a/README.md b/README.md index 2767e91..41a0c7a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# Excelsior +[github](https://github.com/kakilangit/excelsior) +[pkg.go.dev](https://pkg.go.dev/github.com/kakilangit/excelsior) -[![PkgGoDev](https://pkg.go.dev/badge/github.com/kakilangit/excelsior)](https://pkg.go.dev/github.com/kakilangit/excelsior) -[![Build Status](https://app.travis-ci.com/kakilangit/excelsior.svg?branch=main)](https://app.travis-ci.com/github/kakilangit/excelsior) +# Excelsior An excelize wrapper to separate the presentation and business logic. @@ -73,3 +73,8 @@ func main() { } ``` + +## License + +MIT +Copyright (c) 2022 kakilangit diff --git a/serialize.go b/serialize.go index 08f7370..4ab4c47 100644 --- a/serialize.go +++ b/serialize.go @@ -44,8 +44,8 @@ func Serialize(f SerializeFn) ([]byte, error) { return f(file, style) } -// DefaultStyleSetter is helper for the default excelsior row style. -func DefaultStyleSetter(_ int) int { +// DefaultGetStyleFn is helper for the default excelsior row style. +func DefaultGetStyleFn(int) int { return DefaultStyleID } diff --git a/serialize_test.go b/serialize_test.go new file mode 100644 index 0000000..d56cc43 --- /dev/null +++ b/serialize_test.go @@ -0,0 +1,65 @@ +package excelsior_test + +import ( + "net/http" + "testing" + + "github.com/kakilangit/excelsior" + "github.com/xuri/excelize/v2" +) + +type nopResponseWriter struct{} + +func (nopResponseWriter) Header() http.Header { + return http.Header{} +} + +func (nopResponseWriter) Write([]byte) (int, error) { + return 0, nil +} + +func (nopResponseWriter) WriteHeader(int) {} + +func TestSerialize(t *testing.T) { + data, err := excelsior.Serialize(func(file *excelize.File, style excelsior.Style) ([]byte, error) { + const sheetName = "not found alphabet" + + excelsior.SetDefaultSheetName(file, sheetName) + + headers := []any{"alphabet"} + data := nopSheetData{row: excelsior.Row{"a"}} + + sheet := excelsior.NewSheet(headers, excelsior.DefaultGetStyleFn, style.Header(), data) + if err := sheet.Generate(file, sheetName); err != nil { + return nil, err + } + + return excelsior.Byte(file) + }) + + if err != nil { + t.Errorf("failed to serialize: %v", err) + } + + if len(data) == 0 { + t.Errorf("empty data") + } +} + +func TestSetHeader(t *testing.T) { + header := http.Header{} + excelsior.SetHeader(header, "test") + + if header.Get("Content-Disposition") != "attachment; filename=test.xlsx" { + t.Errorf("failed to set header") + } +} + +func TestWriteByt(t *testing.T) { + data := []byte("test") + rw := nopResponseWriter{} + err := excelsior.WriteByte(data, "test", rw) + if err != nil { + t.Errorf("failed to write byte: %v", err) + } +} diff --git a/sheet.go b/sheet.go index c1e5b9f..b83eaa6 100644 --- a/sheet.go +++ b/sheet.go @@ -11,15 +11,15 @@ type SheetProvider interface { // Row represents Excel row. type Row []any -// StyleSetter is signature function for set the row by providing column number. -type StyleSetter func(colID int) int +// GetStyleByColIDFn is signature function for get the style by providing column number. +type GetStyleByColIDFn func(colID int) int // SheetBase is the interface generated by excelsior.Sheet, reducing the boiler-plates. type SheetBase interface { TotalColumn() int HeaderRow() Row - HeaderRowStyle() StyleSetter - RowStyle() StyleSetter + HeaderRowStyle() GetStyleByColIDFn + RowStyle() GetStyleByColIDFn } // SheetData is the interface need to be provided by the user of excelsior. @@ -31,18 +31,18 @@ type SheetData interface { // Sheet implements SheetProvider interface. type Sheet struct { SheetData - headerRow Row - rowSetter StyleSetter - headStyleID int + headerRow Row + rowGetStyleFn GetStyleByColIDFn + headStyleID int } // NewSheet will create new Sheet. -func NewSheet(headerRow Row, rowSetter StyleSetter, headStyleID int, data SheetData) *Sheet { +func NewSheet(headerRow Row, rowGetStyleFn GetStyleByColIDFn, headStyleID int, data SheetData) *Sheet { return &Sheet{ - SheetData: data, - headerRow: headerRow, - rowSetter: rowSetter, - headStyleID: headStyleID, + SheetData: data, + headerRow: headerRow, + rowGetStyleFn: rowGetStyleFn, + headStyleID: headStyleID, } } @@ -57,15 +57,15 @@ func (s Sheet) TotalColumn() int { } // HeaderRowStyle returns the style rowSetter for the header row. -func (s Sheet) HeaderRowStyle() StyleSetter { +func (s Sheet) HeaderRowStyle() GetStyleByColIDFn { return func(_ int) int { return s.headStyleID } } // RowStyle returns the style rowSetter for the remaining row. -func (s Sheet) RowStyle() StyleSetter { - return s.rowSetter +func (s Sheet) RowStyle() GetStyleByColIDFn { + return s.rowGetStyleFn } // Generate creates Excel sheet with provided excelize.File, and sheet name. diff --git a/sheet_test.go b/sheet_test.go new file mode 100644 index 0000000..4f58a31 --- /dev/null +++ b/sheet_test.go @@ -0,0 +1,63 @@ +package excelsior_test + +import ( + "testing" + + "github.com/kakilangit/excelsior" +) + +type nopSheetData struct { + row excelsior.Row +} + +func (nopSheetData) Total() int { + return 1 +} + +func (d nopSheetData) Row(int) excelsior.Row { + return d.row +} + +func TestSheet(t *testing.T) { + header := excelsior.Row{"Name", "Age"} + getRowStyle := func(int) int { return 0 } + headStyleID := 1 + data := nopSheetData{row: excelsior.Row{"John Doe", 30}} + s := excelsior.NewSheet(header, getRowStyle, headStyleID, data) + + if s.TotalColumn() != 2 { + t.Errorf("TotalColumn() = %d; want 2", s.TotalColumn()) + } + + if s.HeaderRow()[0] != "Name" { + t.Errorf("HeaderRow()[0] = %s; want Name", s.HeaderRow()[0]) + } + + if s.HeaderRow()[1] != "Age" { + t.Errorf("HeaderRow()[1] = %s; want Age", s.HeaderRow()[1]) + } + + if s.HeaderRowStyle()(0) != 1 { + t.Errorf("HeaderRowStyle()(0) = %d; want 1", s.HeaderRowStyle()(0)) + } + + if s.HeaderRowStyle()(1) != 1 { + t.Errorf("HeaderRowStyle()(1) = %d; want 1", s.HeaderRowStyle()(1)) + } + + if s.RowStyle()(0) != 0 { + t.Errorf("RowStyle()(0) = %d; want 0", s.RowStyle()(0)) + } + + if s.RowStyle()(1) != 0 { + t.Errorf("RowStyle()(1) = %d; want 0", s.RowStyle()(1)) + } + + if s.Row(0)[0] != "John Doe" { + t.Errorf("Row(0)[0] = %s; want John Doe", s.Row(0)[0]) + } + + if s.Row(0)[1] != 30 { + t.Errorf("Row(0)[1] = %d; want 30", s.Row(0)[1]) + } +} diff --git a/stream.go b/stream.go index f6bae75..77ca1c1 100644 --- a/stream.go +++ b/stream.go @@ -7,10 +7,10 @@ import ( ) // SetRow sets a single row. -func SetRow(writer *excelize.StreamWriter, colID, rowID int, data []any, styleSetter StyleSetter) error { +func SetRow(writer *excelize.StreamWriter, colID, rowID int, data []any, getStyle GetStyleByColIDFn) error { row := make([]any, len(data)) for i, value := range data { - row[i] = excelize.Cell{Value: value, StyleID: styleSetter(i)} + row[i] = excelize.Cell{Value: value, StyleID: getStyle(i)} } cell, err := excelize.CoordinatesToCellName(colID, rowID)