cat / 012
clone

a spreadsheet for the terminal: lotus 1-2-3 looks, google sheets keys and formulas, one go binary

Register each file format's importer and exporter in the kinds table

Dami committed b5ee00d
Import and Export dispatched through switches that had to be kept in
step with the table; now a format is one row and one file. The API used
by the UI and cmd/012 is unchanged.
commit
b5ee00dd062dc2d9079704ab1e0648f79302acc8
parent
ad230063629c3a4588b4b9ff9101c7fd4cb5a90b
author
Dami <dami.etoile@gmail.com>
6 files changed, +81 -53
Minternal/fileio/csv.go16+++++
Minternal/fileio/fileio.go104++++++++++++++++--------------
Minternal/fileio/parquet.go3+-
Minternal/fileio/sqlite.go3+-
Minternal/fileio/wk1.go3+-
Minternal/fileio/xlsx.go5+-
M internal/fileio/csv.go +16 -0
M internal/fileio/fileio.go +56 -48
@@ -31,19 +31,45 @@ const (
3131 WK13232 )3333 34-var kinds = []struct {35- kind Kind36- name string // short name, e.g. in "Export as CSV"37- label string // what the file is, for the import picker38- exts []string // recognized extensions, the first one written39- export bool40-}{41- {CSV, "CSV", "Comma-separated values", []string{".csv"}, true},42- {TSV, "TSV", "Tab-separated values", []string{".tsv", ".tab"}, true},43- {XLSX, "XLSX", "Excel workbook", []string{".xlsx", ".xlsm"}, true},44- {SQLite, "SQLite", "SQLite database", []string{".sqlite", ".sqlite3", ".db"}, true},45- {Parquet, "Parquet", "Parquet file", []string{".parquet"}, false},46- {WK1, "WK1", "Lotus 1-2-3 worksheet", []string{".wk1", ".wks"}, false},34+// A fileFormat is everything about a Kind: its names, its extensions, and35+// the functions reading and writing it. Adding a format is a file with36+// its importer (and exporter) and a row here.37+type fileFormat struct {38+ kind Kind39+ name string // short name, e.g. in "Export as CSV"40+ label string // what the file is, for the import picker41+ exts []string // recognized extensions, the first one written42+ // book is set for formats that hold named sheets of their own; a43+ // file of any other kind names its one sheet after the file.44+ book bool45+ read importer46+ // write is nil for formats 012 only imports.47+ write exporter48+}49+50+// An importer reads a file into a new workbook, returning the sheet to51+// show. It checks ctx between rows, and updates opt.Progress.52+type importer func(ctx context.Context, name string, opt Options) (*Result, error)53+54+// An exporter writes a snapshot to a file.55+type exporter func(ctx context.Context, name string, snap *Snapshot, opt ExportOptions) (*ExportResult, error)56+57+// kinds are the formats, in Kind order, which is menu order.58+var kinds = []fileFormat{59+ {CSV, "CSV", "Comma-separated values", []string{".csv"}, false, importCSV, exportCSV},60+ {TSV, "TSV", "Tab-separated values", []string{".tsv", ".tab"}, false, importTSV, exportTSV},61+ {XLSX, "XLSX", "Excel workbook", []string{".xlsx", ".xlsm"}, true, importXLSX, exportXLSX},62+ {SQLite, "SQLite", "SQLite database", []string{".sqlite", ".sqlite3", ".db"}, false, importSQLite, exportSQLite},63+ {Parquet, "Parquet", "Parquet file", []string{".parquet"}, false, importParquet, nil},64+ {WK1, "WK1", "Lotus 1-2-3 worksheet", []string{".wk1", ".wks"}, false, importWK1, nil},65+}66+67+// format returns the kind's format, or nil for an unknown kind.68+func (k Kind) format() *fileFormat {69+ if k < 1 || int(k) > len(kinds) {70+ return nil71+ }72+ return &kinds[k-1]4773 }4874 4975 // Kinds lists every format, in menu order.
@@ -57,31 +83,32 @@ func Kinds() []Kind {
5783 5884 // String is the format's short name, e.g. "XLSX".5985 func (k Kind) String() string {60- if k < 1 || int(k) > len(kinds) {61- return "unknown"86+ if f := k.format(); f != nil {87+ return f.name6288 }63- return kinds[k-1].name89+ return "unknown"6490 }6591 6692 // Label says what a file of this kind is, e.g. "Excel workbook".6793 func (k Kind) Label() string {68- if k < 1 || int(k) > len(kinds) {69- return ""94+ if f := k.format(); f != nil {95+ return f.label7096 }71- return kinds[k-1].label97+ return ""7298 }7399 74100 // Ext is the extension written for this kind, e.g. ".xlsx".75101 func (k Kind) Ext() string {76- if k < 1 || int(k) > len(kinds) {77- return ""102+ if f := k.format(); f != nil {103+ return f.exts[0]78104 }79- return kinds[k-1].exts[0]105+ return ""80106 }81107 82108 // CanExport reports whether sheets can be written in this format.83109 func (k Kind) CanExport() bool {84- return k >= 1 && int(k) <= len(kinds) && kinds[k-1].export110+ f := k.format()111+ return f != nil && f.write != nil85112 }86113 87114 // KindOf recognizes a file's format by its extension.
@@ -193,27 +220,13 @@ func Import(ctx context.Context, name string, opt Options) (*Result, error) {
193220 }194221 195222 func importKind(ctx context.Context, name string, k Kind, opt Options) (*Result, error) {196- var (197- r *Result198- err error199- )200- switch k {201- case CSV, TSV:202- r, err = importDelimited(ctx, name, k, opt.Progress)203- case XLSX:204- r, err = importXLSX(ctx, name, opt.Progress)205- case SQLite:206- r, err = importSQLite(ctx, name, opt)207- case Parquet:208- r, err = importParquet(ctx, name, opt.Progress)209- case WK1:210- r, err = importWK1(ctx, name, opt.Progress)211- }223+ f := k.format()224+ r, err := f.read(ctx, name, opt)212225 if err != nil {213226 return nil, err214227 }215228 // A file of one table becomes one sheet named after it, as in Sheets.216- if book := r.Sheet.Book(); k != XLSX && book.Len() == 1 {229+ if book := r.Sheet.Book(); !f.book && book.Len() == 1 {217230 base := strings.TrimSuffix(filepath.Base(name), filepath.Ext(name))218231 if opt.Table != "" {219232 base = opt.Table
@@ -255,15 +268,10 @@ func Export(ctx context.Context, name string, k Kind, snap *Snapshot, opt Export
255268 }256269 257270 func exportKind(ctx context.Context, name string, k Kind, snap *Snapshot, opt ExportOptions) (*ExportResult, error) {258- switch k {259- case CSV, TSV:260- return exportDelimited(name, k, snap)261- case XLSX:262- return exportXLSX(name, snap)263- case SQLite:264- return exportSQLite(ctx, name, snap, opt.Table)271+ if !k.CanExport() {272+ return nil, fmt.Errorf("can't export %s files", k)265273 }266- return nil, fmt.Errorf("can't export %s files", k)274+ return k.format().write(ctx, name, snap, opt)267275 }268276 269277 // fileSize is the size of the file name, for telemetry; -1 if unknown.
M internal/fileio/parquet.go +2 -1
M internal/fileio/sqlite.go +2 -1
M internal/fileio/wk1.go +2 -1
M internal/fileio/xlsx.go +3 -2