diff --git a/main.go b/main.go index 9ebba73..f335b5d 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "encoding/json" "flag" "fmt" @@ -15,6 +16,9 @@ import ( "time" "github.com/xuri/excelize/v2" + "golang.org/x/text/encoding/unicode" + "golang.org/x/text/transform" + "golang.org/x/text/unicode/norm" ) type Input struct { @@ -232,7 +236,7 @@ func AvgChart(f *excelize.File, worksheetName string, location string, avgCpuCol chartSeries = append(chartSeries, thisChartSeries) } - if err := f.AddChart("Report", location, &excelize.Chart{ + chart := excelize.Chart{ Type: excelize.Line, Series: chartSeries, Format: excelize.GraphicOptions{ @@ -278,14 +282,19 @@ func AvgChart(f *excelize.File, worksheetName string, location string, avgCpuCol Font: excelize.Font{ Color: "000000", }, - Maximum: &yMaxValue, - Minimum: &yMinValue, }, Dimension: excelize.ChartDimension{ Height: 500, Width: 800, }, - }); err != nil { + } + + if yMaxValue > 0 || yMinValue > 0 { + chart.YAxis.Maximum = &yMaxValue + chart.YAxis.Minimum = &yMinValue + } + + if err := f.AddChart("Report", location, &chart); err != nil { fmt.Printf("Error adding chart to workbook %s at location %s: %s\n", worksheetName, location, err) return } @@ -293,6 +302,7 @@ func AvgChart(f *excelize.File, worksheetName string, location string, avgCpuCol func main() { var err error + var data Input inputFile := flag.String("input", "input.json", "The filename from which to load historical data") outputFile := flag.String("output", "book1.xlsx", "The filename to use when writing excel workbook") @@ -322,9 +332,19 @@ func main() { } defer file.Close() - byteValue, _ := io.ReadAll(file) - var data Input - if err := json.Unmarshal(byteValue, &data); err != nil { + byteValue, err := io.ReadAll(file) + if err != nil { + log.Fatalf("Failed to read input json: %v", err) + } + + // Detect encoding and convert to UTF-8 if necessary + utf8Data, err := ensureUTF8(byteValue) + if err != nil { + fmt.Printf("Error ensuring UTF-8 encoding: %v\n", err) + return + } + + if err := json.Unmarshal(utf8Data, &data); err != nil { fmt.Printf("Error reading json input: %s\n", err) os.Exit(1) } @@ -366,6 +386,44 @@ func sortDates(dates []string) error { return nil } +// ensureUTF8 checks if the data is UTF-8, and if not, converts it to UTF-8. +// It also removes BOM from UTF-8 if present. +func ensureUTF8(data []byte) ([]byte, error) { + // Detect and strip UTF-8 BOM if present + if hasUTF8BOM(data) { + data = stripUTF8BOM(data) + } + + // If data is already UTF-8 (without BOM), return as is + if isUTF8(data) { + return data, nil + } + + // Detect and decode UTF-16 (either UTF-16LE or UTF-16BE) + decoder := unicode.UTF16(unicode.LittleEndian, unicode.UseBOM).NewDecoder() + utf8Data, _, err := transform.Bytes(decoder, data) + if err != nil { + return nil, fmt.Errorf("error converting to UTF-8: %v", err) + } + + return utf8Data, nil +} + +// isUTF8 checks if the byte slice is already encoded in UTF-8 +func isUTF8(data []byte) bool { + return bytes.Equal(data, norm.NFC.Bytes(data)) // UTF-8 normalization check +} + +// hasUTF8BOM checks if the data has a UTF-8 BOM +func hasUTF8BOM(data []byte) bool { + return len(data) >= 3 && data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF +} + +// stripUTF8BOM removes the UTF-8 BOM if it exists +func stripUTF8BOM(data []byte) []byte { + return data[3:] +} + func fetchValue(value interface{}) { switch value.(type) { case string: