diff options
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/extpo/main.go | 305 | ||||
-rw-r--r-- | cmd/exttmpl/main.go | 224 | ||||
-rw-r--r-- | cmd/extwiki/main.go | 151 | ||||
-rw-r--r-- | cmd/mfmt/main.go | 189 | ||||
-rw-r--r-- | cmd/mfmt/main_test.go | 140 | ||||
-rw-r--r-- | cmd/mfmt/util_test.go | 35 |
6 files changed, 456 insertions, 588 deletions
diff --git a/cmd/extpo/main.go b/cmd/extpo/main.go new file mode 100644 index 0000000..6aa1503 --- /dev/null +++ b/cmd/extpo/main.go @@ -0,0 +1,305 @@ +package main + +import ( + "bytes" + "flag" + "fmt" + "io" + "os" + "path/filepath" + "slices" + "strings" + "text/template/parse" + "time" +) + +type config struct { + arg int + plural int + context int + domain int +} + +type translation struct { + msgid string + msgidPlural string + msgctxt string + domain string +} + +type transinfo struct { + comment string + locs []loc +} + +type loc struct { + file string + line int +} + +func (l loc) String() string { + return fmt.Sprintf("%s:%d", l.file, l.line) +} + +var ( + currentFile []byte + currentPath string + lastComment string + translations = make(map[translation]transinfo) + configs = map[string]config{ + "Get": {1, -1, -1, -1}, + "GetC": {1, -1, 2, -1}, + "GetD": {2, -1, -1, 1}, + "GetDC": {2, -1, 3, 1}, + "GetN": {1, 2, -1, -1}, + "GetNC": {1, 2, 4, -1}, + "GetND": {2, 3, -1, 1}, + "GetNDC": {2, 3, 5, 1}, + } +) + +func usage() { + fmt.Fprintf(os.Stderr, "Usage: %s [flags] template...\n", + filepath.Base(os.Args[0])) + flag.PrintDefaults() +} + +func main() { + try(os.Chdir(filepath.Dir(os.Args[0]))) + + outpath := flag.String("out", "-", "output file") + flag.Usage = usage + flag.Parse() + + if flag.NArg() < 1 { + flag.Usage() + os.Exit(1) + } + + var outfile *os.File + if *outpath == "-" { + outfile = os.Stdout + } else { + outfile = try2(os.Create(*outpath)) + } + + for _, f := range flag.Args() { + process(f) + } + + fmt.Fprintf(outfile, `# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: %s\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +`, time.Now().Format("2006-01-02 15:04-0700")) + + for tl, ti := range translations { + if ti.comment != "" { + fmt.Fprintln(outfile, "#.", ti.comment) + } + + slices.SortFunc(ti.locs, func(a, b loc) int { + if x := strings.Compare(a.file, b.file); x != 0 { + return x + } + return a.line - b.line + }) + for _, x := range ti.locs { + fmt.Fprintln(outfile, "#:", x) + } + + if tl.msgctxt != "" { + writeField(outfile, "msgctxt", tl.msgctxt) + } + writeField(outfile, "msgid", tl.msgid) + if tl.msgidPlural != "" { + writeField(outfile, "msgid_plural", tl.msgidPlural) + writeField(outfile, "msgstr[0]", "") + writeField(outfile, "msgstr[1]", "") + } else { + writeField(outfile, "msgstr", "") + } + fmt.Fprint(outfile, "\n") + } +} + +func process(path string) { + currentPath = path + currentFile = try2(os.ReadFile(path)) + trees := make(map[string]*parse.Tree) + t := parse.New(path) + t.Mode |= parse.ParseComments | parse.SkipFuncCheck + try2(t.Parse(string(currentFile), "", "", trees)) + for _, t := range trees { + processNode(t.Root) + } +} + +func processNode(node parse.Node) { + switch n := node.(type) { + case *parse.ListNode: + for _, m := range n.Nodes { + processNode(m) + } + case *parse.PipeNode: + for _, m := range n.Cmds { + processNode(m) + } + case *parse.TemplateNode: + processNode(n.Pipe) + case *parse.IfNode: + processBranch(n.BranchNode) + case *parse.RangeNode: + processBranch(n.BranchNode) + case *parse.WithNode: + processBranch(n.BranchNode) + case *parse.BranchNode: + processBranch(*n) + case *parse.ActionNode: + processNode(n.Pipe) + case *parse.CommandNode: + if len(n.Args) == 0 { + break + } + + var funcname string + f, ok := n.Args[0].(*parse.FieldNode) + if !ok || len(f.Ident) == 0 { + ff, ok := n.Args[0].(*parse.VariableNode) + if !ok || len(ff.Ident) == 0 { + fff, ok := n.Args[0].(*parse.IdentifierNode) + if !ok { + for _, pipe := range n.Args { + processNode(pipe) + } + break + } + funcname = fff.Ident + } else { + funcname = ff.Ident[len(ff.Ident)-1] + } + } else { + funcname = f.Ident[len(f.Ident)-1] + } + + cfg, ok := configs[funcname] + if !ok { + for _, pipe := range n.Args { + processNode(pipe) + } + break + } + + var ( + tl translation + linenr int + ) + + if sn, ok := n.Args[cfg.arg].(*parse.StringNode); ok { + tl.msgid = sn.Text + linenr = getlinenr(sn.Pos.Position()) + } else { + break + } + if cfg.plural != -1 { + if sn, ok := n.Args[cfg.plural].(*parse.StringNode); ok { + tl.msgidPlural = sn.Text + } + } + if cfg.context != -1 { + if sn, ok := n.Args[cfg.context].(*parse.StringNode); ok { + tl.msgctxt = sn.Text + } + } + + ti := translations[tl] + if lastComment != "" { + ti.comment = lastComment + lastComment = "" + } + ti.locs = append(ti.locs, loc{currentPath, linenr}) + translations[tl] = ti + case *parse.CommentNode: + if strings.HasPrefix(n.Text, "/* TRANSLATORS:") { + lastComment = strings.TrimSpace(n.Text[2 : len(n.Text)-2]) + } + } +} + +func processBranch(n parse.BranchNode) { + processNode(n.List) + if n.ElseList != nil { + processNode(n.ElseList) + } +} + +func writeField(w io.Writer, pfx, s string) { + fmt.Fprintf(w, "%s ", pfx) + if strings.ContainsRune(s, '\n') { + fmt.Fprintln(w, "\"\"") + lines := strings.SplitAfter(s, "\n") + n := len(lines) + if n > 1 && lines[n-1] == "" { + lines = lines[:n-1] + } + for _, ss := range lines { + writeLine(w, ss) + } + fmt.Fprintln(w, "\"\"") + } else { + writeLine(w, s) + } +} + +func writeLine(w io.Writer, s string) { + fmt.Fprint(w, "\"") + for _, c := range s { + switch c { + case '\\', '"': + fmt.Fprintf(w, "\\%c", c) + case '\n': + fmt.Fprint(w, "\\n") + default: + fmt.Fprintf(w, "%c", c) + } + } + fmt.Fprintln(w, "\"") +} + +func getlinenr(p parse.Pos) int { + return bytes.Count(currentFile[:p], []byte{'\n'}) + 1 +} + +func try(err error) { + if err != nil { + die(err) + } +} + +func try2[T any](val T, err error) T { + try(err) + return val +} + +func warn(err any) { + fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err) +} + +func die(err any) { + warn(err) + os.Exit(1) +} diff --git a/cmd/exttmpl/main.go b/cmd/exttmpl/main.go deleted file mode 100644 index 299bf2a..0000000 --- a/cmd/exttmpl/main.go +++ /dev/null @@ -1,224 +0,0 @@ -package main - -import ( - "fmt" - "os" - "path/filepath" - "slices" - "strings" - "text/template/parse" - - "golang.org/x/text/language" - "golang.org/x/text/message/pipeline" - "golang.org/x/tools/go/packages" -) - -const ( - pkgbase = "git.thomasvoss.com/euro-cash.eu" - srclang = "en" - srcdir = "./src" - transdir = srcdir + "/rosetta" - outfile = "catalog.gen.go" - transfn = "T" -) - -func main() { - /* cd to the project root directory */ - try(os.Chdir(filepath.Dir(os.Args[0]))) - - pkgnames := packageList(".") - - var paths []string - pkgs := try2(packages.Load(&packages.Config{ - Mode: packages.NeedFiles | packages.NeedEmbedFiles, - }, pkgnames...)) - - for _, pkg := range pkgs { - if len(pkg.Errors) != 0 { - for _, err := range pkg.Errors { - warn(err.Msg) - } - os.Exit(1) - } - for _, f := range pkg.EmbedFiles { - if filepath.Ext(f) == ".tmpl" { - paths = append(paths, f) - } - } - } - - msgs := make([]pipeline.Message, 0, 1024) - for _, path := range paths { - f := try2(os.ReadFile(path)) - trees := make(map[string]*parse.Tree) - t := parse.New("name") - t.Mode |= parse.SkipFuncCheck - try2(t.Parse(string(f), "", "", trees)) - for _, t := range trees { - process(&msgs, t.Root) - } - } - - pconf := &pipeline.Config{ - Supported: languages(), - SourceLanguage: language.Make(srclang), - Packages: pkgnames, - Dir: transdir, - GenFile: outfile, - GenPackage: srcdir, - } - - state := try2(pipeline.Extract(pconf)) - state.Extracted.Messages = append(state.Extracted.Messages, msgs...) - - try(state.Import()) - try(state.Merge()) - try(state.Export()) - try(state.Generate()) -} - -func process(tmplMsgs *[]pipeline.Message, node parse.Node) { - switch node.Type() { - case parse.NodeList: - if ln, ok := node.(*parse.ListNode); ok { - for _, n := range ln.Nodes { - process(tmplMsgs, n) - } - } - case parse.NodeIf: - if in, ok := node.(*parse.IfNode); ok { - process(tmplMsgs, in.List) - if in.ElseList != nil { - process(tmplMsgs, in.ElseList) - } - } - case parse.NodeWith: - if wn, ok := node.(*parse.WithNode); ok { - process(tmplMsgs, wn.List) - if wn.ElseList != nil { - process(tmplMsgs, wn.ElseList) - } - } - case parse.NodeRange: - if rn, ok := node.(*parse.RangeNode); ok { - process(tmplMsgs, rn.List) - if rn.ElseList != nil { - process(tmplMsgs, rn.ElseList) - } - } - case parse.NodeAction: - an, ok := node.(*parse.ActionNode) - if !ok { - break - } - - for _, cmd := range an.Pipe.Cmds { - if !hasIndent(cmd, transfn) { - continue - } - for _, arg := range cmd.Args { - if arg.Type() != parse.NodeString { - continue - } - if sn, ok := arg.(*parse.StringNode); ok { - txt := collapse(sn.Text) - *tmplMsgs = append(*tmplMsgs, pipeline.Message{ - ID: pipeline.IDList{txt}, - Key: txt, - Message: pipeline.Text{Msg: txt}, - }) - break - } - } - } - } -} - -func hasIndent(cmd *parse.CommandNode, s string) bool { - if len(cmd.Args) == 0 { - return false - } - arg := cmd.Args[0] - var idents []string - switch arg.Type() { - case parse.NodeField: - idents = arg.(*parse.FieldNode).Ident - case parse.NodeVariable: - idents = arg.(*parse.VariableNode).Ident - } - return slices.Contains(idents, s) -} - -func packageList(path string) []string { - ents := try2(os.ReadDir(path)) - xs := make([]string, 0, len(ents)) - foundOne := false - for _, ent := range ents { - switch { - case filepath.Ext(ent.Name()) == ".go": - if !foundOne { - xs = append(xs, pkgbase+"/"+path) - foundOne = true - } - case !ent.IsDir(), ent.Name() == "cmd", ent.Name() == "vendor": - continue - default: - xs = append(xs, packageList(path+"/"+ent.Name())...) - } - } - return xs -} - -func languages() []language.Tag { - ents := try2(os.ReadDir(transdir)) - tags := make([]language.Tag, len(ents)) - for i, e := range ents { - tags[i] = language.MustParse(e.Name()) - } - return tags -} - -func collapse(s string) string { - var ( - sb strings.Builder - prev bool - ) - const spc = " \t\n" - - for _, ch := range strings.Trim(s, spc) { - if strings.ContainsRune(spc, ch) { - if prev { - continue - } - ch = ' ' - prev = true - } else { - prev = false - } - sb.WriteRune(ch) - } - - return sb.String() -} - -func try(err error) { - if err != nil { - die(err) - } -} - -func try2[T any](val T, err error) T { - if err != nil { - die(err) - } - return val -} - -func warn(err any) { - fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err) -} - -func die(err any) { - warn(err) - os.Exit(1) -} diff --git a/cmd/extwiki/main.go b/cmd/extwiki/main.go new file mode 100644 index 0000000..76e0f3a --- /dev/null +++ b/cmd/extwiki/main.go @@ -0,0 +1,151 @@ +package main + +import ( + "flag" + "fmt" + "maps" + "os" + "path/filepath" + "slices" + "sort" + "text/template/parse" +) + +var ( + titles = make(map[string]struct{}) + configs = map[string]int{"Wikipedia": 1} +) + +func usage() { + fmt.Fprintf(os.Stderr, "Usage: %s [flags] template...\n", + filepath.Base(os.Args[0])) + flag.PrintDefaults() +} + +func main() { + try(os.Chdir(filepath.Dir(os.Args[0]))) + + outpath := flag.String("out", "-", "output file") + flag.Usage = usage + flag.Parse() + + if flag.NArg() < 1 { + flag.Usage() + os.Exit(1) + } + + var outfile *os.File + if *outpath == "-" { + outfile = os.Stdout + } else { + outfile = try2(os.Create(*outpath)) + } + + for _, f := range flag.Args() { + process(f) + } + + fmt.Fprint(outfile, `// Code generated by extwiki. DO NOT EDIT. + +package wikipedia + +var extractedTitles = [...]string{ +`) + xs := slices.Collect(maps.Keys(titles)) + sort.Strings(xs) + for _, t := range xs { + fmt.Fprintf(outfile, "%q,\n", t) + } + fmt.Fprint(outfile, "}\n") +} + +func process(path string) { + currentFile := try2(os.ReadFile(path)) + trees := make(map[string]*parse.Tree) + t := parse.New(path) + t.Mode |= parse.ParseComments | parse.SkipFuncCheck + try2(t.Parse(string(currentFile), "", "", trees)) + for _, t := range trees { + processNode(t.Root) + } +} + +func processNode(node parse.Node) { + switch n := node.(type) { + case *parse.ListNode: + for _, m := range n.Nodes { + processNode(m) + } + case *parse.PipeNode: + for _, m := range n.Cmds { + processNode(m) + } + case *parse.TemplateNode: + processNode(n.Pipe) + case *parse.IfNode: + processBranch(n.BranchNode) + case *parse.RangeNode: + processBranch(n.BranchNode) + case *parse.WithNode: + processBranch(n.BranchNode) + case *parse.BranchNode: + processBranch(*n) + case *parse.ActionNode: + processNode(n.Pipe) + case *parse.CommandNode: + if len(n.Args) == 0 { + break + } + + var funcname string + f, ok := n.Args[0].(*parse.FieldNode) + if !ok || len(f.Ident) == 0 { + ff, ok := n.Args[0].(*parse.VariableNode) + if !ok || len(ff.Ident) == 0 { + fff, ok := n.Args[0].(*parse.IdentifierNode) + if !ok { + for _, pipe := range n.Args { + processNode(pipe) + } + break + } + funcname = fff.Ident + } else { + funcname = ff.Ident[len(ff.Ident)-1] + } + } else { + funcname = f.Ident[len(f.Ident)-1] + } + + i, ok := configs[funcname] + if !ok { + for _, pipe := range n.Args { + processNode(pipe) + } + break + } + + if sn, ok := n.Args[i].(*parse.StringNode); ok { + titles[sn.Text] = struct{}{} + } + } +} + +func processBranch(n parse.BranchNode) { + processNode(n.List) + if n.ElseList != nil { + processNode(n.ElseList) + } +} + +func try(err error) { + if err != nil { + fmt.Fprintf(os.Stderr, "%s: %s\n", filepath.Base(os.Args[0]), err) + os.Exit(1) + } +} + +func try2[T any](val T, err error) T { + try(err) + return val +} diff --git a/cmd/mfmt/main.go b/cmd/mfmt/main.go deleted file mode 100644 index c4e2277..0000000 --- a/cmd/mfmt/main.go +++ /dev/null @@ -1,189 +0,0 @@ -/* 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" - "bytes" - "fmt" - "io" - "os" - "path/filepath" - "regexp" - "strings" -) - -const ws = " \t" - -var ( - rv int - - reMintageYear = regexp.MustCompile(`^\d{4}(-[^ \t]+)?`) - reMintageRowC = regexp.MustCompile(`^[ \t]*(([0-9.]+|\?)[ \t]+){2}([0-9.]+|\?)$`) - reMintageRowS = regexp.MustCompile(`^[ \t]*(([0-9.]+|\?)[ \t]+){7}([0-9.]+|\?)$`) -) - -func main() { - if len(os.Args) == 1 { - mfmt("-", os.Stdin, os.Stdout) - } - for _, arg := range os.Args[1:] { - f, err := os.OpenFile(arg, os.O_RDWR, 0) - if err != nil { - warn(err) - continue - } - defer f.Close() - - buf := bytes.NewBuffer(make([]byte, 0, 8192)) - mfmt(arg, f, buf) - - if _, err = f.Seek(0, io.SeekStart); err != nil { - warn(err) - continue - } - - if _, err = f.Write(buf.Bytes()); err != nil { - warn(err) - continue - } - - if err = f.Truncate(int64(buf.Len())); err != nil { - warn(err) - } - } - os.Exit(rv) -} - -func mfmt(file string, r io.Reader, w io.Writer) { - var ( - buf [3]string - bufsz int - longestNums [8]int - ) - - 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 reMintageRowS.MatchString(line): - switch bufsz { - case len(buf): - bufsz = 0 - clear(longestNums[:]) - fallthrough - default: - setLongestNum(longestNums[:], line) - buf[bufsz] = line - if bufsz++; bufsz == len(buf) { - fmtMintageRow(buf[0], longestNums[:], w) - fmtMintageRow(buf[1], longestNums[:], w) - fmtMintageRow(buf[2], longestNums[:], w) - } - } - case reMintageRowC.MatchString(line): - var ns [3]int - setLongestNum(ns[:], line) - fmtMintageRow(line, ns[:], w) - case reMintageYear.MatchString(line): - fmtMintageYear(line, w) - default: - warn(fmt.Sprintf("%s:%d: potential syntax error", file, linenr)) - fmt.Fprintln(w, line) - } - } -} - -func setLongestNum(longest []int, line string) { - for i, x := range strings.FieldsFunc(line, func(r rune) bool { - return strings.ContainsRune(ws, r) - }) { - n := len(strings.ReplaceAll(x, ".", "")) - n += (n - 1) / 3 // Thousands separators - longest[i] = max(longest[i], n) - } -} - -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)) - } -} - -func fmtMintageRow(line string, longest []int, w io.Writer) { - tokens := strings.FieldsFunc(line, func(r rune) bool { - return strings.ContainsRune(ws, r) - }) - - for i, tok := range tokens { - s := formatMintage(tok) - - if i == 0 { - fmt.Fprintf(w, "\t%*s", longest[i], s) - } else { - fmt.Fprintf(w, "%*s", longest[i]+1, s) - } - } - - fmt.Fprintln(w) -} - -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 - if n /= 10; n == 0 { - return string(out) - } - if j++; j == 3 { - i, j = i-1, 0 - out[i] = '.' - } - } -} - -func intlen(v int) int { - switch { - case v == 0: - return 1 - default: - n := 0 - for x := v; x != 0; x /= 10 { - n++ - } - return n - } -} - -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 deleted file mode 100644 index ef91473..0000000 --- a/cmd/mfmt/main_test.go +++ /dev/null @@ -1,140 +0,0 @@ -package main - -import ( - "bytes" - "testing" -) - -func runTest(t *testing.T, in, out string, nilErr bool) { -} - -func TestComplete(t *testing.T) { - in := ` -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 := ` -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" - ? ? ? -` - - r := bytes.NewReader([]byte(in)) - w := new(bytes.Buffer) - - 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 deleted file mode 100644 index 8366a50..0000000 --- a/cmd/mfmt/util_test.go +++ /dev/null @@ -1,35 +0,0 @@ -/* This file contains tests for utility functions used in the mfmt - binary. Tests of the actual application are in main_test.go. */ - -package main - -import "testing" - -func TestFormatInt(t *testing.T) { - for _, x := range [...]struct{ x, y string }{ - {"?", "?"}, - {"0", "0"}, - {"123", "123"}, - {"81758", "81.758"}, - {"752759237528", "752.759.237.528"}, - } { - s := formatMintage(x.x) - if s != x.y { - t.Fatalf(`Expected s="%s"; got "%s"`, x.y, s) - } - } -} - -func TestIntLen(t *testing.T) { - for _, x := range [...]struct{ x, y int }{ - {0, len("0")}, - {123, len("123")}, - {81758, len("81758")}, - {752759237528, len("752759237528")}, - } { - n := intlen(x.x) - if n != x.y { - t.Fatalf("Expected n=%d; got %d", x.y, n) - } - } -} |