package main import ( "archive/zip" "fmt" "github.com/tealeg/xlsx/v3" "path/filepath" ) type FilePreparer interface { Prepare() ([]string, error) } type FileSource struct { FilePath string } type DirectoryFilePackage struct { FileSource } type ReturnLine struct { Sheet string CellRef string Value string } type Return struct { Name string ReturnLines []ReturnLine } type ZipFilePackage struct { FileSource } // NewReturnLine creates a new ReturnLine object func NewReturnLine(sheet, cellRef, value string) (*ReturnLine, error) { if err := validateInputs(sheet, cellRef, value); err != nil { return nil, err } if !validateSpreadsheetCell(cellRef) { return nil, fmt.Errorf("cellRef must be A1 format") } return &ReturnLine{ Sheet: sheet, CellRef: cellRef, Value: value, }, nil } func validateInputs(sheet, cellRef, value string) error { if sheet == "" { return fmt.Errorf("sheet parameter is required") } if cellRef == "" { return fmt.Errorf("cellRef parameter is required") } if value == "" { return fmt.Errorf("value parameter is required") } return nil } func NewReturn(name string, dm *Datamap, returnLines []ReturnLine) (*Return, error) { if len(returnLines) == 0 { return nil, fmt.Errorf("ReturnLines must contain at least one ReturnLine") } return &Return{ Name: name, ReturnLines: returnLines, }, nil } func ParseXLSX(filePath string, dm *Datamap) (*Return, error) { // Use tealeg/xlsx to parse the Excel file wb, err := xlsx.OpenFile(filePath) if err != nil { return nil, err } // Get the set of sheets from the Datamap sheets := GetSheetsFromDM(*dm) // Loop through all DatamapLines returnLines := []ReturnLine{} for _, dml := range dm.DMLs { // Check if the sheet for this DatamapLine is in the set of sheets if !contains(sheets, dml.Sheet) { continue } sh, ok := wb.Sheet[dml.Sheet] if !ok { return nil, fmt.Errorf("sheet %s not found in Excel file", dml.Sheet) } col, row, err := xlsx.GetCoordsFromCellIDString(dml.CellRef) if err != nil { return nil, err } cell, err := sh.Cell(row, col) if err != nil { return nil, err } returnLines = append(returnLines, ReturnLine{ Sheet: dml.Sheet, CellRef: dml.CellRef, Value: cell.Value, // or cell.FormattedValue() if you need formatted values }) } // Here we create a new Return object with the name of the Excel file and the ReturnLines slice // that we just populated rtn, err := NewReturn(filepath.Base(filePath), dm, returnLines) if err != nil { return nil, err } return rtn, nil } func contains(slice []string, str string) bool { for _, s := range slice { if s == str { return true } } return false } func PrepareFiles(fp FilePreparer) ([]string, error) { ch := make(chan string, 100) go func() { defer close(ch) files, err := fp.Prepare() if err != nil { ch <- err.Error() } for _, f := range files { ch <- f } }() var files []string for f := range ch { files = append(files, f) } return files, nil } func (fp *DirectoryFilePackage) Prepare() ([]string, error) { files, err := filepath.Glob(fp.FilePath + "/*") if err != nil { return nil, err } return files, nil } func (fp *ZipFilePackage) Prepare() ([]string, error) { files, err := zip.OpenReader(fp.FilePath) if err != nil { return nil, err } defer files.Close() out := []string{} for _, file := range files.File { out = append(out, file.Name) } return out, nil } // NewDirectoryFilePackage creates a new DirectoryFilePackage object with the given filePath to the directory func NewDirectoryFilePackage(filePath string) *DirectoryFilePackage { return &DirectoryFilePackage{FileSource{FilePath: filePath}} } // NewZipFilePackage creates a new ZipFilePackage object with the given filePath to the zip file func NewZipFilePackage(filePath string) *ZipFilePackage { return &ZipFilePackage{FileSource{FilePath: filePath}} }