github.com/pachyderm/pachyderm@v1.13.4/src/server/pfs/pretty/pretty.go (about) 1 package pretty 2 3 import ( 4 "fmt" 5 "html/template" 6 "io" 7 "os" 8 "strings" 9 10 units "github.com/docker/go-units" 11 "github.com/fatih/color" 12 "github.com/pachyderm/pachyderm/src/client/pfs" 13 "github.com/pachyderm/pachyderm/src/server/pkg/pretty" 14 ) 15 16 const ( 17 // RepoHeader is the header for repos. 18 RepoHeader = "NAME\tCREATED\tSIZE (MASTER)\tDESCRIPTION\t\n" 19 // RepoAuthHeader is the header for repos with auth information attached. 20 RepoAuthHeader = "NAME\tCREATED\tSIZE (MASTER)\tACCESS LEVEL\t\n" 21 // CommitHeader is the header for commits. 22 CommitHeader = "REPO\tBRANCH\tCOMMIT\tFINISHED\tSIZE\tPROGRESS\tDESCRIPTION\n" 23 // BranchHeader is the header for branches. 24 BranchHeader = "BRANCH\tHEAD\tTRIGGER\t\n" 25 // FileHeader is the header for files. 26 FileHeader = "NAME\tTYPE\tSIZE\t\n" 27 // FileHeaderWithCommit is the header for files that includes a commit field. 28 FileHeaderWithCommit = "COMMIT\tNAME\tTYPE\tCOMMITTED\tSIZE\t\n" 29 // DiffFileHeader is the header for files produced by diff file. 30 DiffFileHeader = "OP\t" + FileHeader 31 ) 32 33 // PrintRepoInfo pretty-prints repo info. 34 func PrintRepoInfo(w io.Writer, repoInfo *pfs.RepoInfo, fullTimestamps bool) { 35 fmt.Fprintf(w, "%s\t", repoInfo.Repo.Name) 36 if fullTimestamps { 37 fmt.Fprintf(w, "%s\t", repoInfo.Created.String()) 38 } else { 39 fmt.Fprintf(w, "%s\t", pretty.Ago(repoInfo.Created)) 40 } 41 fmt.Fprintf(w, "%s\t", units.BytesSize(float64(repoInfo.SizeBytes))) 42 if repoInfo.AuthInfo != nil { 43 fmt.Fprintf(w, "%s\t", repoInfo.AuthInfo.AccessLevel.String()) 44 } 45 fmt.Fprintf(w, "%s\t", repoInfo.Description) 46 fmt.Fprintln(w) 47 } 48 49 // PrintableRepoInfo is a wrapper around RepoInfo containing any formatting options 50 // used within the template to conditionally print information. 51 type PrintableRepoInfo struct { 52 *pfs.RepoInfo 53 FullTimestamps bool 54 } 55 56 // NewPrintableRepoInfo constructs a PrintableRepoInfo from just a RepoInfo. 57 func NewPrintableRepoInfo(ri *pfs.RepoInfo) *PrintableRepoInfo { 58 return &PrintableRepoInfo{ 59 RepoInfo: ri, 60 } 61 } 62 63 // PrintDetailedRepoInfo pretty-prints detailed repo info. 64 func PrintDetailedRepoInfo(repoInfo *PrintableRepoInfo) error { 65 template, err := template.New("RepoInfo").Funcs(funcMap).Parse( 66 `Name: {{.Repo.Name}}{{if .Description}} 67 Description: {{.Description}}{{end}}{{if .FullTimestamps}} 68 Created: {{.Created}}{{else}} 69 Created: {{prettyAgo .Created}}{{end}} 70 Size of HEAD on master: {{prettySize .SizeBytes}}{{if .AuthInfo}} 71 Access level: {{ .AuthInfo.AccessLevel.String }}{{end}} 72 `) 73 if err != nil { 74 return err 75 } 76 err = template.Execute(os.Stdout, repoInfo) 77 if err != nil { 78 return err 79 } 80 return nil 81 } 82 83 func printTrigger(trigger *pfs.Trigger) string { 84 var conds []string 85 if trigger.CronSpec != "" { 86 conds = append(conds, fmt.Sprintf("Cron(%s)", trigger.CronSpec)) 87 } 88 if trigger.Size_ != "" { 89 conds = append(conds, fmt.Sprintf("Size(%s)", trigger.Size_)) 90 } 91 if trigger.Commits != 0 { 92 conds = append(conds, fmt.Sprintf("Commits(%d)", trigger.Commits)) 93 } 94 cond := "" 95 if trigger.All { 96 cond = strings.Join(conds, " and ") 97 } else { 98 cond = strings.Join(conds, " or ") 99 } 100 return fmt.Sprintf("%s on %s", trigger.Branch, cond) 101 } 102 103 // PrintBranch pretty-prints a Branch. 104 func PrintBranch(w io.Writer, branchInfo *pfs.BranchInfo) { 105 fmt.Fprintf(w, "%s\t", branchInfo.Branch.Name) 106 if branchInfo.Head != nil { 107 fmt.Fprintf(w, "%s\t", branchInfo.Head.ID) 108 } else { 109 fmt.Fprintf(w, "-\t") 110 } 111 if branchInfo.Trigger != nil { 112 fmt.Fprintf(w, "%s\t", printTrigger(branchInfo.Trigger)) 113 } else { 114 fmt.Fprintf(w, "-\t") 115 } 116 fmt.Fprintln(w) 117 } 118 119 // PrintDetailedBranchInfo pretty-prints detailed branch info. 120 func PrintDetailedBranchInfo(branchInfo *pfs.BranchInfo) error { 121 template, err := template.New("BranchInfo").Funcs(funcMap).Parse( 122 `Name: {{.Branch.Repo.Name}}@{{.Branch.Name}}{{if .Head}} 123 Head Commit: {{ .Head.Repo.Name}}@{{.Head.ID}} {{end}}{{if .Provenance}} 124 Provenance: {{range .Provenance}} {{.Repo.Name}}@{{.Name}} {{end}} {{end}}{{if .Trigger}} 125 Trigger: {{printTrigger .Trigger}} {{end}} 126 `) 127 if err != nil { 128 return err 129 } 130 err = template.Execute(os.Stdout, branchInfo) 131 if err != nil { 132 return err 133 } 134 return nil 135 } 136 137 // PrintCommitInfo pretty-prints commit info. 138 func PrintCommitInfo(w io.Writer, commitInfo *pfs.CommitInfo, fullTimestamps bool) { 139 fmt.Fprintf(w, "%s\t", commitInfo.Commit.Repo.Name) 140 if commitInfo.Branch != nil { 141 fmt.Fprintf(w, "%s\t", commitInfo.Branch.Name) 142 } else { 143 fmt.Fprintf(w, "<none>\t") 144 } 145 fmt.Fprintf(w, "%s\t", commitInfo.Commit.ID) 146 if commitInfo.Finished == nil { 147 fmt.Fprintf(w, "-\t") 148 } else { 149 if fullTimestamps { 150 fmt.Fprintf(w, "%s\t", commitInfo.Finished.String()) 151 } else { 152 fmt.Fprintf(w, "%s\t", pretty.Ago(commitInfo.Finished)) 153 } 154 } 155 if commitInfo.Finished == nil { 156 fmt.Fprintf(w, "-\t") 157 } else { 158 fmt.Fprintf(w, "%s\t", units.BytesSize(float64(commitInfo.SizeBytes))) 159 } 160 if commitInfo.SubvenantCommitsTotal == 0 { 161 fmt.Fprintf(w, "-\t") 162 } else { 163 fmt.Fprintf(w, "%s\t", pretty.ProgressBar( 164 8, 165 int(commitInfo.SubvenantCommitsSuccess), 166 int(commitInfo.SubvenantCommitsTotal-commitInfo.SubvenantCommitsSuccess-commitInfo.SubvenantCommitsFailure), 167 int(commitInfo.SubvenantCommitsFailure))) 168 } 169 fmt.Fprintf(w, "%s\t", commitInfo.Description) 170 fmt.Fprintln(w) 171 } 172 173 // PrintableCommitInfo is a wrapper around CommitInfo containing any formatting options 174 // used within the template to conditionally print information. 175 type PrintableCommitInfo struct { 176 *pfs.CommitInfo 177 FullTimestamps bool 178 } 179 180 // NewPrintableCommitInfo constructs a PrintableCommitInfo from just a CommitInfo. 181 func NewPrintableCommitInfo(ci *pfs.CommitInfo) *PrintableCommitInfo { 182 return &PrintableCommitInfo{ 183 CommitInfo: ci, 184 } 185 } 186 187 // PrintDetailedCommitInfo pretty-prints detailed commit info. 188 func PrintDetailedCommitInfo(w io.Writer, commitInfo *PrintableCommitInfo) error { 189 template, err := template.New("CommitInfo").Funcs(funcMap).Parse( 190 `Commit: {{.Commit.Repo.Name}}@{{.Commit.ID}}{{if .Branch}} 191 Original Branch: {{.Branch.Name}}{{end}}{{if .Description}} 192 Description: {{.Description}}{{end}}{{if .ParentCommit}} 193 Parent: {{.ParentCommit.ID}}{{end}}{{if .FullTimestamps}} 194 Started: {{.Started}}{{else}} 195 Started: {{prettyAgo .Started}}{{end}}{{if .Finished}}{{if .FullTimestamps}} 196 Finished: {{.Finished}}{{else}} 197 Finished: {{prettyAgo .Finished}}{{end}}{{end}} 198 Size: {{prettySize .SizeBytes}}{{if .Provenance}} 199 Provenance: {{range .Provenance}} {{.Commit.Repo.Name}}@{{.Commit.ID}} ({{.Branch.Name}}) {{end}} {{end}} 200 `) 201 if err != nil { 202 return err 203 } 204 return template.Execute(w, commitInfo) 205 } 206 207 // PrintFileInfo pretty-prints file info. 208 // If recurse is false and directory size is 0, display "-" instead 209 // If fast is true and file size is 0, display "-" instead 210 func PrintFileInfo(w io.Writer, fileInfo *pfs.FileInfo, fullTimestamps, withCommit bool) { 211 if withCommit { 212 fmt.Fprintf(w, "%s\t", fileInfo.File.Commit.ID) 213 } 214 fmt.Fprintf(w, "%s\t", fileInfo.File.Path) 215 if fileInfo.FileType == pfs.FileType_FILE { 216 fmt.Fprint(w, "file\t") 217 } else { 218 fmt.Fprint(w, "dir\t") 219 } 220 if withCommit { 221 if fileInfo.Committed == nil { 222 fmt.Fprintf(w, "-\t") 223 } else if fullTimestamps { 224 fmt.Fprintf(w, "%s\t", fileInfo.Committed.String()) 225 } else { 226 fmt.Fprintf(w, "%s\t", pretty.Ago(fileInfo.Committed)) 227 } 228 } 229 fmt.Fprintf(w, "%s\t", units.BytesSize(float64(fileInfo.SizeBytes))) 230 fmt.Fprintln(w) 231 } 232 233 // PrintDiffFileInfo pretty-prints a file info from diff file. 234 func PrintDiffFileInfo(w io.Writer, added bool, fileInfo *pfs.FileInfo, fullTimestamps bool) { 235 if added { 236 fmt.Fprint(w, color.GreenString("+\t")) 237 } else { 238 fmt.Fprint(w, color.RedString("-\t")) 239 } 240 PrintFileInfo(w, fileInfo, fullTimestamps, false) 241 } 242 243 // PrintDetailedFileInfo pretty-prints detailed file info. 244 func PrintDetailedFileInfo(fileInfo *pfs.FileInfo) error { 245 template, err := template.New("FileInfo").Funcs(funcMap).Parse( 246 `Path: {{.File.Path}} 247 Type: {{fileType .FileType}} 248 Size: {{prettySize .SizeBytes}} 249 Children: {{range .Children}} {{.}} {{end}} 250 `) 251 if err != nil { 252 return err 253 } 254 return template.Execute(os.Stdout, fileInfo) 255 } 256 257 func fileType(fileType pfs.FileType) string { 258 if fileType == pfs.FileType_FILE { 259 return "file" 260 } 261 return "dir" 262 } 263 264 var funcMap = template.FuncMap{ 265 "prettyAgo": pretty.Ago, 266 "prettySize": pretty.Size, 267 "fileType": fileType, 268 "printTrigger": printTrigger, 269 } 270 271 // CompactPrintBranch renders 'b' as a compact string, e.g. 272 // "myrepo@master:/my/file" 273 func CompactPrintBranch(b *pfs.Branch) string { 274 return fmt.Sprintf("%s@%s", b.Repo.Name, b.Name) 275 } 276 277 // CompactPrintCommit renders 'c' as a compact string, e.g. 278 // "myrepo@123abc:/my/file" 279 func CompactPrintCommit(c *pfs.Commit) string { 280 return fmt.Sprintf("%s@%s", c.Repo.Name, c.ID) 281 } 282 283 // CompactPrintCommitSafe is similar to CompactPrintCommit but accepts 'nil' 284 // arguments 285 func CompactPrintCommitSafe(c *pfs.Commit) string { 286 if c == nil { 287 return "nil" 288 } 289 return CompactPrintCommit(c) 290 } 291 292 // CompactPrintFile renders 'f' as a compact string, e.g. 293 // "myrepo@master:/my/file" 294 func CompactPrintFile(f *pfs.File) string { 295 return fmt.Sprintf("%s@%s:%s", f.Commit.Repo.Name, f.Commit.ID, f.Path) 296 }