summary history files

vendor/github.com/olekukonko/tablewriter/csv.go
package tablewriter

import (
	"encoding/csv"
	"io"
	"os"
)

// NewCSV Start A new table by importing from a CSV file
// Takes io.Writer and csv File name
func NewCSV(writer io.Writer, fileName string, hasHeader bool, opts ...Option) (*Table, error) {
	// Open the CSV file
	file, err := os.Open(fileName)
	if err != nil {
		// Log implicitly handled by NewTable if logger is configured via opts
		return nil, err // Return nil *Table on error
	}
	defer file.Close() // Ensure file is closed

	// Create a CSV reader
	csvReader := csv.NewReader(file)

	// Delegate to NewCSVReader, passing through the options
	return NewCSVReader(writer, csvReader, hasHeader, opts...)
}

// NewCSVReader Start a New Table Writer with csv.Reader
// This enables customisation such as reader.Comma = ';'
// See http://golang.org/src/pkg/encoding/csv/reader.go?s=3213:3671#L94
func NewCSVReader(writer io.Writer, csvReader *csv.Reader, hasHeader bool, opts ...Option) (*Table, error) {
	// Create a new table instance using the modern API and provided options.
	// Options configure the table's appearance and behavior (renderer, borders, etc.).
	t := NewTable(writer, opts...) // Logger setup happens here if WithLogger/WithDebug is passed

	// Process header row if specified
	if hasHeader {
		headers, err := csvReader.Read()
		if err != nil {
			// Handle EOF specifically: means the CSV was empty or contained only an empty header line.
			if err == io.EOF {
				t.logger.Debug("NewCSVReader: CSV empty or only header found (EOF after header read attempt).")
				// Return the table configured by opts, but without data/header.
				// It's ready for Render() which will likely output nothing or just borders if configured.
				return t, nil
			}
			// Log other read errors
			t.logger.Errorf("NewCSVReader: Error reading CSV header: %v", err)
			return nil, err // Return nil *Table on critical read error
		}

		// Check if the read header is genuinely empty (e.g., a blank line in the CSV)
		isEmptyHeader := true
		for _, h := range headers {
			if h != "" {
				isEmptyHeader = false
				break
			}
		}

		if !isEmptyHeader {
			t.Header(headers) // Use the Table method to set the header data
			t.logger.Debugf("NewCSVReader: Header set from CSV: %v", headers)
		} else {
			t.logger.Debug("NewCSVReader: Read an empty header line, skipping setting table header.")
		}
	}

	// Process data rows
	rowCount := 0
	for {
		record, err := csvReader.Read()
		if err == io.EOF {
			break // Reached the end of the CSV data
		}
		if err != nil {
			// Log other read errors during data processing
			t.logger.Errorf("NewCSVReader: Error reading CSV record: %v", err)
			return nil, err // Return nil *Table on critical read error
		}

		// Append the record to the table's internal buffer (for batch rendering).
		// The Table.Append method handles conversion and storage.
		if appendErr := t.Append(record); appendErr != nil {
			t.logger.Errorf("NewCSVReader: Error appending record #%d: %v", rowCount+1, appendErr)
			// Decide if append error is fatal. For now, let's treat it as fatal.
			return nil, appendErr
		}
		rowCount++
	}
	t.logger.Debugf("NewCSVReader: Finished reading CSV. Appended %d data rows.", rowCount)

	// Return the configured and populated table instance, ready for Render() call.
	return t, nil
}