github.com/zkry/enry@v1.6.3/cli/enry/main.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"flag"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"log"
    10  	"os"
    11  	"path/filepath"
    12  	"sort"
    13  	"strings"
    14  
    15  	"gopkg.in/src-d/enry.v1"
    16  	"gopkg.in/src-d/enry.v1/data"
    17  )
    18  
    19  var (
    20  	version = "undefined"
    21  	build   = "undefined"
    22  	commit  = "undefined"
    23  )
    24  
    25  func main() {
    26  	flag.Usage = usage
    27  	breakdownFlag := flag.Bool("breakdown", false, "")
    28  	jsonFlag := flag.Bool("json", false, "")
    29  	showVersion := flag.Bool("version", false, "Show the enry version information")
    30  	flag.Parse()
    31  
    32  	if *showVersion {
    33  		fmt.Println(version)
    34  		return
    35  	}
    36  
    37  	root, err := filepath.Abs(flag.Arg(0))
    38  	if err != nil {
    39  		log.Fatal(err)
    40  	}
    41  
    42  	fileInfo, err := os.Stat(root)
    43  	if err != nil {
    44  		log.Fatal(err)
    45  	}
    46  
    47  	if fileInfo.Mode().IsRegular() {
    48  		printFileAnalysis(root)
    49  		return
    50  	}
    51  
    52  	out := make(map[string][]string, 0)
    53  	err = filepath.Walk(root, func(path string, f os.FileInfo, err error) error {
    54  		if err != nil {
    55  			log.Println(err)
    56  			return filepath.SkipDir
    57  		}
    58  
    59  		if !f.Mode().IsDir() && !f.Mode().IsRegular() {
    60  			return nil
    61  		}
    62  
    63  		relativePath, err := filepath.Rel(root, path)
    64  		if err != nil {
    65  			log.Println(err)
    66  			return nil
    67  		}
    68  
    69  		if relativePath == "." {
    70  			return nil
    71  		}
    72  
    73  		if f.IsDir() {
    74  			relativePath = relativePath + "/"
    75  		}
    76  
    77  		if enry.IsVendor(relativePath) || enry.IsDotFile(relativePath) ||
    78  			enry.IsDocumentation(relativePath) || enry.IsConfiguration(relativePath) {
    79  			if f.IsDir() {
    80  				return filepath.SkipDir
    81  			}
    82  
    83  			return nil
    84  		}
    85  
    86  		if f.IsDir() {
    87  			return nil
    88  		}
    89  
    90  		language, ok := enry.GetLanguageByExtension(path)
    91  		if !ok {
    92  			if language, ok = enry.GetLanguageByFilename(path); !ok {
    93  				content, err := ioutil.ReadFile(path)
    94  				if err != nil {
    95  					log.Println(err)
    96  					return nil
    97  				}
    98  
    99  				language = enry.GetLanguage(filepath.Base(path), content)
   100  				if language == enry.OtherLanguage {
   101  					return nil
   102  				}
   103  			}
   104  		}
   105  
   106  		out[language] = append(out[language], relativePath)
   107  		return nil
   108  	})
   109  
   110  	if err != nil {
   111  		log.Fatal(err)
   112  	}
   113  
   114  	var buff bytes.Buffer
   115  	switch {
   116  	case *jsonFlag && !*breakdownFlag:
   117  		printJson(out, &buff)
   118  	case *jsonFlag && *breakdownFlag:
   119  		printBreakDown(out, &buff)
   120  	case *breakdownFlag:
   121  		printPercents(out, &buff)
   122  		buff.WriteByte('\n')
   123  		printBreakDown(out, &buff)
   124  	default:
   125  		printPercents(out, &buff)
   126  	}
   127  
   128  	fmt.Print(buff.String())
   129  }
   130  
   131  func usage() {
   132  	fmt.Fprintf(
   133  		os.Stderr,
   134  		`  %[1]s %[2]s build: %[3]s commit: %[4]s, based on linguist commit: %[5]s
   135    %[1]s, A simple (and faster) implementation of github/linguist
   136    usage: %[1]s <path>
   137           %[1]s [-json] [-breakdown] <path>
   138           %[1]s [-json] [-breakdown]
   139           %[1]s [-version]
   140  `,
   141  		os.Args[0], version, build, commit, data.LinguistCommit[:7],
   142  	)
   143  }
   144  
   145  func printBreakDown(out map[string][]string, buff *bytes.Buffer) {
   146  	for name, language := range out {
   147  		writeStringLn(name, buff)
   148  		for _, file := range language {
   149  			writeStringLn(file, buff)
   150  		}
   151  
   152  		writeStringLn("", buff)
   153  	}
   154  }
   155  
   156  func printJson(out map[string][]string, buff *bytes.Buffer) {
   157  	data, _ := json.Marshal(out)
   158  	buff.Write(data)
   159  	buff.WriteByte('\n')
   160  }
   161  
   162  func printPercents(out map[string][]string, buff *bytes.Buffer) {
   163  	var fileCountList enry.FileCountList
   164  	total := 0
   165  	for name, language := range out {
   166  		fc := enry.FileCount{Name: name, Count: len(language)}
   167  		fileCountList = append(fileCountList, fc)
   168  		total += len(language)
   169  	}
   170  	// Sort the fileCountList in descending order of their count value.
   171  	sort.Sort(sort.Reverse(fileCountList))
   172  
   173  	for _, fc := range fileCountList {
   174  		percent := float32(fc.Count) / float32(total) * 100
   175  		buff.WriteString(fmt.Sprintf("%.2f%%	%s\n", percent, fc.Name))
   176  	}
   177  }
   178  
   179  func printFileAnalysis(file string) {
   180  	content, err := ioutil.ReadFile(file)
   181  	if err != nil {
   182  		fmt.Println(err)
   183  	}
   184  
   185  	totalLines, nonBlank := getLines(file, string(content))
   186  	fileType := getFileType(file, content)
   187  	language := enry.GetLanguage(file, content)
   188  	mimeType := enry.GetMimeType(file, language)
   189  
   190  	fmt.Printf(
   191  		`%s: %d lines (%d sloc)
   192    type:      %s
   193    mime_type: %s
   194    language:  %s
   195  `,
   196  		filepath.Base(file), totalLines, nonBlank, fileType, mimeType, language,
   197  	)
   198  }
   199  
   200  func getLines(file string, content string) (int, int) {
   201  	totalLines := strings.Count(content, "\n")
   202  	nonBlank := totalLines - strings.Count(content, "\n\n")
   203  	return totalLines, nonBlank
   204  }
   205  
   206  func getFileType(file string, content []byte) string {
   207  	switch {
   208  	case enry.IsImage(file):
   209  		return "Image"
   210  	case enry.IsBinary(content):
   211  		return "Binary"
   212  	default:
   213  		return "Text"
   214  	}
   215  }
   216  
   217  func writeStringLn(s string, buff *bytes.Buffer) {
   218  	buff.WriteString(s)
   219  	buff.WriteByte('\n')
   220  }