From 14b01de47aa024ac1408e7a7e071af9d59826032 Mon Sep 17 00:00:00 2001 From: Thomas Voss Date: Fri, 9 Aug 2024 18:21:36 +0200 Subject: Implement a formatter for mintage files --- cmd/mfmt/main.go | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 cmd/mfmt/main.go (limited to 'cmd') diff --git a/cmd/mfmt/main.go b/cmd/mfmt/main.go new file mode 100644 index 0000000..c0d2b92 --- /dev/null +++ b/cmd/mfmt/main.go @@ -0,0 +1,175 @@ +package main + +import ( + "fmt" + "io" + "os" + "path/filepath" + "strconv" + "strings" + "unsafe" + + "git.thomasvoss.com/euro-cash.eu/mintages" +) + +const cols = unsafe.Sizeof(mintages.Row{}.Cols) / + unsafe.Sizeof(mintages.Row{}.Cols[0]) + +var rv int + +func main() { + if len(os.Args) == 1 { + if err := mfmt("-", os.Stdin, os.Stdout); err != nil { + warn(err) + } + } + for _, arg := range os.Args[1:] { + f, err := os.OpenFile(arg, os.O_RDWR, 0) + if err != nil { + warn(err) + continue + } + defer f.Close() + if err := mfmt(arg, f, f); err != nil { + warn(err) + } + } + os.Exit(rv) +} + +func mfmt(path string, in io.Reader, out io.Writer) error { + data, err := mintages.Parse(in, path) + if err != nil { + return err + } + + f, outfile := out.(*os.File) + if outfile { + _, err := f.Seek(0, io.SeekStart) + if err != nil { + return err + } + } + + fmt.Fprintf(out, `BEGIN %d`, data.StartYear) + if len(data.Circ) != 0 { + fmt.Fprintln(out, "\n\nBEGIN CIRC") + formatSection(out, data.Circ) + } + if len(data.BU) != 0 { + fmt.Fprintln(out, "\n\nBEGIN BU") + formatSection(out, data.BU) + } + if len(data.Proof) != 0 { + fmt.Fprintln(out, "\n\nBEGIN PROOF") + formatSection(out, data.Proof) + } + + if outfile { + if off, err := f.Seek(0, io.SeekCurrent); err != nil { + return err + } else if err = f.Truncate(off); err != nil { + return err + } + } + return nil +} + +func formatSection(out io.Writer, rows []mintages.Row) { + var ( + year string + longestMM int + longestNum [cols]int + ) + + for _, row := range rows { + s, mm, ok := strings.Cut(row.Label, "\u00A0") + if ok { + n := len(mm) + if s != year { + n++ + } + longestMM = max(longestMM, n) + } + year = s + for i, col := range row.Cols { + longestNum[i] = max(longestNum[i], intlen(col)) + } + } + + extraSpace := 0 + if longestMM != 0 { + extraSpace = 2 + } + + year = "" + + for i, row := range rows { + var label string + + s, mm, ok := strings.Cut(row.Label, "\u00A0") + if ok { + if s != year { + label = mm + "*: " + } else { + label = mm + ": " + } + } + year = s + + fmt.Fprintf(out, "%-*s", longestMM+extraSpace, label) + for j, n := range row.Cols { + if j != 0 { + fmt.Fprintf(out, " ") + } + fmt.Fprintf(out, "%*s", longestNum[j], formatInt(n)) + } + + if i != len(rows)-1 { + fmt.Fprintln(out, "") + } + } +} + +func formatInt(n int) string { + if n < -1 { + panic("mintage count is negative and not -1") + } else if n == -1 { + return "?" + } + + in := strconv.Itoa(n) + dots := (len(in) - 1) / 3 + out := make([]byte, len(in)+dots) + + for i, j, k := len(in)-1, len(out)-1, 0; ; i, j = i-1, j-1 { + out[j] = in[i] + if i == 0 { + return string(out) + } + if k++; k == 3 { + j, k = j-1, 0 + out[j] = '.' + } + } +} + +func intlen(v int) int { + switch { + case v < -1: + panic("mintage count is negative and not -1") + case v == 0, v == -1: + return 1 + default: + n := 0 + for x := v; x != 0; x /= 10 { + n++ + } + return n + (n-1)/3 /* Thousands separators */ + } +} + +func warn(e error) { + fmt.Fprintf(os.Stderr, "%s: %+v\n", filepath.Base(os.Args[0]), e) + rv = 1 +} -- cgit v1.2.3