1package headless23import (4 "fmt"5 "io"6 "strings"7)89// WriteDescription writes d as text: a line for each sheet saying what10// it holds, with what's on it indented below, then the named ranges.11func WriteDescription(w io.Writer, d Description) error {12 var b strings.Builder13 for _, s := range d.Sheets {14 describeSheetText(&b, s)15 }16 if len(d.Names) > 0 {17 b.WriteString("names\n")18 for _, n := range d.Names {19 fmt.Fprintf(&b, " %s %s\n", n.Name, n.Range)20 }21 }22 _, err := io.WriteString(w, b.String())23 return err24}2526func describeSheetText(b *strings.Builder, s SheetDescription) {27 var tags []string28 if s.Shown {29 tags = append(tags, "shown")30 }31 if s.Hidden {32 tags = append(tags, "hidden")33 }34 if s.Kind != "sheet" {35 tags = append(tags, s.Kind)36 }37 b.WriteString(s.Name)38 if len(tags) > 0 {39 b.WriteString(" (" + strings.Join(tags, ", ") + ")")40 }41 switch {42 case s.Kind == "notebook":43 fmt.Fprintf(b, " %d %s\n", len(s.Notebook), plural(len(s.Notebook), "cell", "cells"))44 case s.Used == "":45 b.WriteString(" empty\n")46 default:47 fmt.Fprintf(b, " %s, %d %s x %d %s, %d %s%s\n", s.Used, s.Rows, plural(s.Rows, "row", "rows"), s.Cols,48 plural(s.Cols, "column", "columns"), s.Cells, plural(s.Cells, "cell", "cells"), formulaCount(s))49 }50 describeParts(b, s)51}5253// formulaCount says how many formulas there are and how many show54// errors.55func formulaCount(s SheetDescription) string {56 if s.Formulas <= 0 {57 return ""58 }59 out := fmt.Sprintf(", %d %s", s.Formulas, plural(s.Formulas, "formula", "formulas"))60 if s.Errors > 0 {61 out += fmt.Sprintf(" (%d showing errors)", s.Errors)62 }63 return out64}6566// describeParts writes what's on a sheet, a line each.67func describeParts(b *strings.Builder, s SheetDescription) {68 if s.Header > 0 {69 fmt.Fprintf(b, " header row %d: %s\n", s.Header, strings.Join(s.Columns, ", "))70 }71 if s.Frozen != [2]int{} {72 fmt.Fprintf(b, " frozen %d %s, %d %s\n", s.Frozen[0], plural(s.Frozen[0], "row", "rows"), s.Frozen[1], plural(s.Frozen[1], "column", "columns"))73 }74 if s.Filter != "" {75 fmt.Fprintf(b, " filter %s\n", s.Filter)76 }77 if s.Pivot != nil {78 fmt.Fprintf(b, " pivot table of %s\n", s.Pivot.Source)79 }80 for _, t := range s.Tables {81 fmt.Fprintf(b, " table %s %s %s\n", t.Name, t.Range, strings.Join(t.Columns, ", "))82 }83 for _, r := range s.Regions {84 switch r.Kind {85 case "linked":86 fmt.Fprintf(b, " linked %s %s following %s\n", r.Name, r.Range, r.File)87 case "source":88 fmt.Fprintf(b, " source %s %s reading %s in place\n", r.Name, r.Range, r.File)89 default:90 fmt.Fprintf(b, " output %s %s\n", r.Name, r.Range)91 }92 }93 for _, c := range s.Charts {94 fmt.Fprintf(b, " chart %d %s %s of %s at %s\n", c.Number, c.Title, c.Type, c.Data, c.At)95 }96 for _, c := range s.Notebook {97 line := []rune(strings.SplitN(c.Source, "\n", 2)[0])98 if len(line) > 60 {99 line = append(line[:57], []rune("...")...)100 }101 state := ""102 switch {103 case c.Error != "":104 state = " (failed: " + c.Error + ")"105 case c.State != "":106 state = " (" + c.State + ")"107 }108 fmt.Fprintf(b, " %d %s %s%s\n", c.Number, c.Kind, string(line), state)109 }110}