1package restic
2
3import (
4 "encoding/json"
5 "fmt"
6 "strings"
7 "time"
8)
9
10// Snapshot represents a single restic snapshot as returned by
11// `restic snapshots --json`.
12type Snapshot struct {
13 ID string `json:"id"`
14 ShortID string `json:"short_id"`
15 Time time.Time `json:"time"`
16 Hostname string `json:"hostname"`
17 Paths []string `json:"paths"`
18 Tags []string `json:"tags"`
19}
20
21// ParseSnapshots decodes the JSON output of `restic snapshots --json`
22// into a slice of Snapshot values.
23func ParseSnapshots(data []byte) ([]Snapshot, error) {
24 var snapshots []Snapshot
25 if err := json.Unmarshal(data, &snapshots); err != nil {
26 return nil, fmt.Errorf("parsing snapshot JSON: %w", err)
27 }
28 return snapshots, nil
29}
30
31// FormatSnapshotLine returns a single-line human-readable summary of a
32// snapshot, suitable for use as a menu option label. The format is:
33//
34// <short_id> <date> <time> <hostname> <paths> [<tags>]
35//
36// Paths and tags are omitted when empty. Hostname shows "(unknown)"
37// when blank.
38func FormatSnapshotLine(s Snapshot) string {
39 var b strings.Builder
40
41 b.WriteString(s.ShortID)
42 b.WriteString(" ")
43 b.WriteString(s.Time.Format("2006-01-02 15:04"))
44
45 b.WriteString(" ")
46 if s.Hostname == "" {
47 b.WriteString("(unknown)")
48 } else {
49 b.WriteString(s.Hostname)
50 }
51
52 if len(s.Paths) > 0 {
53 b.WriteString(" ")
54 b.WriteString(strings.Join(s.Paths, ", "))
55 }
56
57 if len(s.Tags) > 0 {
58 b.WriteString(" [")
59 b.WriteString(strings.Join(s.Tags, ", "))
60 b.WriteString("]")
61 }
62
63 return b.String()
64}