github.com/grafana/tanka@v0.26.1-0.20240506093700-c22cfc35c21a/cmd/tk/fmt.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  
     8  	"github.com/go-clix/cli"
     9  	"github.com/gobwas/glob"
    10  	"github.com/posener/complete"
    11  
    12  	"github.com/grafana/tanka/pkg/tanka"
    13  )
    14  
    15  // ArgStdin is the "magic" argument for reading from stdin
    16  const ArgStdin = "-"
    17  
    18  func fmtCmd() *cli.Command {
    19  	cmd := &cli.Command{
    20  		Use:   "fmt <FILES|DIRECTORIES>",
    21  		Short: "format Jsonnet code",
    22  		Args: cli.Args{
    23  			Validator: cli.ArgsMin(1),
    24  			Predictor: complete.PredictFiles("*.*sonnet"),
    25  		},
    26  	}
    27  
    28  	stdout := cmd.Flags().Bool("stdout", false, "print formatted contents to stdout instead of writing to disk")
    29  	test := cmd.Flags().BoolP("test", "t", false, "exit with non-zero when changes would be made")
    30  	exclude := cmd.Flags().StringSliceP("exclude", "e", []string{"**/.*", ".*", "**/vendor/**", "vendor/**"}, "globs to exclude")
    31  	verbose := cmd.Flags().BoolP("verbose", "v", false, "print each checked file")
    32  
    33  	cmd.Run = func(cmd *cli.Command, args []string) error {
    34  		if len(args) == 1 && args[0] == ArgStdin {
    35  			return fmtStdin(*test)
    36  		}
    37  
    38  		globs := make([]glob.Glob, len(*exclude))
    39  		for i, e := range *exclude {
    40  			g, err := glob.Compile(e)
    41  			if err != nil {
    42  				return err
    43  			}
    44  			globs[i] = g
    45  		}
    46  
    47  		var outFn tanka.OutFn
    48  		switch {
    49  		case *test:
    50  			outFn = func(name, content string) error { return nil }
    51  		case *stdout:
    52  			outFn = func(name, content string) error {
    53  				fmt.Printf("// %s\n%s", name, content)
    54  				fmt.Fprintln(os.Stderr) // some spacing
    55  				return nil
    56  			}
    57  		}
    58  
    59  		opts := &tanka.FormatOpts{
    60  			Excludes:   globs,
    61  			OutFn:      outFn,
    62  			PrintNames: *verbose,
    63  		}
    64  
    65  		changed, err := tanka.FormatFiles(args, opts)
    66  		if err != nil {
    67  			return err
    68  		}
    69  
    70  		if *verbose {
    71  			fmt.Fprintln(os.Stderr)
    72  		}
    73  
    74  		switch {
    75  		case *test && len(changed) > 0:
    76  			fmt.Fprintln(os.Stderr, "The following files are not properly formatted:")
    77  			for _, s := range changed {
    78  				fmt.Fprintln(os.Stderr, s)
    79  			}
    80  			os.Exit(ExitStatusDiff)
    81  		case len(changed) == 0:
    82  			fmt.Fprintln(os.Stderr, "All discovered files are already formatted. No changes were made")
    83  		case len(changed) > 0:
    84  			fmt.Fprintf(os.Stderr, "Formatted %v files", len(changed))
    85  		}
    86  
    87  		return nil
    88  	}
    89  
    90  	return cmd
    91  }
    92  
    93  func fmtStdin(test bool) error {
    94  	content, err := io.ReadAll(os.Stdin)
    95  	if err != nil {
    96  		return err
    97  	}
    98  
    99  	formatted, err := tanka.Format("<stdin>", string(content))
   100  	if err != nil {
   101  		return err
   102  	}
   103  
   104  	fmt.Print(formatted)
   105  	if test && string(content) != formatted {
   106  		os.Exit(ExitStatusDiff)
   107  	}
   108  	return nil
   109  }