refactor(sheets): extract dimension span parsing
This commit is contained in:
parent
a394c1c7b0
commit
074c4a5042
85
internal/cmd/sheets_dimension_span.go
Normal file
85
internal/cmd/sheets_dimension_span.go
Normal file
@ -0,0 +1,85 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
columnsRangeRe = regexp.MustCompile(`^([A-Za-z]+)(?::([A-Za-z]+))?$`)
|
||||
rowsRangeRe = regexp.MustCompile(`^([0-9]+)(?::([0-9]+))?$`)
|
||||
)
|
||||
|
||||
type dimensionSpan struct {
|
||||
SheetName string
|
||||
StartIndex int64
|
||||
EndIndex int64
|
||||
}
|
||||
|
||||
func parseColumnsSpan(spec, label string) (dimensionSpan, error) {
|
||||
sheetName, part, err := splitA1Sheet(strings.TrimSpace(spec))
|
||||
if err != nil {
|
||||
return dimensionSpan{}, fmt.Errorf("parse %s range: %w", label, err)
|
||||
}
|
||||
part = strings.ReplaceAll(strings.TrimSpace(part), "$", "")
|
||||
m := columnsRangeRe.FindStringSubmatch(part)
|
||||
if m == nil {
|
||||
return dimensionSpan{}, fmt.Errorf("invalid %s range %q (expected A:C or Sheet!A:C)", label, spec)
|
||||
}
|
||||
|
||||
startCol, err := colLettersToIndex(m[1])
|
||||
if err != nil {
|
||||
return dimensionSpan{}, err
|
||||
}
|
||||
endCol := startCol
|
||||
if m[2] != "" {
|
||||
endCol, err = colLettersToIndex(m[2])
|
||||
if err != nil {
|
||||
return dimensionSpan{}, err
|
||||
}
|
||||
}
|
||||
if endCol < startCol {
|
||||
startCol, endCol = endCol, startCol
|
||||
}
|
||||
|
||||
return dimensionSpan{
|
||||
SheetName: sheetName,
|
||||
StartIndex: int64(startCol - 1),
|
||||
EndIndex: int64(endCol),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseRowsSpan(spec, label string) (dimensionSpan, error) {
|
||||
sheetName, part, err := splitA1Sheet(strings.TrimSpace(spec))
|
||||
if err != nil {
|
||||
return dimensionSpan{}, fmt.Errorf("parse %s range: %w", label, err)
|
||||
}
|
||||
part = strings.ReplaceAll(strings.TrimSpace(part), "$", "")
|
||||
m := rowsRangeRe.FindStringSubmatch(part)
|
||||
if m == nil {
|
||||
return dimensionSpan{}, fmt.Errorf("invalid %s range %q (expected 1:10 or Sheet!1:10)", label, spec)
|
||||
}
|
||||
|
||||
startRow, err := strconv.ParseInt(m[1], 10, 64)
|
||||
if err != nil || startRow <= 0 {
|
||||
return dimensionSpan{}, fmt.Errorf("invalid %s start row %q", label, m[1])
|
||||
}
|
||||
endRow := startRow
|
||||
if m[2] != "" {
|
||||
endRow, err = strconv.ParseInt(m[2], 10, 64)
|
||||
if err != nil || endRow <= 0 {
|
||||
return dimensionSpan{}, fmt.Errorf("invalid %s end row %q", label, m[2])
|
||||
}
|
||||
}
|
||||
if endRow < startRow {
|
||||
startRow, endRow = endRow, startRow
|
||||
}
|
||||
|
||||
return dimensionSpan{
|
||||
SheetName: sheetName,
|
||||
StartIndex: startRow - 1,
|
||||
EndIndex: endRow,
|
||||
}, nil
|
||||
}
|
||||
@ -3,18 +3,11 @@ package cmd
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/api/sheets/v4"
|
||||
)
|
||||
|
||||
var (
|
||||
columnsRangeRe = regexp.MustCompile(`^([A-Za-z]+)(?::([A-Za-z]+))?$`)
|
||||
rowsRangeRe = regexp.MustCompile(`^([0-9]+)(?::([0-9]+))?$`)
|
||||
)
|
||||
|
||||
type SheetsResizeColumnsCmd struct {
|
||||
SpreadsheetID string `arg:"" name:"spreadsheetId" help:"Spreadsheet ID"`
|
||||
Columns string `arg:"" name:"columns" help:"Columns range (eg. Sheet1!A:C)"`
|
||||
@ -174,75 +167,3 @@ func (c *SheetsResizeRowsCmd) Run(ctx context.Context, flags *RootFlags) error {
|
||||
}, text, nil
|
||||
})
|
||||
}
|
||||
|
||||
type dimensionSpan struct {
|
||||
SheetName string
|
||||
StartIndex int64
|
||||
EndIndex int64
|
||||
}
|
||||
|
||||
func parseColumnsSpan(spec, label string) (dimensionSpan, error) {
|
||||
sheetName, part, err := splitA1Sheet(strings.TrimSpace(spec))
|
||||
if err != nil {
|
||||
return dimensionSpan{}, fmt.Errorf("parse %s range: %w", label, err)
|
||||
}
|
||||
part = strings.ReplaceAll(strings.TrimSpace(part), "$", "")
|
||||
m := columnsRangeRe.FindStringSubmatch(part)
|
||||
if m == nil {
|
||||
return dimensionSpan{}, fmt.Errorf("invalid %s range %q (expected A:C or Sheet!A:C)", label, spec)
|
||||
}
|
||||
|
||||
startCol, err := colLettersToIndex(m[1])
|
||||
if err != nil {
|
||||
return dimensionSpan{}, err
|
||||
}
|
||||
endCol := startCol
|
||||
if m[2] != "" {
|
||||
endCol, err = colLettersToIndex(m[2])
|
||||
if err != nil {
|
||||
return dimensionSpan{}, err
|
||||
}
|
||||
}
|
||||
if endCol < startCol {
|
||||
startCol, endCol = endCol, startCol
|
||||
}
|
||||
|
||||
return dimensionSpan{
|
||||
SheetName: sheetName,
|
||||
StartIndex: int64(startCol - 1),
|
||||
EndIndex: int64(endCol),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseRowsSpan(spec, label string) (dimensionSpan, error) {
|
||||
sheetName, part, err := splitA1Sheet(strings.TrimSpace(spec))
|
||||
if err != nil {
|
||||
return dimensionSpan{}, fmt.Errorf("parse %s range: %w", label, err)
|
||||
}
|
||||
part = strings.ReplaceAll(strings.TrimSpace(part), "$", "")
|
||||
m := rowsRangeRe.FindStringSubmatch(part)
|
||||
if m == nil {
|
||||
return dimensionSpan{}, fmt.Errorf("invalid %s range %q (expected 1:10 or Sheet!1:10)", label, spec)
|
||||
}
|
||||
|
||||
startRow, err := strconv.ParseInt(m[1], 10, 64)
|
||||
if err != nil || startRow <= 0 {
|
||||
return dimensionSpan{}, fmt.Errorf("invalid %s start row %q", label, m[1])
|
||||
}
|
||||
endRow := startRow
|
||||
if m[2] != "" {
|
||||
endRow, err = strconv.ParseInt(m[2], 10, 64)
|
||||
if err != nil || endRow <= 0 {
|
||||
return dimensionSpan{}, fmt.Errorf("invalid %s end row %q", label, m[2])
|
||||
}
|
||||
}
|
||||
if endRow < startRow {
|
||||
startRow, endRow = endRow, startRow
|
||||
}
|
||||
|
||||
return dimensionSpan{
|
||||
SheetName: sheetName,
|
||||
StartIndex: startRow - 1,
|
||||
EndIndex: endRow,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -99,3 +99,45 @@ func TestSheetsResizeCmds(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestParseColumnsSpan(t *testing.T) {
|
||||
t.Run("sheet range", func(t *testing.T) {
|
||||
span, err := parseColumnsSpan("Sheet 1!$C:$A", "columns")
|
||||
if err != nil {
|
||||
t.Fatalf("parseColumnsSpan: %v", err)
|
||||
}
|
||||
if span.SheetName != "Sheet 1" {
|
||||
t.Fatalf("SheetName=%q, want %q", span.SheetName, "Sheet 1")
|
||||
}
|
||||
if span.StartIndex != 0 || span.EndIndex != 3 {
|
||||
t.Fatalf("range=%d:%d, want 0:3", span.StartIndex, span.EndIndex)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid range", func(t *testing.T) {
|
||||
if _, err := parseColumnsSpan("Sheet1!1:3", "columns"); err == nil {
|
||||
t.Fatal("expected error for invalid column range")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestParseRowsSpan(t *testing.T) {
|
||||
t.Run("sheet range", func(t *testing.T) {
|
||||
span, err := parseRowsSpan("Sheet 1!$4:$2", "rows")
|
||||
if err != nil {
|
||||
t.Fatalf("parseRowsSpan: %v", err)
|
||||
}
|
||||
if span.SheetName != "Sheet 1" {
|
||||
t.Fatalf("SheetName=%q, want %q", span.SheetName, "Sheet 1")
|
||||
}
|
||||
if span.StartIndex != 1 || span.EndIndex != 4 {
|
||||
t.Fatalf("range=%d:%d, want 1:4", span.StartIndex, span.EndIndex)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid row", func(t *testing.T) {
|
||||
if _, err := parseRowsSpan("Sheet1!0:2", "rows"); err == nil {
|
||||
t.Fatal("expected error for invalid row range")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user