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
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// display-infos prints a one-line status summary — battery, load, memory,
// Claude plan usage, and the date in three notations — for a status bar or a
// notification popup (see the display-infos-notify wrapper in the rolery
// machine config).
//
// It is a Go port of an embedded Python script. The port exists to drop the
// runtime dependencies that script carried: it shelled out to bc, date and
// free (parsing the last with the `jc` library) on every invocation, and lived
// as a string inside default.nix where it could be neither type-checked nor
// tested. Everything it used those tools for is a few lines of standard
// library here, and the sfttime rendering is inlined in sft.go.
package main

import (
	"fmt"
	"os"
	"strings"
	"time"
)

func main() {
	line, err := statusLine(time.Now())
	if err != nil {
		fmt.Fprintf(os.Stderr, "display-infos: %v\n", err)
		os.Exit(1)
	}
	fmt.Println(line)
}

// statusLine assembles the whole line. now is a parameter so tests can pin it.
func statusLine(now time.Time) (string, error) {
	bat, err := readBattery()
	if err != nil {
		return "", err
	}
	load, err := readLoad5()
	if err != nil {
		return "", err
	}
	availGiB, err := readAvailableGiB()
	if err != nil {
		return "", err
	}
	// A missing or broken claude-usage service must not break the status line,
	// so this error is advisory and the segment is simply omitted.
	claude, _ := fetchClaudeUsage()

	var b strings.Builder

	fmt.Fprintf(&b, "BAT: %d%% ", bat.Percent)
	if bat.Charging {
		b.WriteString("⚡ ")
	}
	if bat.Remaining > 0 {
		fmt.Fprintf(&b, "%s ", sftDuration(bat.Remaining.Seconds()))
	}
	fmt.Fprintf(&b, "{%s, %dG} | ", load, availGiB)

	b.WriteString(claudeSegment(claude))

	fmt.Fprintf(&b, "%s | %s | %s",
		formatDate(now),
		toSfttime(float64(now.UnixNano())/1e9, 3, false),
		now.UTC().Format("15·04"))

	return b.String(), nil
}

// claudeSegment renders the Claude usage part, including its trailing
// separator, or the empty string when there is nothing to say.
func claudeSegment(cu *claudeUsage) string {
	switch {
	case cu == nil:
		return ""

	case cu.NeedsLogin:
		// Nobody is logged in, so any cached percentage is frozen at whatever
		// it was when the session died and is actively misleading. Show the
		// required action instead of a plausible-looking number.
		return "Claude: ⚠login | "

	default:
		// ⚠ marks that the service could not reach claude.ai on its last
		// refresh. Show how old the numbers are too, so a brief network blip
		// is visibly different from a fortnight of silence.
		stale := ""
		if !cu.Reachable {
			stale = "⚠"
			if cu.Age >= 0 {
				stale += humanizeAge(cu.Age)
			}
		}
		return fmt.Sprintf("Claude: %d%% %s%s | ",
			cu.Percent, sftDuration(cu.ResetIn.Seconds()), stale)
	}
}

// weekdayAbbrev is an explicit English weekday table.
//
// Go's time package is locale-blind by design, so the abbreviation has to come
// from somewhere. Naming it here rather than inheriting LC_TIME also makes the
// output stable: the Python version rendered German ("Fr") purely because
// LC_TIME happened to be de_DE, and would have silently changed language with
// the environment.
var weekdayAbbrev = [...]string{
	time.Sunday:    "Sun",
	time.Monday:    "Mon",
	time.Tuesday:   "Tue",
	time.Wednesday: "Wed",
	time.Thursday:  "Thu",
	time.Friday:    "Fri",
	time.Saturday:  "Sat",
}

// formatDate renders the civil date part: "25.09. KW39 Fri 11:56:42".
//
// KW is the ISO 8601 week number (Kalenderwoche), which is not the same as the
// week of the month or a simple day-of-year division: ISO weeks start on
// Monday and week 1 is the one containing the first Thursday, so early January
// can fall in week 52 or 53 of the previous year.
func formatDate(t time.Time) string {
	_, week := t.ISOWeek()
	return fmt.Sprintf("%02d.%02d. KW%02d %s %s",
		t.Day(), int(t.Month()), week,
		weekdayAbbrev[t.Weekday()],
		t.Format("15:04:05"))
}