package sandbox import ( "fmt" "io" "strings" "github.com/safedep/pmg/errcodes" "github.com/safedep/pmg/internal/ui" pmgsandbox "github.com/safedep/pmg/sandbox" "github.com/spf13/cobra" ) type profileListOptions struct { jsonOut bool } func newProfileListCommand(factory registryFactory) *cobra.Command { opts := &profileListOptions{} cmd := &cobra.Command{ Use: "list", Short: "List available sandbox profiles (built-in and user)", Example: " pmg sandbox profile list", SilenceErrors: false, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { err := runProfileList(cmd.OutOrStdout(), opts, factory) if err != nil { return sandboxErrorExit(cmd, err) } return err }, } cmd.Flags().BoolVar(&opts.jsonOut, "json", false, "Emit profiles as JSON") return cmd } func runProfileList(out io.Writer, opts *profileListOptions, factory registryFactory) error { registry, err := factory() if err != nil { return registryInitError(err) } summaries, err := registry.ListProfiles() if err != nil { return wrapUseful(err, ioErrorCode(err, errcodes.Unknown), "Failed to enumerate sandbox profiles. Check the user profile directory permissions.") } if opts.jsonOut { return writeProfileListJSON(out, summaries) } return renderProfileListHuman(out, summaries) } type jsonProfileSummary struct { Name string `json:"name"` Source string `json:"source"` Path string `json:"path,omitempty"` Inherits string `json:"inherits,omitempty"` PackageManagers []string `json:"package_managers,omitempty"` Description string `json:"description,omitempty"` Shadowed bool `json:"shadowed,omitempty"` } type jsonProfileListReport struct { Profiles []jsonProfileSummary `json:"profiles"` } func writeProfileListJSON(out io.Writer, summaries []pmgsandbox.ProfileSummary) error { report := jsonProfileListReport{Profiles: make([]jsonProfileSummary, 0, len(summaries))} for _, s := range summaries { report.Profiles = append(report.Profiles, jsonProfileSummary{ Name: s.Name, Source: string(s.Source), Path: s.Path, Inherits: s.Inherits, PackageManagers: s.PackageManagers, Description: s.Description, Shadowed: s.Shadowed, }) } return writeJSONIndent(out, report) } func renderProfileListHuman(out io.Writer, summaries []pmgsandbox.ProfileSummary) error { if len(summaries) == 0 { _, err := fmt.Fprintln(out, ui.Colors.Dim("No sandbox profiles available.")) return err } if _, err := fmt.Fprintln(out); err != nil { return err } if _, err := fmt.Fprintln(out, ui.Colors.Cyan("Sandbox Profiles")); err != nil { return err } if _, err := fmt.Fprintln(out, ui.Colors.Normal("-----------------")); err != nil { return err } rows := make([][]string, 0, len(summaries)+1) rows = append(rows, []string{ ui.Colors.Bold("STATUS"), ui.Colors.Bold("NAME"), ui.Colors.Bold("SOURCE"), ui.Colors.Bold("INHERITS"), ui.Colors.Bold("PMS"), ui.Colors.Bold("DESCRIPTION"), }) for _, s := range summaries { rows = append(rows, []string{ statusCell(s), s.Name, sourceCell(s), emptyDash(s.Inherits), truncate(strings.Join(s.PackageManagers, ","), 30), truncate(s.Description, 60), }) } return renderTable(out, rows, nil) } func statusCell(s pmgsandbox.ProfileSummary) string { if s.Shadowed { return ui.Colors.Dim("SHADOWED") } return " " } func sourceCell(s pmgsandbox.ProfileSummary) string { if s.Source == pmgsandbox.ProfileSourceBuiltin { return ui.Colors.Dim("builtin") } return truncateLeft(s.Path, 50) } func emptyDash(s string) string { if s == "" { return ui.Colors.Dim("—") } return s }