CARLOS rastrillo docs

🤖 table

amadan.net/rastrillo/rastrillo/table

Serve one table, a header and its rows, as a CSV or XLSX download. It also protects CSV exports against formula injection.

Serve

const (
	CSV  = "csv"
	XLSX = "xlsx"
)

type Export struct {
	Filename string
	Sheet    string
	Header   []string
	Rows     [][]string
}

func Serve(w http.ResponseWriter, format string, e Export) error

var ErrTooBigForXLSX = errors.New("table: too big for an XLSX workbook")
err := table.Serve(w, r.URL.Query().Get("format"), table.Export{
	Filename: "orders",
	Header:   []string{"Order", "Name", "Total"},
	Rows:     rows,
})

Serve sends e as a download in format. Pass table.XLSX for a workbook; anything else, including a value taken straight from a query string, gives CSV. The file is named Filename plus the extension, and the workbook's tab is Sheet, or Filename if that is empty.

If the table is too big for a workbook (more than 16,384 columns or 1,048,576 rows, or over 32,767 characters in one cell), Serve writes nothing and returns ErrTooBigForXLSX, so you can still answer, for example by offering CSV. Any other error means the client went away: log it.

Formula injection

func Guard(field string) string
func GuardRow(rec []string) []string

A CSV cell that starts with =, +, -, @, a tab or a carriage return runs as a formula when someone opens the file in a spreadsheet. A formula can send the row to another website, or run a command.

Serve puts every CSV cell, headers included, through Guard, which adds a leading apostrophe so the spreadsheet shows the text instead. A plain number like -50 is left alone. XLSX cells are never run as formulas, so they are not changed.

If you write CSV yourself, put every cell someone could have typed through Guard, or each row through GuardRow.

Read this page as markdown — exact, unstyled, and cheap for an agent to fetch.