feat: Add row reading and dimension retrieval functionalities for Excel sheets
- Implemented `excel.sheet.rows` to read specified rows from a sheet with pagination support. - Added `excel.sheet.dimension` to retrieve the number of rows and columns in a sheet. - Enhanced the Excel processing logic to handle new functionalities and updated related tests. - Updated README with examples for the new features.
This commit is contained in:
parent
aca3014167
commit
1cce634d24
5 changed files with 730 additions and 25 deletions
|
|
@ -142,6 +142,37 @@ Process("excel.sheet.delete", h, "Sheet1Copy");
|
|||
const exists: boolean = Process("excel.sheet.exists", h, "Sheet1");
|
||||
```
|
||||
|
||||
#### Read sheet rows with pagination
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* Reads rows from a sheet with pagination support
|
||||
* @param handle - Handle ID from excel.open
|
||||
* @param name - Sheet name
|
||||
* @param start - Starting row index (0-based)
|
||||
* @param size - Number of rows to read
|
||||
* @returns string[][] - Two-dimensional array of cell values
|
||||
*/
|
||||
const rows: string[][] = Process("excel.sheet.rows", h, "Sheet1", 0, 10); // Read first 10 rows
|
||||
```
|
||||
|
||||
#### Get sheet dimensions
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* Gets the dimensions (number of rows and columns) of a sheet
|
||||
* @param handle - Handle ID from excel.open
|
||||
* @param name - Sheet name
|
||||
* @returns {rows: number, cols: number} - Object containing row and column counts
|
||||
*/
|
||||
const dim: { rows: number; cols: number } = Process(
|
||||
"excel.sheet.dimension",
|
||||
h,
|
||||
"Sheet1"
|
||||
);
|
||||
console.log(`Sheet has ${dim.rows} rows and ${dim.cols} columns`);
|
||||
```
|
||||
|
||||
### Example: Sheet Operations Workflow
|
||||
|
||||
```typescript
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ func init() {
|
|||
"sheet.copy": processCopySheet,
|
||||
"sheet.list": processListSheets,
|
||||
"sheet.exists": processSheetExists,
|
||||
"sheet.rows": processReadSheetRows,
|
||||
"sheet.dimension": processGetSheetDimension,
|
||||
|
||||
"read.cell": processReadCell,
|
||||
"read.row": processReadRow,
|
||||
|
|
@ -736,3 +738,45 @@ func processSheetExists(process *process.Process) interface{} {
|
|||
|
||||
return xls.SheetExists(name)
|
||||
}
|
||||
|
||||
// processReadSheetRows process the excel.sheet.rows <handle> <name> <start> <size>
|
||||
func processReadSheetRows(process *process.Process) interface{} {
|
||||
process.ValidateArgNums(4)
|
||||
handle := process.ArgsString(0)
|
||||
name := process.ArgsString(1)
|
||||
start := process.ArgsInt(2)
|
||||
size := process.ArgsInt(3)
|
||||
|
||||
xls, err := Get(handle)
|
||||
if err != nil {
|
||||
exception.New("excel.sheet.rows %s error: %s", 500, handle, err.Error()).Throw()
|
||||
}
|
||||
|
||||
data, err := xls.ReadSheetRows(name, start, size)
|
||||
if err != nil {
|
||||
exception.New("excel.sheet.rows %s:%s error: %s", 500, handle, name, err.Error()).Throw()
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// processGetSheetDimension process the excel.sheet.dimension <handle> <name>
|
||||
func processGetSheetDimension(process *process.Process) interface{} {
|
||||
process.ValidateArgNums(2)
|
||||
handle := process.ArgsString(0)
|
||||
name := process.ArgsString(1)
|
||||
|
||||
xls, err := Get(handle)
|
||||
if err != nil {
|
||||
exception.New("excel.sheet.dimension %s error: %s", 500, handle, err.Error()).Throw()
|
||||
}
|
||||
|
||||
rows, cols, err := xls.GetSheetDimension(name)
|
||||
if err != nil {
|
||||
exception.New("excel.sheet.dimension %s:%s error: %s", 500, handle, name, err.Error()).Throw()
|
||||
}
|
||||
|
||||
return map[string]int{
|
||||
"rows": rows,
|
||||
"cols": cols,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package excel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
|
@ -531,36 +532,38 @@ func TestProcessWriteOperations(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Test write row
|
||||
p, err = process.Of("excel.write.row", handle, "供销存管理表格", "A1", []interface{}{"Test1", "Test2", "Test3"})
|
||||
// Create test data
|
||||
testData := make([]interface{}, 0)
|
||||
testData = append(testData, []interface{}{"Header1", "Header2", "Header3"})
|
||||
testData = append(testData, []interface{}{1, "Row1", true})
|
||||
testData = append(testData, []interface{}{2, "Row2", false})
|
||||
testData = append(testData, []interface{}{3, "Row3", true})
|
||||
testData = append(testData, []interface{}{4, "Row4", false})
|
||||
testData = append(testData, []interface{}{5, "Row5", true})
|
||||
testData = append(testData, []interface{}{6, "Row6", false})
|
||||
testData = append(testData, []interface{}{7, "Row7", true})
|
||||
testData = append(testData, []interface{}{8, "Row8", false})
|
||||
testData = append(testData, []interface{}{9, "Row9", true})
|
||||
|
||||
// Create a new sheet and write test data
|
||||
p, err = process.Of("excel.sheet.create", handle, "RowTestSheet")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = p.Exec()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = p.Exec()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test write column
|
||||
p, err = process.Of("excel.write.column", handle, "供销存管理表格", "B1", []interface{}{"Col1", "Col2", "Col3"})
|
||||
p, err = process.Of("excel.write.all", handle, "RowTestSheet", "A1", testData)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = p.Exec()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test write all
|
||||
p, err = process.Of("excel.write.all", handle, "供销存管理表格", "C1", [][]interface{}{
|
||||
{"All1", "All2", "All3"},
|
||||
{"All4", "All5", "All6"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = p.Exec()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Save and close
|
||||
p, err = process.Of("excel.save", handle)
|
||||
if err != nil {
|
||||
|
|
@ -916,3 +919,320 @@ func TestProcessSheetOperations(t *testing.T) {
|
|||
_, err = p.Exec()
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestProcessReadSheetRows(t *testing.T) {
|
||||
test.Prepare(t, config.Conf)
|
||||
defer test.Clean()
|
||||
|
||||
files := testFiles(t)
|
||||
|
||||
// Create a new test file path
|
||||
dataRoot := config.Conf.DataRoot
|
||||
newFile := filepath.Join(filepath.Dir(files["test-01"]), "test-read-rows.xlsx")
|
||||
|
||||
// Copy test-01.xlsx to new file
|
||||
content, err := os.ReadFile(filepath.Join(dataRoot, files["test-01"]))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = os.WriteFile(filepath.Join(dataRoot, newFile), content, 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(filepath.Join(dataRoot, newFile)) // Clean up after test
|
||||
|
||||
// Open file in write mode
|
||||
p, err := process.Of("excel.open", newFile, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
handle, err := p.Exec()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Create test data
|
||||
testData := make([]interface{}, 0)
|
||||
testData = append(testData, []interface{}{"Header1", "Header2", "Header3"})
|
||||
testData = append(testData, []interface{}{1, "Row1", true})
|
||||
testData = append(testData, []interface{}{2, "Row2", false})
|
||||
testData = append(testData, []interface{}{3, "Row3", true})
|
||||
testData = append(testData, []interface{}{4, "Row4", false})
|
||||
testData = append(testData, []interface{}{5, "Row5", true})
|
||||
testData = append(testData, []interface{}{6, "Row6", false})
|
||||
testData = append(testData, []interface{}{7, "Row7", true})
|
||||
testData = append(testData, []interface{}{8, "Row8", false})
|
||||
testData = append(testData, []interface{}{9, "Row9", true})
|
||||
|
||||
// Create a new sheet and write test data
|
||||
p, err = process.Of("excel.sheet.create", handle, "RowTestSheet")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = p.Exec()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p, err = process.Of("excel.write.all", handle, "RowTestSheet", "A1", testData)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = p.Exec()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Save and close the file
|
||||
p, err = process.Of("excel.save", handle)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = p.Exec()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
p, err = process.Of("excel.close", handle)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = p.Exec()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Reopen the file for reading
|
||||
p, err = process.Of("excel.open", newFile, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
handle, err = p.Exec()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Test cases for reading rows
|
||||
t.Run("ReadFromMiddle", func(t *testing.T) {
|
||||
p, err := process.Of("excel.sheet.rows", handle, "RowTestSheet", 2, 4)
|
||||
assert.NoError(t, err)
|
||||
data, err := p.Exec()
|
||||
assert.NoError(t, err)
|
||||
rows := data.([][]string)
|
||||
assert.Equal(t, 4, len(rows))
|
||||
assert.Equal(t, "2", rows[0][0]) // First row should be row 2
|
||||
})
|
||||
|
||||
t.Run("ReadFromBeginning", func(t *testing.T) {
|
||||
p, err := process.Of("excel.sheet.rows", handle, "RowTestSheet", 0, 3)
|
||||
assert.NoError(t, err)
|
||||
data, err := p.Exec()
|
||||
assert.NoError(t, err)
|
||||
rows := data.([][]string)
|
||||
assert.Equal(t, 3, len(rows))
|
||||
assert.Equal(t, "Header1", rows[0][0]) // First row should be header
|
||||
})
|
||||
|
||||
t.Run("ReadBeyondAvailable", func(t *testing.T) {
|
||||
p, err := process.Of("excel.sheet.rows", handle, "RowTestSheet", 8, 5)
|
||||
assert.NoError(t, err)
|
||||
data, err := p.Exec()
|
||||
assert.NoError(t, err)
|
||||
rows := data.([][]string)
|
||||
assert.Equal(t, 2, len(rows)) // Only 2 rows remain
|
||||
})
|
||||
|
||||
t.Run("ReadNonExistentSheet", func(t *testing.T) {
|
||||
p, err := process.Of("excel.sheet.rows", handle, "NonExistentSheet", 0, 5)
|
||||
assert.NoError(t, err)
|
||||
_, err = p.Exec()
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("ReadWithSizeZero", func(t *testing.T) {
|
||||
p, err := process.Of("excel.sheet.rows", handle, "RowTestSheet", 0, 0)
|
||||
assert.NoError(t, err)
|
||||
data, err := p.Exec()
|
||||
assert.NoError(t, err)
|
||||
rows := data.([][]string)
|
||||
assert.Equal(t, 0, len(rows))
|
||||
})
|
||||
|
||||
t.Run("ReadWithNegativeStart", func(t *testing.T) {
|
||||
p, err := process.Of("excel.sheet.rows", handle, "RowTestSheet", -1, 5)
|
||||
assert.NoError(t, err)
|
||||
_, err = p.Exec()
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("ReadWithNegativeSize", func(t *testing.T) {
|
||||
p, err := process.Of("excel.sheet.rows", handle, "RowTestSheet", 0, -1)
|
||||
assert.NoError(t, err)
|
||||
_, err = p.Exec()
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
// Save and close
|
||||
p, err = process.Of("excel.save", handle)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = p.Exec()
|
||||
assert.NoError(t, err)
|
||||
|
||||
p, err = process.Of("excel.close", handle)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = p.Exec()
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestProcessGetSheetDimension(t *testing.T) {
|
||||
test.Prepare(t, config.Conf)
|
||||
defer test.Clean()
|
||||
|
||||
files := testFiles(t)
|
||||
|
||||
// Create a new test file path
|
||||
dataRoot := config.Conf.DataRoot
|
||||
newFile := filepath.Join(filepath.Dir(files["test-01"]), "test-dimension.xlsx")
|
||||
|
||||
// Copy test-01.xlsx to new file
|
||||
content, err := os.ReadFile(filepath.Join(dataRoot, files["test-01"]))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = os.WriteFile(filepath.Join(dataRoot, newFile), content, 0644)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(filepath.Join(dataRoot, newFile)) // Clean up after test
|
||||
|
||||
// Open file in write mode
|
||||
p, err := process.Of("excel.open", newFile, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
handle, err := p.Exec()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Test 1: Empty sheet
|
||||
t.Run("EmptySheet", func(t *testing.T) {
|
||||
// Create empty sheet
|
||||
p, err := process.Of("excel.sheet.create", handle, "EmptySheet")
|
||||
assert.NoError(t, err)
|
||||
_, err = p.Exec()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Get dimensions
|
||||
p, err = process.Of("excel.sheet.dimension", handle, "EmptySheet")
|
||||
assert.NoError(t, err)
|
||||
dim, err := p.Exec()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify dimensions
|
||||
dimMap := dim.(map[string]int)
|
||||
assert.Equal(t, 0, dimMap["rows"])
|
||||
assert.Equal(t, 0, dimMap["cols"])
|
||||
})
|
||||
|
||||
// Test 2: Sheet with data
|
||||
t.Run("SheetWithData", func(t *testing.T) {
|
||||
|
||||
// Create test data
|
||||
testData := make([]interface{}, 0)
|
||||
testData = append(testData, []interface{}{"A1", "B1", "C1"})
|
||||
testData = append(testData, []interface{}{"A2", "B2", "C2"})
|
||||
testData = append(testData, []interface{}{"A3", "B3", "C3"})
|
||||
|
||||
// Create and write to sheet
|
||||
p, err := process.Of("excel.sheet.create", handle, "DataSheet")
|
||||
assert.NoError(t, err)
|
||||
_, err = p.Exec()
|
||||
assert.NoError(t, err)
|
||||
|
||||
p, err = process.Of("excel.write.all", handle, "DataSheet", "A1", testData)
|
||||
assert.NoError(t, err)
|
||||
_, err = p.Exec()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Save to ensure dimensions are updated
|
||||
p, err = process.Of("excel.save", handle)
|
||||
assert.NoError(t, err)
|
||||
_, err = p.Exec()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Get dimensions
|
||||
p, err = process.Of("excel.sheet.dimension", handle, "DataSheet")
|
||||
assert.NoError(t, err)
|
||||
dim, err := p.Exec()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify dimensions
|
||||
dimMap := dim.(map[string]int)
|
||||
assert.Equal(t, 3, dimMap["rows"])
|
||||
assert.Equal(t, 3, dimMap["cols"])
|
||||
})
|
||||
|
||||
// Test 3: Non-existent sheet
|
||||
t.Run("NonExistentSheet", func(t *testing.T) {
|
||||
p, err := process.Of("excel.sheet.dimension", handle, "NonExistentSheet")
|
||||
assert.NoError(t, err)
|
||||
_, err = p.Exec()
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
// Test 4: Large sheet
|
||||
t.Run("LargeSheet", func(t *testing.T) {
|
||||
// Create large test data (100x50)
|
||||
largeData := make([]interface{}, 0)
|
||||
for i := 0; i < 100; i++ {
|
||||
row := make([]interface{}, 50)
|
||||
for j := 0; j < 50; j++ {
|
||||
row[j] = fmt.Sprintf("Cell_%d_%d", i, j)
|
||||
}
|
||||
largeData = append(largeData, row)
|
||||
}
|
||||
|
||||
// Create and write to sheet
|
||||
p, err := process.Of("excel.sheet.create", handle, "LargeSheet")
|
||||
assert.NoError(t, err)
|
||||
_, err = p.Exec()
|
||||
assert.NoError(t, err)
|
||||
|
||||
p, err = process.Of("excel.write.all", handle, "LargeSheet", "A1", largeData)
|
||||
assert.NoError(t, err)
|
||||
_, err = p.Exec()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Save to ensure dimensions are updated
|
||||
p, err = process.Of("excel.save", handle)
|
||||
assert.NoError(t, err)
|
||||
_, err = p.Exec()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Get dimensions
|
||||
p, err = process.Of("excel.sheet.dimension", handle, "LargeSheet")
|
||||
assert.NoError(t, err)
|
||||
dim, err := p.Exec()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify dimensions
|
||||
dimMap := dim.(map[string]int)
|
||||
assert.Equal(t, 100, dimMap["rows"])
|
||||
assert.Equal(t, 50, dimMap["cols"])
|
||||
})
|
||||
|
||||
// Clean up
|
||||
p, err = process.Of("excel.close", handle)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = p.Exec()
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
|
|
|||
145
excel/sheet.go
145
excel/sheet.go
|
|
@ -76,6 +76,151 @@ func (excel *Excel) ReadSheet(name string) ([][]interface{}, error) {
|
|||
return result, nil
|
||||
}
|
||||
|
||||
// GetSheetDimension returns the number of rows and columns in a sheet
|
||||
func (excel *Excel) GetSheetDimension(name string) (rows int, cols int, err error) {
|
||||
// Check if sheet exists
|
||||
if idx, _ := excel.GetSheetIndex(name); idx == -1 {
|
||||
return 0, 0, fmt.Errorf("sheet %s does not exist", name)
|
||||
}
|
||||
rows = 0
|
||||
cols = 0
|
||||
ri, err := excel.File.Rows(name)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
defer ri.Close()
|
||||
for ri.Next() {
|
||||
rows++
|
||||
row, err := ri.Columns()
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
if len(row) > cols {
|
||||
cols = max(cols, len(row))
|
||||
}
|
||||
}
|
||||
return rows, cols, nil
|
||||
|
||||
// // Get dimension directly using excelize API
|
||||
// dimension, err := excel.File.GetSheetDimension(name)
|
||||
// if err != nil {
|
||||
// return 0, 0, err
|
||||
// }
|
||||
|
||||
// // If sheet is not set, use row and column count
|
||||
// if dimension == "A1" {
|
||||
// rows = 0
|
||||
// cols = 0
|
||||
// ri, err := excel.File.Rows(name)
|
||||
// if err != nil {
|
||||
// return 0, 0, err
|
||||
// }
|
||||
// defer ri.Close()
|
||||
// for ri.Next() {
|
||||
// rows++
|
||||
// row, err := ri.Columns()
|
||||
// if err != nil {
|
||||
// return 0, 0, err
|
||||
// }
|
||||
// if len(row) > cols {
|
||||
// cols = max(cols, len(row))
|
||||
// }
|
||||
// }
|
||||
// return rows, cols, nil
|
||||
// }
|
||||
|
||||
// // If sheet is empty
|
||||
// if dimension == "" || dimension == "A1" {
|
||||
// return 0, 0, nil
|
||||
// }
|
||||
|
||||
// // For single cell case, add the range suffix
|
||||
// if !strings.Contains(dimension, ":") {
|
||||
// dimension = fmt.Sprintf("%s:%s", dimension, dimension)
|
||||
// }
|
||||
|
||||
// // Split dimension into start and end coordinates
|
||||
// parts := strings.Split(dimension, ":")
|
||||
// if len(parts) != 2 {
|
||||
// return 0, 0, fmt.Errorf("invalid dimension format: %s", dimension)
|
||||
// }
|
||||
|
||||
// // Convert end coordinate to row and column numbers
|
||||
// endCol, endRow, err := excelize.CellNameToCoordinates(parts[1])
|
||||
// if err != nil {
|
||||
// return 0, 0, err
|
||||
// }
|
||||
|
||||
// // Convert start coordinate to row and column numbers
|
||||
// startCol, startRow, err := excelize.CellNameToCoordinates(parts[0])
|
||||
// if err != nil {
|
||||
// return 0, 0, err
|
||||
// }
|
||||
|
||||
// // Calculate actual dimensions
|
||||
// rows = endRow - startRow + 1
|
||||
// cols = endCol - startCol + 1
|
||||
|
||||
// return rows, cols, nil
|
||||
}
|
||||
|
||||
// ReadSheetRows reads all data from a sheet by rows
|
||||
func (excel *Excel) ReadSheetRows(name string, start int, size int) ([][]string, error) {
|
||||
// Validate parameters
|
||||
if start < 0 {
|
||||
return nil, fmt.Errorf("start position cannot be negative")
|
||||
}
|
||||
if size < 0 {
|
||||
return nil, fmt.Errorf("size cannot be negative")
|
||||
}
|
||||
|
||||
// Check if sheet exists
|
||||
if idx, _ := excel.GetSheetIndex(name); idx == -1 {
|
||||
return nil, fmt.Errorf("sheet %s does not exist", name)
|
||||
}
|
||||
|
||||
// If size is 0, return empty slice
|
||||
if size == 0 {
|
||||
return [][]string{}, nil
|
||||
}
|
||||
|
||||
// Get rows iterator
|
||||
rows, err := excel.File.Rows(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
// Skip to start position
|
||||
currentRow := -1
|
||||
for rows.Next() {
|
||||
currentRow++
|
||||
if currentRow >= start {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Read requested number of rows
|
||||
result := make([][]string, 0, size)
|
||||
if currentRow == start {
|
||||
row, err := rows.Columns()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, row)
|
||||
}
|
||||
|
||||
for i := 1; i < size && rows.Next(); i++ {
|
||||
row, err := rows.Columns()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result = append(result, row)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// UpdateSheet updates an existing sheet with new data
|
||||
// If the sheet doesn't exist, it will be created
|
||||
func (excel *Excel) UpdateSheet(name string, data [][]interface{}) error {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package excel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -131,4 +133,167 @@ func TestSheetOperations(t *testing.T) {
|
|||
err = excel.DeleteSheet("NonExistentSheet")
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
// Test ReadSheetRows
|
||||
t.Run("ReadSheetRows", func(t *testing.T) {
|
||||
// Create test data with 10 rows
|
||||
testData := [][]interface{}{
|
||||
{"Header1", "Header2", "Header3"},
|
||||
{1, "Row1", true},
|
||||
{2, "Row2", false},
|
||||
{3, "Row3", true},
|
||||
{4, "Row4", false},
|
||||
{5, "Row5", true},
|
||||
{6, "Row6", false},
|
||||
{7, "Row7", true},
|
||||
{8, "Row8", false},
|
||||
{9, "Row9", true},
|
||||
}
|
||||
|
||||
// Expected string data
|
||||
expectedData := [][]string{
|
||||
{"Header1", "Header2", "Header3"},
|
||||
{"1", "Row1", "TRUE"},
|
||||
{"2", "Row2", "FALSE"},
|
||||
{"3", "Row3", "TRUE"},
|
||||
{"4", "Row4", "FALSE"},
|
||||
{"5", "Row5", "TRUE"},
|
||||
{"6", "Row6", "FALSE"},
|
||||
{"7", "Row7", "TRUE"},
|
||||
{"8", "Row8", "FALSE"},
|
||||
{"9", "Row9", "TRUE"},
|
||||
}
|
||||
|
||||
// Create a new sheet for testing
|
||||
_, err := excel.CreateSheet("RowTestSheet")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Write test data
|
||||
err = excel.WriteAll("RowTestSheet", "A1", testData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test 1: Read from middle (start at row 2, read 4 rows)
|
||||
data, err := excel.ReadSheetRows("RowTestSheet", 2, 4)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 4, len(data))
|
||||
assert.Equal(t, expectedData[2:6], data)
|
||||
|
||||
// Test 2: Read from beginning (start at row 0, read 3 rows)
|
||||
data, err = excel.ReadSheetRows("RowTestSheet", 0, 3)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(data))
|
||||
assert.Equal(t, expectedData[0:3], data)
|
||||
|
||||
// Test 3: Read beyond available rows (should return remaining rows)
|
||||
data, err = excel.ReadSheetRows("RowTestSheet", 8, 5)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 2, len(data)) // Only 2 rows remain
|
||||
assert.Equal(t, expectedData[8:], data)
|
||||
|
||||
// Test 4: Read from non-existent sheet
|
||||
_, err = excel.ReadSheetRows("NonExistentSheet", 0, 5)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Test 5: Read with size 0 (should return empty slice)
|
||||
data, err = excel.ReadSheetRows("RowTestSheet", 0, 0)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, len(data))
|
||||
|
||||
// Test 6: Read with negative start (should return error)
|
||||
_, err = excel.ReadSheetRows("RowTestSheet", -1, 5)
|
||||
assert.Error(t, err)
|
||||
|
||||
// Test 7: Read with negative size (should return error)
|
||||
_, err = excel.ReadSheetRows("RowTestSheet", 0, -1)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
// Test GetSheetDimension
|
||||
t.Run("GetSheetDimension", TestGetSheetDimension)
|
||||
}
|
||||
|
||||
// TestGetSheetDimension tests the GetSheetDimension function
|
||||
func TestGetSheetDimension(t *testing.T) {
|
||||
// Get test files and open the test file
|
||||
files := testFiles(t)
|
||||
filename := filepath.Dir(files["test-01"]) + "/test-dimension.xlsx"
|
||||
handler, err := Open(filename, true) // Open in writable mode
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer Close(handler)
|
||||
|
||||
excel, err := Get(handler)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Clean up existing sheets
|
||||
sheets := excel.ListSheets()
|
||||
for _, sheet := range sheets {
|
||||
if sheet != "Sheet1" { // Keep the default sheet
|
||||
err = excel.DeleteSheet(sheet)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test 1: Create a large sheet (100x100)
|
||||
_, err = excel.CreateSheet("LargeSheet")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create test data (100x100)
|
||||
largeData := make([][]interface{}, 100)
|
||||
for i := 0; i < 100; i++ {
|
||||
largeData[i] = make([]interface{}, 100)
|
||||
for j := 0; j < 100; j++ {
|
||||
largeData[i][j] = fmt.Sprintf("Cell_%d_%d", i, j)
|
||||
}
|
||||
}
|
||||
err = excel.WriteAll("LargeSheet", "A1", largeData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Save file to ensure dimensions are updated
|
||||
err = excel.Save()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test large sheet dimensions
|
||||
rows, cols, err := excel.GetSheetDimension("LargeSheet")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 100, rows)
|
||||
assert.Equal(t, 100, cols)
|
||||
|
||||
// Test 2: Empty sheet
|
||||
if excel.SheetExists("EmptySheet") {
|
||||
excel.DeleteSheet("EmptySheet")
|
||||
}
|
||||
_, err = excel.CreateSheet("EmptySheet")
|
||||
assert.NoError(t, err)
|
||||
rows, cols, err = excel.GetSheetDimension("EmptySheet")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, rows)
|
||||
assert.Equal(t, 0, cols)
|
||||
|
||||
// Test 3: Regular sheet with data
|
||||
testData := [][]interface{}{
|
||||
{"A1", "B1", "C1"},
|
||||
{"A2", "B2", "C2"},
|
||||
{"A3", "B3", "C3"},
|
||||
}
|
||||
err = excel.WriteAll("RegularSheet", "A1", testData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Save file to ensure dimensions are updated
|
||||
err = excel.Save()
|
||||
assert.NoError(t, err)
|
||||
|
||||
rows, cols, err = excel.GetSheetDimension("RegularSheet")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, rows)
|
||||
assert.Equal(t, 3, cols)
|
||||
|
||||
// Test 4: Non-existent sheet
|
||||
rows, cols, err = excel.GetSheetDimension("NonExistentSheet")
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, 0, rows)
|
||||
assert.Equal(t, 0, cols)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue