From 0cbccb7d78e3d1b6afbb7e05033d567b5e5c30a9 Mon Sep 17 00:00:00 2001 From: Amolith Date: Sun, 5 Apr 2026 19:53:34 -0600 Subject: [PATCH] Replace double-pointer output params with return struct --- cmd/root.go | 107 +++++++++++++++++++++++----------------------------- 1 file changed, 47 insertions(+), 60 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index 8d666d720d7688c0c7c0c8caf8dc24cc91e8d53b..600ccdcb4a02efcbbfd22b63f7b08372071f0cfb 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -243,12 +243,9 @@ func runInteractive() (command, preset string, overrides map[string][]string, er preset = presets[0] } - // Screen references for extracting results after the session. - // These are populated by the resolve screen's builder function. - var snapshotScreen *screens.Snapshot - var filePickerScreen *screens.FilePicker - var targetScreen *screens.Target - var overwriteScreen *screens.Overwrite + // cmdScreens is populated by the resolve screen's builder function + // so the caller can extract results after the session completes. + var cmdScreens *commandScreens // The resolve screen sits between menu/preset and command-specific // screens. It resolves the config and dynamically builds the @@ -260,8 +257,11 @@ func runInteractive() (command, preset string, overrides map[string][]string, er p = presetScreen.Value() } - return buildCommandScreens(cmd, p, &styles, - &snapshotScreen, &filePickerScreen, &targetScreen, &overwriteScreen) + cmdScreens = buildCommandScreens(cmd, p, &styles) + if cmdScreens == nil { + return nil + } + return cmdScreens.list }) screenList = append(screenList, resolveScreen) @@ -287,26 +287,30 @@ func runInteractive() (command, preset string, overrides map[string][]string, er } // Extract overrides from the completed command-specific screens. - overrides = extractCommandOverrides(command, snapshotScreen, filePickerScreen, targetScreen, overwriteScreen) + overrides = extractCommandOverrides(command, cmdScreens) return command, preset, overrides, nil } +// commandScreens holds the screens built for a command's interactive +// flow along with typed references to each screen. This lets +// runInteractive read results from the completed screens without +// resorting to double-pointer output parameters. +type commandScreens struct { + list []ui.Screen + snapshot *screens.Snapshot + filePicker *screens.FilePicker + target *screens.Target + overwrite *screens.Overwrite +} + // buildCommandScreens resolves the config for the given command and // preset, then builds the command-specific screens that need user -// input. Screen pointers are stored in the provided output parameters -// so the caller can extract results after the session completes. -func buildCommandScreens( - command, preset string, - styles *theme.Styles, - snapshotOut **screens.Snapshot, - filePickerOut **screens.FilePicker, - targetOut **screens.Target, - overwriteOut **screens.Overwrite, -) []ui.Screen { +// input. Returns nil when the command needs no interactive screens. +func buildCommandScreens(command, preset string, styles *theme.Styles) *commandScreens { switch command { case "restore": - return buildRestoreScreens(preset, styles, snapshotOut, filePickerOut, targetOut, overwriteOut) + return buildRestoreScreens(preset, styles) default: return nil } @@ -314,14 +318,7 @@ func buildCommandScreens( // buildRestoreScreens creates the restore flow screens, skipping any // that are already provided by the resolved config. -func buildRestoreScreens( - preset string, - styles *theme.Styles, - snapshotOut **screens.Snapshot, - filePickerOut **screens.FilePicker, - targetOut **screens.Target, - overwriteOut **screens.Overwrite, -) []ui.Screen { +func buildRestoreScreens(preset string, styles *theme.Styles) *commandScreens { cfg, err := config.Resolve(preset, "restore", nil) if err != nil { // Config resolution failed. Return no screens so the session @@ -330,16 +327,15 @@ func buildRestoreScreens( return nil } - var list []ui.Screen + cs := &commandScreens{} // Step 1: Snapshot ID (skip if already provided as an argument). if len(cfg.Arguments) == 0 { loader := screens.SnapshotLoader(func() ([]restic.Snapshot, error) { return restic.ListSnapshots(cfg) }) - ss := screens.NewSnapshot(loader, styles) - *snapshotOut = ss - list = append(list, ss) + cs.snapshot = screens.NewSnapshot(loader, styles) + cs.list = append(cs.list, cs.snapshot) } // Step 2: File selection (skip if include flags set or snapshot @@ -349,8 +345,8 @@ func buildRestoreScreens( // Determine the snapshot ID source: from the snapshot screen // if it exists, otherwise from the resolved config. snapshotIDFn := func() string { - if *snapshotOut != nil { - return (*snapshotOut).Value() + if cs.snapshot != nil { + return cs.snapshot.Value() } if len(cfg.Arguments) > 0 { return cfg.Arguments[0] @@ -370,63 +366,54 @@ func buildRestoreScreens( return restic.RunLs(cfg, snapshotID) } - fps := screens.NewFilePicker(fileLoader, snapshotIDFn, styles) - *filePickerOut = fps - list = append(list, fps) + cs.filePicker = screens.NewFilePicker(fileLoader, snapshotIDFn, styles) + cs.list = append(cs.list, cs.filePicker) } // Step 3: Target directory (skip if already set). if !cfg.HasFlag("target") { - ts := screens.NewTarget(styles) - *targetOut = ts - list = append(list, ts) + cs.target = screens.NewTarget(styles) + cs.list = append(cs.list, cs.target) } // Step 4: Overwrite behaviour (skip if already set). if !cfg.HasFlag("overwrite") { - ows := screens.NewOverwrite(styles) - *overwriteOut = ows - list = append(list, ows) + cs.overwrite = screens.NewOverwrite(styles) + cs.list = append(cs.list, cs.overwrite) } - return list + return cs } // extractCommandOverrides reads the completed screen values and // returns a map suitable for passing to runCommand as overrides. -func extractCommandOverrides( - command string, - snapshotScreen *screens.Snapshot, - filePickerScreen *screens.FilePicker, - targetScreen *screens.Target, - overwriteScreen *screens.Overwrite, -) map[string][]string { - if command != "restore" { +func extractCommandOverrides(command string, cs *commandScreens) map[string][]string { + if command != "restore" || cs == nil { return nil } overrides := make(map[string][]string) - if snapshotScreen != nil { - if v := snapshotScreen.Value(); v != "" { + if cs.snapshot != nil { + if v := cs.snapshot.Value(); v != "" { overrides[overrideArgumentsKey] = []string{v} } } - if filePickerScreen != nil { - if includes := filePickerScreen.Includes(); len(includes) > 0 { + if cs.filePicker != nil { + if includes := cs.filePicker.Includes(); len(includes) > 0 { overrides["include"] = includes } } - if targetScreen != nil { - if v := targetScreen.Value(); v != "" { + if cs.target != nil { + if v := cs.target.Value(); v != "" { overrides["target"] = []string{v} } } - if overwriteScreen != nil { - if v := overwriteScreen.Value(); v != "" { + if cs.overwrite != nil { + if v := cs.overwrite.Value(); v != "" { overrides["overwrite"] = []string{v} } }