summaryrefslogtreecommitdiff
path: root/lib/textui/text.go
blob: f628eabc2e90ad33d81a707864f9cf611e550e3e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// Copyright (C) 2022  Luke Shumaker <lukeshu@lukeshu.com>
//
// SPDX-License-Identifier: GPL-2.0-or-later

package textui

import (
	"fmt"
	"io"

	"golang.org/x/exp/constraints"
	"golang.org/x/text/language"
	"golang.org/x/text/message"
	"golang.org/x/text/number"

	"git.lukeshu.com/btrfs-progs-ng/lib/fmtutil"
)

var printer = message.NewPrinter(language.English)

// Fprintf is like `fmt.Fprintf`, but (1) includes the extensions of
// `golang.org/x/text/message.Printer`, and (2) is useful for marking
// when a print call is part of the UI, rather than something
// internal.
func Fprintf(w io.Writer, key string, a ...any) (n int, err error) {
	return printer.Fprintf(w, key, a...)
}

// Sprintf is like `fmt.Sprintf`, but (1) includes the extensions of
// `golang.org/x/text/message.Printer`, and (2) is useful for marking
// when a sprint call is part of the UI, rather than something
// internal.
func Sprintf(key string, a ...any) string {
	return printer.Sprintf(key, a...)
}

// Humanized wraps a value such that formatting of it can make use of
// the `golang.org/x/text/message.Printer` extensions even when used
// with plain-old `fmt`.
func Humanized(x any) any {
	return humanized{val: x}
}

type humanized struct {
	val any
}

var (
	_ fmt.Formatter = humanized{}
	_ fmt.Stringer  = humanized{}
)

// String implements fmt.Formatter.
func (h humanized) Format(f fmt.State, verb rune) {
	printer.Fprintf(f, fmtutil.FmtStateString(f, verb), h.val)
}

// String implements fmt.Stringer.
func (h humanized) String() string {
	return fmt.Sprint(h)
}

// Portion renders a fraction N/D as both a percentage and
// parenthetically as the exact fractional value, rendered with
// human-friendly commas.
//
// For example:
//
//	fmt.Sprint(Portion[int]{N: 1, D: 12345}) ⇒ "0% (1/12,345)"
type Portion[T constraints.Integer] struct {
	N, D T
}

var (
	_ fmt.Stringer = Portion[int]{}
)

// String implements fmt.Stringer.
func (p Portion[T]) String() string {
	pct := float64(1)
	if p.D > 0 {
		pct = float64(p.N) / float64(p.D)
	}
	return printer.Sprintf("%v (%v/%v)", number.Percent(pct), uint64(p.N), uint64(p.D))
}