diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-12 12:58:14 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-12 12:58:14 +0200 |
| commit | 24a84671ace373ae331fa83a1ff484990f4dff0e (patch) | |
| tree | a6b6e3949d7dd241f1d13e079dfb982d758c89a2 /internal/plan/json.go | |
| parent | 3b36a48b7ce5a53a9366f3b31f94311f178e2553 (diff) | |
| download | krino-24a84671ace373ae331fa83a1ff484990f4dff0e.tar.gz krino-24a84671ace373ae331fa83a1ff484990f4dff0e.zip | |
krino: planning — chains, placeholders, conflicts, JSON
Diffstat (limited to 'internal/plan/json.go')
| -rw-r--r-- | internal/plan/json.go | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/internal/plan/json.go b/internal/plan/json.go new file mode 100644 index 0000000..0b4f1bc --- /dev/null +++ b/internal/plan/json.go @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package plan + +import "time" + +// jsonNote is carried in every JSON document, warning readers that the +// shape is not yet stable. +const jsonNote = "the shape of this document is unstable before krino 1.0" + +// JSON is the --json document. The shape is unstable before 1.0 and says +// so in its own "note" field. +type JSON struct { + Version int `json:"version"` + Note string `json:"note"` + Dirs []JSONDir `json:"dirs"` +} + +// JSONDir is one directory's plan. +type JSONDir struct { + Name string `json:"name"` + Root string `json:"root"` + Files []JSONFile `json:"files"` + Warnings []string `json:"warnings,omitempty"` +} + +// JSONFile is one file's chain. +type JSONFile struct { + Rel string `json:"rel"` + Size int64 `json:"size"` + ModTime time.Time `json:"mtime"` + Steps []JSONStep `json:"steps"` + Warnings []string `json:"warnings,omitempty"` +} + +// JSONStep is one step of a chain. D14: Reason is carried because the text +// plan already shows it and a machine reader should be able to see why a +// file matched too; Conflict (the rule's on-conflict policy) is deliberately +// not: it is a config detail, and its outcome is already visible through +// dst, displaces and skip. +type JSONStep struct { + Action string `json:"action"` + Rule string `json:"rule"` + Src string `json:"src"` + Dst string `json:"dst,omitempty"` + Displaces string `json:"displaces,omitempty"` + Reason string `json:"reason,omitempty"` + Skip string `json:"skip,omitempty"` +} + +// actionNames maps a Kind onto its JSON action name. This is its own +// mapping, independent of Kind.String(): the display form renders "DELETE +// permanently", which must never reach a machine reader. These names match +// the log's action names in spec §9, so a later `krino log` and a --json +// plan can be grepped together. +var actionNames = map[Kind]string{ + Copy: "copy", + Move: "move", + Rename: "rename", + Trash: "trash", + DeletePermanent: "delete", +} + +// NewJSON builds the top-level --json document over dirs. Version and Note +// are set here, in the one place jsonNote's wording already lives: it is +// unexported, so a struct literal built outside this package would +// silently ship an empty "note" and break the document's own contract. +func NewJSON(dirs []JSONDir) JSON { + return JSON{Version: 1, Note: jsonNote, Dirs: dirs} +} + +// NewJSONDir converts one directory's chains. +func NewJSONDir(name, root string, chains []Chain, warnings []string) JSONDir { + files := make([]JSONFile, 0, len(chains)) + for _, ch := range chains { + steps := make([]JSONStep, 0, len(ch.Steps)) + for _, s := range ch.Steps { + steps = append(steps, JSONStep{ + Action: actionNames[s.Kind], + Rule: s.Rule, + Src: s.Src, + Dst: s.Dst, + Displaces: s.Displaces, + Reason: s.Reason, + Skip: s.Skip, + }) + } + files = append(files, JSONFile{ + Rel: ch.File.Rel, + Size: ch.File.Size, + ModTime: ch.File.ModTime, + Steps: steps, + Warnings: ch.Warnings, + }) + } + return JSONDir{Name: name, Root: root, Files: files, Warnings: warnings} +} |
