1package headless23import (4 "errors"5 "fmt"67 "src.rest/cat/012/internal/sheet"8)910// Entry is one cell to set: a reference to one cell, and what to type11// in it, as the file stores entries (numbers and dates in en-US's form,12// formulas with commas, whatever the workbook's locale), or a typed13// Value; "" clears it. Format, a number format code, formats the cell,14// or with neither input nor value every cell of a range.15type Entry struct {16 Ref string `json:"ref" jsonschema:"one cell, as formulas write it: B7, Q3!B7, 'Q3 plan'!B7; a range when only format is given"`17 Input string `json:"input,omitempty" jsonschema:"what to type, as a person types it in en-US form: =SUM(A1:A6), $1,200.50, 12%, 2026-09-29, '00123 for text; empty clears the cell"`18 Value Value `json:"value,omitempty" jsonschema:"instead of input, the value with its type: 3.5, \"00123\" (text), true, null (clears), {\"currency\": 3.5}, {\"percent\": 0.12}, {\"date\": \"2026-09-29\"}, {\"time\": \"14:30\"}, {\"duration\": \"90min\"}, {\"size\": \"1.5kb\"}, {\"number\": 1234.5, \"decimals\": 2}"`19 Format string `json:"format,omitempty" jsonschema:"a number format code for the cell, or alone for every cell of ref: $#,##0.00, 0.0%, yyyy-mm-dd, #,##0"`20}2122// SetOptions tune Set.23type SetOptions struct {24 // Force sets cells in protected ranges, which are refused otherwise,25 // as the screen asks before editing them.26 Force bool27}2829// Set types each entry into its cell, in order, as one change. An entry30// a cell can't take (a formula that doesn't parse, a validation rule31// that rejects it, part of an array, a pivot table or a region, a32// protected range without Force) stops it with an error naming the33// cell, and the workbook should then be thrown away rather than saved.34// Entries a rule would only mark invalid are set, and returned as35// warnings.36func Set(w *sheet.Workbook, entries []Entry, o SetOptions) (warnings []string, err error) {37 err = w.Batch(sheet.Change{Label: "set"}, func() error {38 for _, e := range entries {39 warn, err := setOne(w, e, o)40 if err != nil {41 return err42 }43 if warn != "" {44 warnings = append(warnings, warn)45 }46 }47 return nil48 })49 return warnings, err50}5152// typedEntry is what e types: its input, or its value's, and the53// format it sets.54func typedEntry(e Entry) (typed, error) {55 t := typed{input: e.Input}56 if e.Value != nil {57 if e.Input != "" {58 return t, errors.New("give input or value, not both")59 }60 var err error61 if t, err = parseValue(e.Value); err != nil {62 return t, err63 }64 }65 if e.Format != "" {66 t.format, t.formatted = FormatOfCode(e.Format), true67 }68 return t, nil69}7071func setOne(w *sheet.Workbook, e Entry, o SetOptions) (string, error) {72 if e.Format != "" && e.Input == "" && e.Value == nil {73 return "", formatRange(w, e.Ref, FormatOfCode(e.Format), o)74 }75 s, a, err := ResolveCell(w, e.Ref)76 if err != nil {77 return "", err78 }79 t, err := typedEntry(e)80 if err != nil {81 return "", fmt.Errorf("%s: %w", sheet.Qualified(s.Name(), sheet.Rect{From: a, To: a}), err)82 }83 return putTyped(s, a, t, o)84}8586// putTyped types t into the cell at a, checked as typing it is.87func putTyped(s *sheet.Sheet, a sheet.Addr, t typed, o SetOptions) (string, error) {88 cell := sheet.Rect{From: a, To: a}89 at := sheet.Qualified(s.Name(), cell)90 if p, ok := s.Protecting(cell); ok && !o.Force {91 return "", fmt.Errorf("%s is protected (%s): --force sets it anyway", at, p.Label())92 }93 warn := ""94 if bad := s.CheckEntry(a, t.input); bad != nil {95 if bad.Reject {96 return "", fmt.Errorf("%s: %s", at, bad.Help)97 }98 warn = at + ": " + bad.Help99 }100 if t.formatted && s.DisplayFormat(a).Kind == sheet.FmtText {101 s.SetFormat(cell, t.format) // a number typed into Plain text would stay text102 }103 if err := s.Set(a, t.input); err != nil {104 var pe *sheet.ParseError105 if errors.As(err, &pe) {106 return "", fmt.Errorf("%s: %s, at character %d of %s", at, pe.Msg, pe.Pos+1, t.input)107 }108 return "", fmt.Errorf("%s: %w", at, err)109 }110 if t.formatted {111 s.SetFormat(cell, t.format)112 }113 return warn, nil114}115116// formatRange gives every cell of the range ref names format f.117func formatRange(w *sheet.Workbook, ref string, f sheet.Format, o SetOptions) error {118 t, err := Resolve(w, ref)119 if err != nil {120 return err121 }122 r := targetRect(t)123 if p, ok := t.Sheet.Protecting(r); ok && !o.Force {124 return fmt.Errorf("%s is protected (%s): --force formats it anyway", t, p.Label())125 }126 t.Sheet.SetFormat(r, f)127 return nil128}