package anyxcelize import ( "strings" "strconv" "math" "fmt" ) func (f *File) formattedValueGeneric(s int, v string, raw bool) (any, error) { // fmt.Println("---") if raw { fmt.Println("fvg: raw"); return v, nil } // s is the style reference if s == 0 { fmt.Println("fvg: s==0"); return v, nil } styleSheet, err := f.stylesReader() if err != nil { fmt.Println("fvg: styleSheet err") return v, err } if styleSheet.CellXfs == nil { fmt.Println("fvg: CellXfs nil") return v, err } // TODO: what does this mean? if s >= len(styleSheet.CellXfs.Xf) { // fmt.Println("fvg: s >= len(...Xf)") return v, err } var numFmtID int if styleSheet.CellXfs.Xf[s].NumFmtID != nil { numFmtID = *styleSheet.CellXfs.Xf[s].NumFmtID } // Use this! // fmt.Printf("xf[s].NumFmtID = %v\n", numFmtID) date1904 := false wb, err := f.workbookReader() if err != nil { // fmt.Println("fvg: wb err") return v, err } if wb != nil && wb.WorkbookPr != nil { date1904 = wb.WorkbookPr.Date1904 } // NOTE: we get the formatting fn in ok, and // call it with v, the numFmt, and date1904. // We need to modify the functions that are // possible to be ok, then, to return any. // Only has up to ~60, so won't call on 167 (date), 164, 166 we have. if ok := builtInNumFmtFuncGen[numFmtID]; ok != nil { // fmt.Println("fvg: ok := ...") // ok() is now a func(v, format string, date1904 bool) any !! return ok(v, builtInNumFmt[numFmtID], date1904), err } // This is what actually gets called, most of the time. // This means NumFmts are NOT nil, so they exist, so // does this mean we can do simple ParseFloat? if styleSheet.NumFmts != nil { // fmt.Printf("fvg: NumFmts != nil on (%s)\n", v) // return v, err } // If the above fails, we want this to work and return a correct // value. for _, xlsxFmt := range styleSheet.NumFmts.NumFmt { if xlsxFmt.NumFmtID == numFmtID { // fmt.Printf("Matched on numFmtID %v; date1904 %v format code %v\n", numFmtID, date1904, xlsxFmt.FormatCode) return formatGeneric(v, xlsxFmt.FormatCode, date1904), err } } return v, err } // builtInNumFmtFunc defined the format conversion functions map. Partial format // code doesn't support currently and will return original string. var builtInNumFmtFuncGen = map[int]func(v, format string, date1904 bool) any{ 0: formatGeneric, 1: formatToIntGeneric, // noted; yet to implement 2: formatToFloatGeneric,// noted; yet to implement 3: formatToIntSeparatorGeneric,// noted; yet to implement 4: formatToFloatGeneric,// noted; yet to implement 9: formatToCGeneric, 10: formatToDGeneric, 11: formatToEGeneric, 12: formatGeneric, // Doesn't support currently 13: formatGeneric, // Doesn't support currently 14: formatGeneric, 15: formatGeneric, 16: formatGeneric, 17: formatGeneric, 18: formatGeneric, 19: formatGeneric, 20: formatGeneric, 21: formatGeneric, 22: formatGeneric, 37: formatToAGeneric, 38: formatToAGeneric, 39: formatToBGeneric, 40: formatToBGeneric, 41: formatGeneric, // Doesn't support currently 42: formatGeneric, // Doesn't support currently 43: formatGeneric, // Doesn't support currently 44: formatGeneric, // Doesn't support currently 45: formatGeneric, 46: formatGeneric, 47: formatGeneric, 48: formatToEGeneric, 49: formatGeneric, } // OnesGeneric to worry about implementing: // formatToint, formatToFloat, formatToIntSeparator, formatTo{A,B,C,D,E} // Return any or int? func formatToIntGeneric(v, format string, date1904 bool) any { // Copied from original. Not sure what this does, probably don't want // this to be the case. TODO: FIX if strings.Contains(v, "_") { magicerr := -420691111 fmt.Printf("formatToIntGeneric: v %v, format %v, date1904 %v: error %v\n", v, format, date1904, magicerr) return magicerr } f, err := strconv.ParseFloat(v, 64) if err != nil { magicerr := -420691112 fmt.Printf("formatToIntGeneric: v %v, format %v, date1904 %v: error %v\n", v, format, date1904, magicerr) return magicerr } // ERROR! return math.Round(f) // That should do us! } func formatToFloatGeneric(v, format string, date1904 bool) any { if strings.Contains(v, "_") { return v } f, err := strconv.ParseFloat(v, 64) if err != nil { magicerr := -420691113 fmt.Printf("formatToFloatGeneric: v %v, format %v, date1904 %v: error %v\n", v, format, date1904, magicerr) return magicerr } return f } func formatToIntSeparatorGeneric(v, format string, date1904 bool) any { if strings.Contains(v, "_") { magicerr := -420691114 fmt.Printf("formatToIntSeparatorGeneric: v %v, format %v, date1904 %v: error %v\n", v, format, date1904, magicerr) return magicerr } f, err := strconv.ParseFloat(v, 64) if err != nil { magicerr := -420691115 fmt.Printf("formatToIntSeparatorGeneric: v %v, format %v, date1904 %v: error %v\n", v, format, date1904, magicerr) return magicerr } return math.Round(f) } // Normally this would format as (100), 100 for -100, 100. This just returns // an int here func formatToAGeneric(v, format string, date1904 bool) any{ return formatToIntGeneric(v, format, date1904) } // The float equivalent of formatToA. Here just format as float! func formatToBGeneric(v, format string, date1904 bool) any { return formatToFloatGeneric(v, format, date1904) } // Normally formats as percent. For now just keep it as is, AIEL pcts should // be in 10.00 format anyways not 10%. If it is, eh. For now, just format as // float, possibly better choices exist. func formatToCGeneric(v, format string, date1904 bool) any { return formatToFloatGeneric(v, format, date1904) } // percents with 00.00% not 00%. Same as above, just do floats func formatToDGeneric(v, format string, date1904 bool) any { return formatToFloatGeneric(v, format, date1904) } // based on %.2E, exponent? Yep, scientific notation. For this, I guess it is // for floats, probably not integrals. Return as float. func formatToEGeneric(v, format string, date1904 bool) any { if strings.Contains(v, "_") { fmt.Printf("formatToEGeneric: v %s, format %s, date1904 %s: error -420691116\n", v, format, date1904) return -420691116 } f, err := strconv.ParseFloat(v, 64) if err != nil { fmt.Printf("formatToEGeneric: v %s, format %s, date1904 %s: error -420691116\n", v, format, date1904) return -420691117 } return f } func formatGeneric(v, format string, date1904 bool) any { // Here we enumerate the possible format strings, and give either // time.Time or int returns for them. switch format { case "\"TRUE\";\"TRUE\";\"FALSE\"": if v == "0" { return false } else if v == "1" { return true } else { // Error case. fmt.Println("f in chat: %v cannot be parsed bool with %v", v, format) } case "General", "#,##0", "#,##0.00", "0", "0%", "0.00%", "0.00e+00": return parseIOrF64(v) case "d\\-mmm\\-yy", "[$-809]dd/mm/yy", "[$-809]mm/dd/yy", "mm/dd/yy", "mm-dd-yy", "d-mmm-yy", "d-mmm", "mmm-yy", "h:mm am/pm", "h:mm:ss am/pm", "hh:mm", "hh:mm:ss", "m/d/y hh:mm": // If it's an integer, it's a complete date. We don't really // need to care about preserving time, which is the fractional // component: yet! TODO: care about the fractional component, // but for now let's just parse as is. // We just need to convert Excel integer time to our format. xlTime, err := strconv.ParseFloat(v, 64) if err != nil { fmt.Printf("formatGeneric: f in chat for %v, which can't be parsed into float in %v case statement; %v error", v, format, err) panic("panic! formatGeneric") // TODO: handle this error better. } f := timeFromExcelTime(xlTime, date1904) return f default: // Default: just parse integer or float! Though this is not a // perfect solution. fmt.Println("Ok, running default case") return parseIOrF64(v) } return -420699999 } /* pure */ func parseIOrF64(v string) any { gv, err := strconv.ParseInt(v, 10, 64) if err != nil { fv, err := strconv.ParseFloat(v, 64) if err != nil { // What do? fmt.Printf("formatGeneric: general format cannot parse as int or float [%v]\n", v) return "parseIOrF64 fail" } return fv } return gv } // NOTE // This is re-writing these as these are functions that read into strings for // reading. I am not sure if they are used for writing, but if so eh. Probably // not as writing can manage with the actual types.