package csv import ( "encoding/csv" "errors" "fmt" "net/http" "reflect" "strings" ) func WriteCSVtoHTTP(w http.ResponseWriter, csv string, filename string) { w.Header().Set("Content-Type", "text/csv") w.Header().Set("Content-Disposition", "attachment; filename="+filename+".csv") w.Write([]byte(csv)) } func MakeCSV(headers []string, records [][]string) (string, error) { if len(records) > 0 { if len(headers) != len(records[0]) { return "", errors.New("data contains more columns then specified by headers") } } // Write to CSV var csvBuilder strings.Builder writer := csv.NewWriter(&csvBuilder) // Write headers if err := writer.Write(headers); err != nil { return "", fmt.Errorf("failed to write headers: %v", err) } // Write records for _, record := range records { if err := writer.Write(record); err != nil { return "", fmt.Errorf("failed to write record: %v", err) } } writer.Flush() if err := writer.Error(); err != nil { return "", fmt.Errorf("csv writer error: %v", err) } return csvBuilder.String(), nil } func StructToCSV(data any) (string, error) { val := reflect.ValueOf(data) if val.Kind() != reflect.Slice { return "", fmt.Errorf("input must be a slice of structs") } if val.Len() == 0 { return "", nil } var headers []string var records [][]string // Get the first element to determine struct fields first := val.Index(0) if first.Kind() == reflect.Ptr { first = first.Elem() } if first.Kind() != reflect.Struct { return "", fmt.Errorf("slice elements must be structs or pointers to structs") } // Get field names as headers t := first.Type() for i := 0; i < t.NumField(); i++ { field := t.Field(i) // Use json tag if present, otherwise use field name tag := field.Tag.Get("json") if tag != "" && tag != "-" { headers = append(headers, tag) } else { headers = append(headers, field.Name) } } // Convert each struct to a record for i := 0; i < val.Len(); i++ { item := val.Index(i) if item.Kind() == reflect.Ptr { item = item.Elem() } var record []string for j := 0; j < item.NumField(); j++ { fieldVal := item.Field(j) record = append(record, fmt.Sprintf("%v", fieldVal.Interface())) } records = append(records, record) } // Write to CSV var csvBuilder strings.Builder writer := csv.NewWriter(&csvBuilder) // Write headers if err := writer.Write(headers); err != nil { return "", fmt.Errorf("failed to write headers: %v", err) } // Write records for _, record := range records { if err := writer.Write(record); err != nil { return "", fmt.Errorf("failed to write record: %v", err) } } writer.Flush() if err := writer.Error(); err != nil { return "", fmt.Errorf("csv writer error: %v", err) } return csvBuilder.String(), nil }