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  }