github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/cmd/snap/cmd_list.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package main 21 22 import ( 23 "errors" 24 "fmt" 25 "sort" 26 "strings" 27 "text/tabwriter" 28 29 "github.com/jessevdk/go-flags" 30 31 "github.com/snapcore/snapd/client" 32 "github.com/snapcore/snapd/i18n" 33 ) 34 35 var shortListHelp = i18n.G("List installed snaps") 36 var longListHelp = i18n.G(` 37 The list command displays a summary of snaps installed in the current system. 38 39 A green check mark (given color and unicode support) after a publisher name 40 indicates that the publisher has been verified. 41 `) 42 43 type cmdList struct { 44 clientMixin 45 Positional struct { 46 Snaps []installedSnapName `positional-arg-name:"<snap>"` 47 } `positional-args:"yes"` 48 49 All bool `long:"all"` 50 colorMixin 51 } 52 53 func init() { 54 addCommand("list", shortListHelp, longListHelp, func() flags.Commander { return &cmdList{} }, 55 colorDescs.also(map[string]string{ 56 // TRANSLATORS: This should not start with a lowercase letter. 57 "all": i18n.G("Show all revisions"), 58 }), nil) 59 } 60 61 type snapsByName []*client.Snap 62 63 func (s snapsByName) Len() int { return len(s) } 64 func (s snapsByName) Less(i, j int) bool { return s[i].Name < s[j].Name } 65 func (s snapsByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 66 67 var ErrNoMatchingSnaps = errors.New(i18n.G("no matching snaps installed")) 68 69 // snapd will give us and we want 70 // "" (local snap) "-" 71 // latest/risk latest/risk 72 // track/risk track/risk 73 // track/risk/branch track/risk/… 74 // anything else SISO 75 func fmtChannel(ch string) string { 76 if ch == "" { 77 // "" -> "-" (local snap) 78 return "-" 79 } 80 if strings.Count(ch, "/") != 2 { 81 return ch 82 } 83 idx := strings.LastIndexByte(ch, '/') 84 return ch[:idx+1] + "…" 85 } 86 87 func (x *cmdList) Execute(args []string) error { 88 if len(args) > 0 { 89 return ErrExtraArgs 90 } 91 92 names := installedSnapNames(x.Positional.Snaps) 93 snaps, err := x.client.List(names, &client.ListOptions{All: x.All}) 94 if err != nil { 95 if err == client.ErrNoSnapsInstalled { 96 if len(names) == 0 { 97 fmt.Fprintln(Stderr, i18n.G("No snaps are installed yet. Try 'snap install hello-world'.")) 98 return nil 99 } else { 100 return ErrNoMatchingSnaps 101 } 102 } 103 return err 104 } else if len(snaps) == 0 { 105 return ErrNoMatchingSnaps 106 } 107 sort.Sort(snapsByName(snaps)) 108 109 esc := x.getEscapes() 110 w := tabWriter() 111 112 // TRANSLATORS: the %s is to insert a filler escape sequence (please keep it flush to the column header, with no extra spaces) 113 fmt.Fprintf(w, i18n.G("Name\tVersion\tRev\tTracking\tPublisher%s\tNotes\n"), fillerPublisher(esc)) 114 115 for _, snap := range snaps { 116 // doing it this way because otherwise it's a sea of %s\t%s\t%s 117 line := []string{ 118 snap.Name, 119 snap.Version, 120 snap.Revision.String(), 121 fmtChannel(snap.TrackingChannel), 122 shortPublisher(esc, snap.Publisher), 123 NotesFromLocal(snap).String(), 124 } 125 fmt.Fprintln(w, strings.Join(line, "\t")) 126 } 127 w.Flush() 128 129 return nil 130 } 131 132 func tabWriter() *tabwriter.Writer { 133 return tabwriter.NewWriter(Stdout, 5, 3, 2, ' ', 0) 134 }