cat / 012
clone

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

main
012 / internal / sheet / sourcefile.go
sourcefile.go 3.3K
1package sheet23import (4 "errors"5 "fmt"6)78// A linked source in the file (source.go): its tab is a sheet whose one9// region is paged, at A1, with no cells. The region keeps what the10// source reads and how its tab sorts and filters it, never its rows:11//12// "regions": [13// {"name":"sales","at":"A1","path":"sales.parquet","paged":true,14// "order":{"sort":[{"column":"C","desc":true}],"filter":[{"column":"B","condition":"eq","value":"North"}]}}15// ]16//17// Sources need version 7: a build without them would read the region18// as a linked file and load the source into cells.1920// fileSourceOrder is a source's SourceOrder, columns by letter.21type fileSourceOrder struct {22 Sort []fileSourceSort `json:"sort,omitempty"`23 Filter []fileSourceFilter `json:"filter,omitempty"`24}2526type fileSourceSort struct {27 Column string `json:"column"`28 Desc bool `json:"desc,omitempty"`29}3031type fileSourceFilter struct {32 Column string `json:"column"`33 Condition string `json:"condition"`34 Value string `json:"value,omitempty"`35}3637// encodeOrder is o as the file keeps it, nil for the source's own.38func encodeOrder(o *SourceOrder) *fileSourceOrder {39 if o.IsZero() {40 return nil41 }42 fo := &fileSourceOrder{}43 for _, s := range o.Sort {44 fo.Sort = append(fo.Sort, fileSourceSort{Column: ColName(s.Col), Desc: s.Desc})45 }46 for _, f := range o.Filter {47 fo.Filter = append(fo.Filter, fileSourceFilter{Column: ColName(f.Col), Condition: f.Cond.Op.String(), Value: f.Cond.Arg})48 }49 return fo50}5152// decodeOrder reads what encodeOrder wrote.53func decodeOrder(fo *fileSourceOrder) (*SourceOrder, error) {54 if fo == nil {55 return nil, nil56 }57 o := &SourceOrder{}58 for _, s := range fo.Sort {59 c, ok := ParseCol(s.Column)60 if !ok {61 return nil, fmt.Errorf("invalid sort column %q", s.Column)62 }63 o.Sort = append(o.Sort, SourceSort{Col: c, Desc: s.Desc})64 }65 for _, f := range fo.Filter {66 c, ok := ParseCol(f.Column)67 if !ok {68 return nil, fmt.Errorf("invalid filter column %q", f.Column)69 }70 op, ok := ParseCondOp(f.Condition)71 if !ok || op == CondNone {72 return nil, fmt.Errorf("invalid filter condition %q", f.Condition)73 }74 o.Filter = append(o.Filter, SourceFilter{Col: c, Cond: Condition{Op: op, Arg: f.Value}})75 }76 if o.IsZero() {77 return nil, nil78 }79 return o, nil80}8182// readSource reads a linked source's region, r as far as it's read.83func (s *Sheet) readSource(fr fileRegion, r Region) error {84 switch {85 case !fr.Paged:86 return errors.New("only a linked source has an order")87 case fr.Path == "" || fr.Output || fr.Command != "" || fr.Window != 0:88 return errors.New("a linked source reads a file, all of it")89 }90 o, err := decodeOrder(fr.Order)91 if err != nil {92 return err93 }94 r.File.Paged, r.File.Order = true, o95 s.regions.list = append(s.regions.list, r)96 return nil97}9899// errSourceTab is why a sheet can't be a source's tab as the file has100// it.101var errSourceTab = errors.New("a linked source's tab holds its source alone, at A1")102103// checkSourceTab checks a sheet read from a file with a paged region:104// the region is its only one, at A1, and it has no cells.105func (s *Sheet) checkSourceTab() error {106 if !s.IsSource() {107 return nil108 }109 if len(s.regions.list) != 1 || s.regions.list[0].At != (Addr{}) || s.cells.len() > 0 || s.regions.notebook {110 return errSourceTab111 }112 return nil113}