pico
created pr with
52.1
cmds
checkout latest patchset:
ssh pr.pico.sh print 52 | git am -3checkout any patchset in a patch request:
ssh pr.pico.sh print 52.[rev] | git am -3add changes to patch request:
git format-patch main --stdout | ssh pr.pico.sh pr add 52
Patchset
52.1
refactor(pgs.cli): use tabwriter for table outputs
Eric Bower
2025-03-12T20:00:27ZSemantic diff summary
1 added,
5 modified,
1 signature changed,
1 removed
across 1 analyzed file
+93
-113
pgs/cli.go
#
| ... | ... | @@ -4,13 +4,13 @@ import ( | |
| 4 | 4 | "context" | |
| 5 | 5 | "errors" | |
| 6 | 6 | "fmt" | |
| 7 | + | "io" | |
| 7 | 8 | "log/slog" | |
| 8 | 9 | "path/filepath" | |
| 9 | 10 | "strings" | |
| 11 | + | "text/tabwriter" | |
| 10 | 12 | "time" | |
| 11 | 13 | ||
| 12 | - | "github.com/charmbracelet/lipgloss" | |
| 13 | - | "github.com/charmbracelet/lipgloss/table" | |
| 14 | 14 | "github.com/picosh/pico/db" | |
| 15 | 15 | pgsdb "github.com/picosh/pico/pgs/db" | |
| 16 | 16 | "github.com/picosh/pico/shared" |
| ... | ... | @@ -18,106 +18,31 @@ import ( | |
| 18 | 18 | "github.com/picosh/utils" | |
| 19 | 19 | ) | |
| 20 | 20 | ||
| 21 | - | func projectTable(projects []*db.Project, width int) *table.Table { | |
| 22 | - | headers := []string{ | |
| 23 | - | "Name", | |
| 24 | - | "Last Updated", | |
| 25 | - | "Links To", | |
| 26 | - | "ACL Type", | |
| 27 | - | "ACL", | |
| 28 | - | "Blocked", | |
| 29 | - | } | |
| 30 | - | data := [][]string{} | |
| 21 | + | func NewTabWriter(out io.Writer) *tabwriter.Writer { | |
| 22 | + | return tabwriter.NewWriter(out, 0, 0, 1, ' ', tabwriter.TabIndent) | |
| 23 | + | } | |
| 24 | + | ||
| 25 | + | func projectTable(sesh io.Writer, projects []*db.Project) { | |
| 26 | + | writer := NewTabWriter(sesh) | |
| 27 | + | fmt.Fprintln(writer, "Name\tLast Updated\tLinks To\tACL Type\tACL\tBlocked") | |
| 28 | + | ||
| 31 | 29 | for _, project := range projects { | |
| 32 | - | row := []string{ | |
| 33 | - | project.Name, | |
| 34 | - | project.UpdatedAt.Format("2006-01-02 15:04:05"), | |
| 35 | - | } | |
| 36 | 30 | links := "" | |
| 37 | 31 | if project.ProjectDir != project.Name { | |
| 38 | 32 | links = project.ProjectDir | |
| 39 | 33 | } | |
| 40 | - | row = append(row, links) | |
| 41 | - | row = append(row, | |
| 34 | + | fmt.Fprintf( | |
| 35 | + | writer, | |
| 36 | + | "%s\t%s\t%s\t%s\t%s\t%s\n", | |
| 37 | + | project.Name, | |
| 38 | + | project.UpdatedAt.Format("2006-01-02 15:04:05"), | |
| 39 | + | links, | |
| 42 | 40 | project.Acl.Type, | |
| 43 | 41 | strings.Join(project.Acl.Data, " "), | |
| 42 | + | project.Blocked, | |
| 44 | 43 | ) | |
| 45 | - | row = append(row, project.Blocked) | |
| 46 | - | data = append(data, row) | |
| 47 | - | } | |
| 48 | - | ||
| 49 | - | t := table.New(). | |
| 50 | - | Width(width). | |
| 51 | - | Headers(headers...). | |
| 52 | - | Rows(data...) | |
| 53 | - | return t | |
| 54 | - | } | |
| 55 | - | ||
| 56 | - | func getHelpText(width int) string { | |
| 57 | - | helpStr := "Commands: [help, stats, ls, fzf, rm, link, unlink, prune, retain, depends, acl, cache]\n" | |
| 58 | - | helpStr += "NOTICE:" + " *must* append with `--write` for the changes to persist.\n" | |
| 59 | - | ||
| 60 | - | projectName := "projA" | |
| 61 | - | headers := []string{"Cmd", "Description"} | |
| 62 | - | data := [][]string{ | |
| 63 | - | { | |
| 64 | - | "help", | |
| 65 | - | "prints this screen", | |
| 66 | - | }, | |
| 67 | - | { | |
| 68 | - | "stats", | |
| 69 | - | "usage statistics", | |
| 70 | - | }, | |
| 71 | - | { | |
| 72 | - | "ls", | |
| 73 | - | "lists projects", | |
| 74 | - | }, | |
| 75 | - | { | |
| 76 | - | fmt.Sprintf("fzf %s", projectName), | |
| 77 | - | fmt.Sprintf("lists urls of all assets in %s", projectName), | |
| 78 | - | }, | |
| 79 | - | { | |
| 80 | - | fmt.Sprintf("rm %s", projectName), | |
| 81 | - | fmt.Sprintf("delete %s", projectName), | |
| 82 | - | }, | |
| 83 | - | { | |
| 84 | - | fmt.Sprintf("link %s --to projB", projectName), | |
| 85 | - | fmt.Sprintf("symbolic link `%s` to `projB`", projectName), | |
| 86 | - | }, | |
| 87 | - | { | |
| 88 | - | fmt.Sprintf("unlink %s", projectName), | |
| 89 | - | fmt.Sprintf("removes symbolic link for `%s`", projectName), | |
| 90 | - | }, | |
| 91 | - | { | |
| 92 | - | fmt.Sprintf("prune %s", projectName), | |
| 93 | - | fmt.Sprintf("removes projects that match prefix `%s`", projectName), | |
| 94 | - | }, | |
| 95 | - | { | |
| 96 | - | fmt.Sprintf("retain %s", projectName), | |
| 97 | - | "alias to `prune` but keeps last N projects", | |
| 98 | - | }, | |
| 99 | - | { | |
| 100 | - | fmt.Sprintf("depends %s", projectName), | |
| 101 | - | fmt.Sprintf("lists all projects linked to `%s`", projectName), | |
| 102 | - | }, | |
| 103 | - | { | |
| 104 | - | fmt.Sprintf("acl %s", projectName), | |
| 105 | - | fmt.Sprintf("access control for `%s`", projectName), | |
| 106 | - | }, | |
| 107 | - | { | |
| 108 | - | fmt.Sprintf("cache %s", projectName), | |
| 109 | - | fmt.Sprintf("clear http cache for `%s`", projectName), | |
| 110 | - | }, | |
| 111 | 44 | } | |
| 112 | - | ||
| 113 | - | t := table.New(). | |
| 114 | - | Width(width). | |
| 115 | - | Border(lipgloss.RoundedBorder()). | |
| 116 | - | Headers(headers...). | |
| 117 | - | Rows(data...) | |
| 118 | - | ||
| 119 | - | helpStr += t.String() | |
| 120 | - | return helpStr | |
| 45 | + | writer.Flush() | |
| 121 | 46 | } | |
| 122 | 47 | ||
| 123 | 48 | type Cmd struct { |
| ... | ... | @@ -201,7 +126,68 @@ func (c *Cmd) RmProjectAssets(projectName string) error { | |
| 201 | 126 | } | |
| 202 | 127 | ||
| 203 | 128 | func (c *Cmd) help() { | |
| 204 | - | c.output(getHelpText(c.Width)) | |
| 129 | + | helpStr := "Commands: [help, stats, ls, fzf, rm, link, unlink, prune, retain, depends, acl, cache]\n" | |
| 130 | + | helpStr += "NOTICE:" + " *must* append with `--write` for the changes to persist.\n" | |
| 131 | + | c.output(helpStr) | |
| 132 | + | projectName := "projA" | |
| 133 | + | ||
| 134 | + | data := [][]string{ | |
| 135 | + | { | |
| 136 | + | "help", | |
| 137 | + | "prints this screen", | |
| 138 | + | }, | |
| 139 | + | { | |
| 140 | + | "stats", | |
| 141 | + | "usage statistics", | |
| 142 | + | }, | |
| 143 | + | { | |
| 144 | + | "ls", | |
| 145 | + | "lists projects", | |
| 146 | + | }, | |
| 147 | + | { | |
| 148 | + | fmt.Sprintf("fzf %s", projectName), | |
| 149 | + | fmt.Sprintf("lists urls of all assets in %s", projectName), | |
| 150 | + | }, | |
| 151 | + | { | |
| 152 | + | fmt.Sprintf("rm %s", projectName), | |
| 153 | + | fmt.Sprintf("delete %s", projectName), | |
| 154 | + | }, | |
| 155 | + | { | |
| 156 | + | fmt.Sprintf("link %s --to projB", projectName), | |
| 157 | + | fmt.Sprintf("symbolic link `%s` to `projB`", projectName), | |
| 158 | + | }, | |
| 159 | + | { | |
| 160 | + | fmt.Sprintf("unlink %s", projectName), | |
| 161 | + | fmt.Sprintf("removes symbolic link for `%s`", projectName), | |
| 162 | + | }, | |
| 163 | + | { | |
| 164 | + | fmt.Sprintf("prune %s", projectName), | |
| 165 | + | fmt.Sprintf("removes projects that match prefix `%s`", projectName), | |
| 166 | + | }, | |
| 167 | + | { | |
| 168 | + | fmt.Sprintf("retain %s", projectName), | |
| 169 | + | "alias to `prune` but keeps last N projects", | |
| 170 | + | }, | |
| 171 | + | { | |
| 172 | + | fmt.Sprintf("depends %s", projectName), | |
| 173 | + | fmt.Sprintf("lists all projects linked to `%s`", projectName), | |
| 174 | + | }, | |
| 175 | + | { | |
| 176 | + | fmt.Sprintf("acl %s", projectName), | |
| 177 | + | fmt.Sprintf("access control for `%s`", projectName), | |
| 178 | + | }, | |
| 179 | + | { | |
| 180 | + | fmt.Sprintf("cache %s", projectName), | |
| 181 | + | fmt.Sprintf("clear http cache for `%s`", projectName), | |
| 182 | + | }, | |
| 183 | + | } | |
| 184 | + | ||
| 185 | + | writer := NewTabWriter(c.Session) | |
| 186 | + | fmt.Fprintln(writer, "Cmd\tDescription") | |
| 187 | + | for _, dat := range data { | |
| 188 | + | fmt.Fprintf(writer, "%s\t%s\n", dat[0], dat[1]) | |
| 189 | + | } | |
| 190 | + | writer.Flush() | |
| 205 | 191 | } | |
| 206 | 192 | ||
| 207 | 193 | func (c *Cmd) stats(cfgMaxSize uint64) error { |
| ... | ... | @@ -229,20 +215,17 @@ func (c *Cmd) stats(cfgMaxSize uint64) error { | |
| 229 | 215 | return err | |
| 230 | 216 | } | |
| 231 | 217 | ||
| 232 | - | headers := []string{"Used (GB)", "Quota (GB)", "Used (%)", "Projects (#)"} | |
| 233 | - | data := []string{ | |
| 234 | - | fmt.Sprintf("%.4f", utils.BytesToGB(int(totalFileSize))), | |
| 235 | - | fmt.Sprintf("%.4f", utils.BytesToGB(int(storageMax))), | |
| 236 | - | fmt.Sprintf("%.4f", (float32(totalFileSize)/float32(storageMax))*100), | |
| 237 | - | fmt.Sprintf("%d", len(projects)), | |
| 238 | - | } | |
| 239 | - | ||
| 240 | - | t := table.New(). | |
| 241 | - | Width(c.Width). | |
| 242 | - | Border(lipgloss.RoundedBorder()). | |
| 243 | - | Headers(headers...). | |
| 244 | - | Rows(data) | |
| 245 | - | c.output(t.String()) | |
| 218 | + | writer := NewTabWriter(c.Session) | |
| 219 | + | fmt.Fprintln(writer, "Used (GB)\tQuota (GB)\tUsed (%)\tProjects (#)") | |
| 220 | + | fmt.Fprintf( | |
| 221 | + | writer, | |
| 222 | + | "%.4f\t%.4f\t%.4f\t%d\n", | |
| 223 | + | utils.BytesToGB(int(totalFileSize)), | |
| 224 | + | utils.BytesToGB(int(storageMax)), | |
| 225 | + | (float32(totalFileSize)/float32(storageMax))*100, | |
| 226 | + | len(projects), | |
| 227 | + | ) | |
| 228 | + | writer.Flush() | |
| 246 | 229 | ||
| 247 | 230 | return nil | |
| 248 | 231 | } |