summaryrefslogtreecommitdiff
path: root/internal/recordline
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-04-14 10:39:16 +0300
committerPaul Buetow <paul@buetow.org>2026-04-14 10:39:16 +0300
commitff749457e392288e29dbf553bf8e0e64cc8b6401 (patch)
tree4d43b1de40007ebcc880bd596114e097a30cc590 /internal/recordline
parentb0ac28f90ddf85e85b0308229569292fd6f7f9a2 (diff)
refactor: extract record line parser to internal/recordline (m3)
Deduplicate parseRecordLine from goprecords and storage; shared recordline.Parse with Fields type. Tests live in recordline package; DB import path still covered by goprecords TestImportFromDir. Made-with: Cursor
Diffstat (limited to 'internal/recordline')
-rw-r--r--internal/recordline/recordline.go49
-rw-r--r--internal/recordline/recordline_test.go83
2 files changed, 132 insertions, 0 deletions
diff --git a/internal/recordline/recordline.go b/internal/recordline/recordline.go
new file mode 100644
index 0000000..940f235
--- /dev/null
+++ b/internal/recordline/recordline.go
@@ -0,0 +1,49 @@
+package recordline
+
+import (
+ "strconv"
+ "strings"
+)
+
+type Fields struct {
+ Uptime uint64
+ BootTime uint64
+ OS string
+ KernelName string
+ KernelMajor string
+}
+
+func Parse(line string) (Fields, bool) {
+ line = strings.TrimSpace(line)
+ if line == "" {
+ return Fields{}, false
+ }
+ parts := strings.SplitN(line, ":", 3)
+ if len(parts) != 3 {
+ return Fields{}, false
+ }
+ uptime, _ := strconv.ParseUint(parts[0], 10, 64)
+ bootTime, _ := strconv.ParseUint(parts[1], 10, 64)
+ osStr := parts[2]
+ kernelName := osStr
+ if i := strings.Index(osStr, " "); i > 0 {
+ kernelName = osStr[:i]
+ }
+ kernelMajor := kernelName + " "
+ rest := osStr
+ if i := strings.Index(osStr, " "); i >= 0 {
+ rest = osStr[i+1:]
+ }
+ if j := strings.Index(rest, "."); j >= 0 {
+ kernelMajor += rest[:j] + "..."
+ } else {
+ kernelMajor += rest + "..."
+ }
+ return Fields{
+ Uptime: uptime,
+ BootTime: bootTime,
+ OS: osStr,
+ KernelName: kernelName,
+ KernelMajor: kernelMajor,
+ }, true
+}
diff --git a/internal/recordline/recordline_test.go b/internal/recordline/recordline_test.go
new file mode 100644
index 0000000..4fcc152
--- /dev/null
+++ b/internal/recordline/recordline_test.go
@@ -0,0 +1,83 @@
+package recordline
+
+import (
+ "testing"
+)
+
+func TestParse(t *testing.T) {
+ tests := []struct {
+ in string
+ want Fields
+ ok bool
+ }{
+ {
+ "12345:1700000000:Linux 6.5.0-generic",
+ Fields{
+ Uptime: 12345,
+ BootTime: 1700000000,
+ OS: "Linux 6.5.0-generic",
+ KernelName: "Linux",
+ KernelMajor: "Linux 6...",
+ },
+ true,
+ },
+ {
+ " 99:100:FreeBSD 14.0-RELEASE ",
+ Fields{
+ Uptime: 99,
+ BootTime: 100,
+ OS: "FreeBSD 14.0-RELEASE",
+ KernelName: "FreeBSD",
+ KernelMajor: "FreeBSD 14...",
+ },
+ true,
+ },
+ {
+ "500:200:SingleToken",
+ Fields{
+ Uptime: 500,
+ BootTime: 200,
+ OS: "SingleToken",
+ KernelName: "SingleToken",
+ KernelMajor: "SingleToken SingleToken...",
+ },
+ true,
+ },
+ {
+ "100:200:Linux 6.5.0:extra",
+ Fields{
+ Uptime: 100,
+ BootTime: 200,
+ OS: "Linux 6.5.0:extra",
+ KernelName: "Linux",
+ KernelMajor: "Linux 6...",
+ },
+ true,
+ },
+ {
+ "abc:def:Linux 6.5.0",
+ Fields{
+ Uptime: 0,
+ BootTime: 0,
+ OS: "Linux 6.5.0",
+ KernelName: "Linux",
+ KernelMajor: "Linux 6...",
+ },
+ true,
+ },
+ {"", Fields{}, false},
+ {" ", Fields{}, false},
+ {"only:two", Fields{}, false},
+ {"no-colons-at-all", Fields{}, false},
+ }
+ for _, tt := range tests {
+ got, ok := Parse(tt.in)
+ if ok != tt.ok {
+ t.Errorf("Parse(%q) ok=%v, want %v", tt.in, ok, tt.ok)
+ continue
+ }
+ if tt.ok && got != tt.want {
+ t.Errorf("Parse(%q) = %+v, want %+v", tt.in, got, tt.want)
+ }
+ }
+}