From 792b2bd299b611e378679e5a269cc249e079c60e Mon Sep 17 00:00:00 2001 From: Thomas Voss Date: Mon, 12 Aug 2024 14:56:37 +0200 Subject: Use a new mintage format --- cmd/mfmt/main.go | 168 +++++++++++++++++---------------------- cmd/mfmt/main_test.go | 214 +++++++++++++++++++++++++++++--------------------- cmd/mfmt/util_test.go | 28 +++---- 3 files changed, 203 insertions(+), 207 deletions(-) (limited to 'cmd/mfmt') diff --git a/cmd/mfmt/main.go b/cmd/mfmt/main.go index 263b258..7f7ec8b 100644 --- a/cmd/mfmt/main.go +++ b/cmd/mfmt/main.go @@ -1,25 +1,35 @@ +/* Simple formatter for mintage files. This is not perfect and doesn’t + check for syntactic correctness, it’s just to get stuff aligned + nicely. Maybe in the future I will construct a military-grade + formatter. */ + package main import ( + "bufio" "fmt" "io" "os" "path/filepath" - "unsafe" + "regexp" + "strings" +) - "git.thomasvoss.com/euro-cash.eu/lib/mintage" +const ( + ws = " \t" + longestNum = len("1.000.000.000") ) -const cols = unsafe.Sizeof(mintage.Row{}.Cols) / - unsafe.Sizeof(mintage.Row{}.Cols[0]) +var ( + rv int -var rv int + reMintageYear = regexp.MustCompile(`^\d{4}(-[^ \t]+)?`) + reMintageRow = regexp.MustCompile(`^(([0-9.]+|\?)[ \t]+)*([0-9.]+|\?)$`) +) func main() { if len(os.Args) == 1 { - if err := mfmt("-", os.Stdin, os.Stdout); err != nil { - warn(err) - } + mfmt("-", os.Stdin, os.Stdout) } for _, arg := range os.Args[1:] { f, err := os.OpenFile(arg, os.O_RDWR, 0) @@ -28,117 +38,70 @@ func main() { continue } defer f.Close() - if err := mfmt(arg, f, f); err != nil { - warn(err) - } + mfmt(arg, f, f) } os.Exit(rv) } -func mfmt(path string, in io.Reader, out io.Writer) error { - data, err := mintage.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 +func mfmt(file string, r io.Reader, w io.Writer) { + scanner := bufio.NewScanner(r) + for linenr := 1; scanner.Scan(); linenr++ { + line := strings.Trim(scanner.Text(), ws) + + switch { + case len(line) == 0, line[0] == '#': + fmt.Fprintln(w, line) + case reMintageYear.MatchString(line): + fmtMintageYear(line, w) + case reMintageRow.MatchString(line): + fmtMintageRow(line, w) + default: + warn(fmt.Sprintf("%s:%d: potential syntax error", file, linenr)) + fmt.Fprintln(w, line) } } +} - fmt.Fprintf(out, `BEGIN %d`, data.StartYear) - for i, tbl := range data.Tables { - if len(tbl) != 0 { - var section string - switch mintage.CoinType(i) { - case mintage.TypeCirculated: - section = "CIRC" - case mintage.TypeNIFC: - section = "BU" - case mintage.TypeProof: - section = "PROOF" - } - fmt.Fprintf(out, "\n\nBEGIN %s\n", section) - formatSection(out, tbl) - } - } - fmt.Fprintln(out, "") - - if outfile { - if off, err := f.Seek(0, io.SeekCurrent); err != nil { - return err - } else if err = f.Truncate(off); err != nil { - return err - } +func fmtMintageYear(line string, w io.Writer) { + switch i := strings.IndexAny(line, ws); i { + case -1: + fmt.Fprintln(w, line) + default: + fmt.Fprintf(w, "%s %s\n", line[:i], strings.TrimLeft(line[i:], ws)) } - return nil } -func formatSection(out io.Writer, rows []mintage.Row) { - var ( - longestMM int - longestNum [cols]int - ) +func fmtMintageRow(line string, w io.Writer) { + tokens := strings.FieldsFunc(line, func(r rune) bool { + return strings.ContainsRune(ws, r) + }) - for i, row := range rows { - n := len(row.Mintmark) - if n != 0 && i != 0 && row.Year != rows[i-1].Year { - n++ - } - longestMM = max(longestMM, n) + for i, tok := range tokens { + s := formatMintage(tok) - for j, col := range row.Cols { - n := intlen(col) - longestNum[j] = max(longestNum[j], n+(n-1)/3) + if i == 0 { + fmt.Fprintf(w, "\t%*s", longestNum, s) + } else { + fmt.Fprintf(w, "%*s", longestNum+1, s) } } - extraSpace := 0 - if longestMM != 0 { - extraSpace = 2 - } - - for i, row := range rows { - var label string - if row.Mintmark != "" { - if i != 0 && row.Year != rows[i-1].Year { - label = row.Mintmark + "*: " - } else { - label = row.Mintmark + ": " - } - } - 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, "") - } - } + fmt.Fprintln(w) } -func formatInt(n int) string { - if n <= mintage.Invalid { - panic(fmt.Sprintf("invalid input %d", n)) - } else if n == mintage.Unknown { - return "?" +func formatMintage(s string) string { + if s == "?" { + return s } + n := atoiWithDots(s) digits := intlen(n) dots := (digits - 1) / 3 out := make([]byte, digits+dots) for i, j := len(out)-1, 0; ; i-- { out[i] = byte(n%10) + 48 - n /= 10 - if n == 0 { + if n /= 10; n == 0 { return string(out) } if j++; j == 3 { @@ -150,9 +113,7 @@ func formatInt(n int) string { func intlen(v int) int { switch { - case v <= mintage.Invalid: - panic("mintage count is negative and not -1") - case v == 0, v == mintage.Unknown: + case v == 0: return 1 default: n := 0 @@ -163,7 +124,18 @@ func intlen(v int) int { } } -func warn(e error) { +func atoiWithDots(s string) int { + n := 0 + for _, ch := range s { + if ch == '.' { + continue + } + n = n*10 + int(ch) - '0' + } + return n +} + +func warn(e any) { fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), e) rv = 1 } diff --git a/cmd/mfmt/main_test.go b/cmd/mfmt/main_test.go index 72e3665..7366ae0 100644 --- a/cmd/mfmt/main_test.go +++ b/cmd/mfmt/main_test.go @@ -6,103 +6,135 @@ import ( ) func runTest(t *testing.T, in, out string, nilErr bool) { - r := bytes.NewReader([]byte(in)) - w := new(bytes.Buffer) - - err := mfmt("-", r, w) - if nilErr && err != nil { - t.Fatalf(`Expected err=nil; got "%s"`, err) - } else if !nilErr && err == nil { - t.Fatalf(`Expected err=Error; got nil`) - } - - if nilErr && w.String() != out { - t.Fatalf(`Expected w.String()="%s"; got "%s"`, out, w.String()) - } -} - -func TestSpacing(t *testing.T) { - in := ` - BEGIN 2002 - BEGIN CIRC - - -378.383.000 326.439.000 217.031.000 441.621.000 203.388.000 169.088.000 223.544.000 196.438.000 - - - 10.817.950 118.526.000 ? 43.859.072 50.913.200 9.074.200 150.000 4.679.500 -` - out := `BEGIN 2002 - -BEGIN CIRC -378.383.000 326.439.000 217.031.000 441.621.000 203.388.000 169.088.000 223.544.000 196.438.000 - 10.817.950 118.526.000 ? 43.859.072 50.913.200 9.074.200 150.000 4.679.500 -` - runTest(t, in, out, true) -} - -func TestAddDots(t *testing.T) { - in := `BEGIN 2002 - -BEGIN CIRC -378383000 326439000 217031000 441621000 203388000 169088000 223544000 196438000 - 10817950 118526000 ? 43859072 50913200 9074200 150000 4679500 -` - out := `BEGIN 2002 - -BEGIN CIRC -378.383.000 326.439.000 217.031.000 441.621.000 203.388.000 169.088.000 223.544.000 196.438.000 - 10.817.950 118.526.000 ? 43.859.072 50.913.200 9.074.200 150.000 4.679.500 -` - runTest(t, in, out, true) } func TestComplete(t *testing.T) { in := ` - BEGIN 2002 - BEGIN CIRC - - -378383000 326439000 21703...1000 441621000 203388000 .1.6.9.0.8.8.0.0.0 223544000 196438000 - - - 10817950 118526000 ? 43859072 50913200 9074..200 150000 4679500 - - BEGIN PROOF - - -378383000 123456789 ..5 441621000 203388000 .1.6.9.0.8.8.0.0.0 223544000 196438000 - - - 10817950 118526000 ? 43859072 50913200 9074..200 150000 4679500 +2014 +60.000 60.000 860.000 860.000 860.000 340.000 511.843 360.000 +70.000 70.000 70.000 70.000 70.000 70.000 70.000 70.000 +3.000 3.000 3.000 3.000 3.000 3.000 3.000 3.000 + +# Ref: https://example.com +2015 + 0 0 0 0 0 0 0 1.072.400 +40.000 40.000 40.000 40.000 40.000 40.000 40.000 40.000 + ? ? ? ? ? ? ? ? + +2016 + 0 0 0 0 0 0 2.339.200 0 +35.000 35.000 35.000 35.000 35.000 35.000 35.000 35.000 + ? ? ? ? ? ? ? ? + +2017 +2.582.395 1.515.000 2.191.421 1.103.000 1.213.000 968.800 17.000 794.588 +22.000 22.000 22.000 22.000 22.000 22.000 22.000 22.000 + ? ? ? ? ? ? ? ? + +2018 +2.430.000 2.550.000 1.800.000 980.000 1.014.000 890.000 0 868.000 +20.000 20.000 20.000 20.000 20.000 20.000 20.000 20.000 + ? ? ? ? ? ? ? ? + +2019 +2.447.000 1.727.000 2.100.000 1.610.000 1.570.000 930.000 0 1.058.310 +15.000 15.000 15.000 15.000 15.000 15.000 15.000 15.000 + ? ? ? ? ? ? ? ? + +2020 + 0 0 0 860.000 175.000 740.000 0 1.500.000 +12.000 12.000 12.000 12.000 12.000 12.000 12.000 12.000 + ? ? ? ? ? ? ? ? + +2021 + 200.000 700.000 0 1.400.000 1.420.000 600.000 50.000 1.474.500 +10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 + ? ? ? ? ? ? ? ? + +2022 + 700.000 450.000 400.000 700.000 700.000 380.000 0 1.708.000 +10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 + ? ? ? ? ? ? ? ? + +2023 + 0 0 0 0 0 0 0 2.075.250 +10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 + ? ? ? ? ? ? ? ? + +2024 + ? ? ? ? ? ? ? ? + ? ? ? ? ? ? ? ? + ? ? ? ? ? ? ? ? + + 2014 "Council of Europe" + ? ? ? ` - out := `BEGIN 2002 - -BEGIN CIRC -378.383.000 326.439.000 217.031.000 441.621.000 203.388.000 169.088.000 223.544.000 196.438.000 - 10.817.950 118.526.000 ? 43.859.072 50.913.200 9.074.200 150.000 4.679.500 - -BEGIN PROOF -378.383.000 123.456.789 5 441.621.000 203.388.000 169.088.000 223.544.000 196.438.000 - 10.817.950 118.526.000 ? 43.859.072 50.913.200 9.074.200 150.000 4.679.500 + out := ` +2014 + 60.000 60.000 860.000 860.000 860.000 340.000 511.843 360.000 + 70.000 70.000 70.000 70.000 70.000 70.000 70.000 70.000 + 3.000 3.000 3.000 3.000 3.000 3.000 3.000 3.000 + +# Ref: https://example.com +2015 + 0 0 0 0 0 0 0 1.072.400 + 40.000 40.000 40.000 40.000 40.000 40.000 40.000 40.000 + ? ? ? ? ? ? ? ? + +2016 + 0 0 0 0 0 0 2.339.200 0 + 35.000 35.000 35.000 35.000 35.000 35.000 35.000 35.000 + ? ? ? ? ? ? ? ? + +2017 + 2.582.395 1.515.000 2.191.421 1.103.000 1.213.000 968.800 17.000 794.588 + 22.000 22.000 22.000 22.000 22.000 22.000 22.000 22.000 + ? ? ? ? ? ? ? ? + +2018 + 2.430.000 2.550.000 1.800.000 980.000 1.014.000 890.000 0 868.000 + 20.000 20.000 20.000 20.000 20.000 20.000 20.000 20.000 + ? ? ? ? ? ? ? ? + +2019 + 2.447.000 1.727.000 2.100.000 1.610.000 1.570.000 930.000 0 1.058.310 + 15.000 15.000 15.000 15.000 15.000 15.000 15.000 15.000 + ? ? ? ? ? ? ? ? + +2020 + 0 0 0 860.000 175.000 740.000 0 1.500.000 + 12.000 12.000 12.000 12.000 12.000 12.000 12.000 12.000 + ? ? ? ? ? ? ? ? + +2021 + 200.000 700.000 0 1.400.000 1.420.000 600.000 50.000 1.474.500 + 10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 + ? ? ? ? ? ? ? ? + +2022 + 700.000 450.000 400.000 700.000 700.000 380.000 0 1.708.000 + 10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 + ? ? ? ? ? ? ? ? + +2023 + 0 0 0 0 0 0 0 2.075.250 + 10.500 10.500 10.500 10.500 10.500 10.500 10.500 10.500 + ? ? ? ? ? ? ? ? + +2024 + ? ? ? ? ? ? ? ? + ? ? ? ? ? ? ? ? + ? ? ? ? ? ? ? ? + +2014 "Council of Europe" + ? ? ? ` - runTest(t, in, out, true) -} -func TestLabelAlignment(t *testing.T) { - in := `BEGIN 2002 - -BEGIN CIRC -378383000 326439000 217031000 441621000 203388000 169088000 223544000 196438000 -KNM*: 10817950 118526000 ? 43859072 50913200 9074200 150000 4679500 -MdP: 115.000.000 156.400.000 89.300.000 5.200.000 54.800.000 3.100.000 2.600.000 2.500.000 -` - out := `BEGIN 2002 + r := bytes.NewReader([]byte(in)) + w := new(bytes.Buffer) -BEGIN CIRC - 378.383.000 326.439.000 217.031.000 441.621.000 203.388.000 169.088.000 223.544.000 196.438.000 -KNM*: 10.817.950 118.526.000 ? 43.859.072 50.913.200 9.074.200 150.000 4.679.500 -MdP: 115.000.000 156.400.000 89.300.000 5.200.000 54.800.000 3.100.000 2.600.000 2.500.000 -` - runTest(t, in, out, true) + mfmt("-", r, w) + if w.String() != out { + t.Fatalf(`Expected w.String()="%s"; got "%s"`, out, w.String()) + } } diff --git a/cmd/mfmt/util_test.go b/cmd/mfmt/util_test.go index 1041e4f..8366a50 100644 --- a/cmd/mfmt/util_test.go +++ b/cmd/mfmt/util_test.go @@ -3,26 +3,19 @@ package main -import ( - "testing" - - "git.thomasvoss.com/euro-cash.eu/lib/mintage" -) +import "testing" func TestFormatInt(t *testing.T) { - for _, x := range [...]struct { - n int - s string - }{ - {0, "0"}, - {123, "123"}, - {81758, "81.758"}, - {752759237528, "752.759.237.528"}, - {mintage.Unknown, "?"}, + for _, x := range [...]struct{ x, y string }{ + {"?", "?"}, + {"0", "0"}, + {"123", "123"}, + {"81758", "81.758"}, + {"752759237528", "752.759.237.528"}, } { - s := formatInt(x.n) - if s != x.s { - t.Fatalf(`Expected s="%s"; got "%s"`, x.s, s) + s := formatMintage(x.x) + if s != x.y { + t.Fatalf(`Expected s="%s"; got "%s"`, x.y, s) } } } @@ -33,7 +26,6 @@ func TestIntLen(t *testing.T) { {123, len("123")}, {81758, len("81758")}, {752759237528, len("752759237528")}, - {mintage.Unknown, len("?")}, } { n := intlen(x.x) if n != x.y { -- cgit v1.2.3