2021-10-30 02:04:05 +00:00
package cmd
import (
"fmt"
"io"
"os"
"github.com/mikefarah/yq/v4/pkg/yqlib"
"github.com/spf13/cobra"
2022-02-06 21:46:43 +00:00
"gopkg.in/op/go-logging.v1"
2021-10-30 02:04:05 +00:00
)
2023-03-19 22:16:20 +00:00
func isAutomaticOutputFormat ( ) bool {
return outputFormat == "" || outputFormat == "auto" || outputFormat == "a"
}
2022-04-26 23:08:50 +00:00
func initCommand ( cmd * cobra . Command , args [ ] string ) ( string , [ ] string , error ) {
2021-10-30 02:04:05 +00:00
cmd . SilenceUsage = true
fileInfo , _ := os . Stdout . Stat ( )
if forceColor || ( ! forceNoColor && ( fileInfo . Mode ( ) & os . ModeCharDevice ) != 0 ) {
colorsEnabled = true
}
2022-04-26 23:08:50 +00:00
expression , args , err := processArgs ( args )
if err != nil {
return "" , nil , err
2021-10-30 02:04:05 +00:00
}
2022-04-29 01:08:41 +00:00
if splitFileExpFile != "" {
splitExpressionBytes , err := os . ReadFile ( splitFileExpFile )
if err != nil {
return "" , nil , err
}
splitFileExp = string ( splitExpressionBytes )
}
2021-11-25 09:24:51 +00:00
// backwards compatibility
2021-10-30 02:04:05 +00:00
if outputToJSON {
outputFormat = "json"
}
2022-04-26 23:08:50 +00:00
if writeInplace && ( len ( args ) == 0 || args [ 0 ] == "-" ) {
return "" , nil , fmt . Errorf ( "write inplace flag only applicable when giving an expression and at least one file" )
}
if frontMatter != "" && len ( args ) == 0 {
return "" , nil , fmt . Errorf ( "front matter flag only applicable when giving an expression and at least one file" )
2021-10-30 02:04:05 +00:00
}
if writeInplace && splitFileExp != "" {
2022-04-26 23:08:50 +00:00
return "" , nil , fmt . Errorf ( "write inplace cannot be used with split file" )
2021-10-30 02:04:05 +00:00
}
2022-04-26 23:08:50 +00:00
if nullInput && len ( args ) > 0 {
return "" , nil , fmt . Errorf ( "cannot pass files in when using null-input flag" )
2021-10-30 02:04:05 +00:00
}
2023-03-15 02:22:58 +00:00
inputFilename := ""
if len ( args ) > 0 {
inputFilename = args [ 0 ]
}
if inputFormat == "" || inputFormat == "auto" || inputFormat == "a" {
inputFormat = yqlib . FormatFromFilename ( inputFilename )
2023-03-19 22:16:20 +00:00
_ , err := yqlib . InputFormatFromString ( inputFormat )
if err != nil {
// unknown file type, default to yaml
yqlib . GetLogger ( ) . Debug ( "Unknown file format extension '%v', defaulting to yaml" , inputFormat )
inputFormat = "yaml"
if isAutomaticOutputFormat ( ) {
outputFormat = "yaml"
}
} else if isAutomaticOutputFormat ( ) {
// automatic input worked, we can do it for output too unless specified
2023-03-25 23:59:15 +00:00
if inputFormat == "toml" {
return "" , nil , fmt . Errorf ( "toml is not yet supported as an output format. Please specify another output format using the [--output-format/-o] flag" )
}
2023-03-19 22:16:20 +00:00
outputFormat = inputFormat
2023-03-15 02:22:58 +00:00
}
2023-03-19 22:16:20 +00:00
} else if isAutomaticOutputFormat ( ) {
2023-03-15 02:22:58 +00:00
// backwards compatibility -
// before this was introduced, `yq -pcsv things.csv`
// would produce *yaml* output.
//
outputFormat = yqlib . FormatFromFilename ( inputFilename )
if inputFilename != "-" {
yqlib . GetLogger ( ) . Warning ( "yq default output is now 'auto' (based on the filename extension). Normally yq would output '%v', but for backwards compatibility 'yaml' has been set. Please use -oy to specify yaml, or drop the -p flag." , outputFormat )
}
outputFormat = "yaml"
}
outputFormatType , err := yqlib . OutputFormatFromString ( outputFormat )
if err != nil {
return "" , nil , err
}
2023-03-19 22:16:20 +00:00
yqlib . GetLogger ( ) . Debug ( "Using input format %v" , inputFormat )
yqlib . GetLogger ( ) . Debug ( "Using output format %v" , outputFormat )
2023-03-15 02:22:58 +00:00
if outputFormatType == yqlib . YamlOutputFormat ||
outputFormatType == yqlib . PropsOutputFormat {
unwrapScalar = true
}
if unwrapScalarFlag . IsExplicitySet ( ) {
unwrapScalar = unwrapScalarFlag . IsSet ( )
}
//copy preference form global setting
yqlib . ConfiguredYamlPreferences . UnwrapScalar = unwrapScalar
yqlib . ConfiguredYamlPreferences . PrintDocSeparators = ! noDocSeparators
2022-04-26 23:08:50 +00:00
return expression , args , nil
2021-10-30 02:04:05 +00:00
}
2023-03-15 02:22:58 +00:00
func configureDecoder ( evaluateTogether bool ) ( yqlib . Decoder , error ) {
2021-12-21 04:02:07 +00:00
yqlibInputFormat , err := yqlib . InputFormatFromString ( inputFormat )
if err != nil {
return nil , err
}
2023-03-01 02:19:06 +00:00
yqlibDecoder , err := createDecoder ( yqlibInputFormat , evaluateTogether )
if yqlibDecoder == nil {
return nil , fmt . Errorf ( "no support for %s input format" , inputFormat )
}
return yqlibDecoder , err
}
func createDecoder ( format yqlib . InputFormat , evaluateTogether bool ) ( yqlib . Decoder , error ) {
switch format {
2022-02-07 00:55:55 +00:00
case yqlib . XMLInputFormat :
2022-10-25 03:27:16 +00:00
return yqlib . NewXMLDecoder ( yqlib . ConfiguredXMLPreferences ) , nil
2022-02-10 01:02:53 +00:00
case yqlib . PropertiesInputFormat :
return yqlib . NewPropertiesDecoder ( ) , nil
2022-07-27 02:26:22 +00:00
case yqlib . JsonInputFormat :
return yqlib . NewJSONDecoder ( ) , nil
2022-08-01 00:28:34 +00:00
case yqlib . CSVObjectInputFormat :
return yqlib . NewCSVObjectDecoder ( ',' ) , nil
case yqlib . TSVObjectInputFormat :
return yqlib . NewCSVObjectDecoder ( '\t' ) , nil
2023-03-25 23:59:15 +00:00
case yqlib . TomlInputFormat :
return yqlib . NewTomlDecoder ( ) , nil
2023-03-01 02:19:06 +00:00
case yqlib . YamlInputFormat :
prefs := yqlib . ConfiguredYamlPreferences
prefs . EvaluateTogether = evaluateTogether
return yqlib . NewYamlDecoder ( prefs ) , nil
2021-12-21 04:02:07 +00:00
}
2023-03-01 02:19:06 +00:00
return nil , fmt . Errorf ( "invalid decoder: %v" , format )
2021-12-21 04:02:07 +00:00
}
2022-01-26 22:29:41 +00:00
func configurePrinterWriter ( format yqlib . PrinterOutputFormat , out io . Writer ) ( yqlib . PrinterWriter , error ) {
2021-10-30 02:04:05 +00:00
var printerWriter yqlib . PrinterWriter
if splitFileExp != "" {
colorsEnabled = forceColor
2022-02-01 03:47:51 +00:00
splitExp , err := yqlib . ExpressionParser . ParseExpression ( splitFileExp )
2021-10-30 02:04:05 +00:00
if err != nil {
2022-01-26 22:29:41 +00:00
return nil , fmt . Errorf ( "bad split document expression: %w" , err )
2021-10-30 02:04:05 +00:00
}
printerWriter = yqlib . NewMultiPrinterWriter ( splitExp , format )
} else {
printerWriter = yqlib . NewSinglePrinterWriter ( out )
}
2022-01-26 22:29:41 +00:00
return printerWriter , nil
2021-10-30 02:04:05 +00:00
}
2022-01-15 00:57:59 +00:00
2023-03-01 02:19:06 +00:00
func configureEncoder ( ) ( yqlib . Encoder , error ) {
yqlibOutputFormat , err := yqlib . OutputFormatFromString ( outputFormat )
if err != nil {
return nil , err
}
yqlibEncoder , err := createEncoder ( yqlibOutputFormat )
if yqlibEncoder == nil {
return nil , fmt . Errorf ( "no support for %s output format" , outputFormat )
}
return yqlibEncoder , err
}
func createEncoder ( format yqlib . PrinterOutputFormat ) ( yqlib . Encoder , error ) {
2022-01-15 00:57:59 +00:00
switch format {
2022-02-07 00:55:55 +00:00
case yqlib . JSONOutputFormat :
2023-03-01 02:19:06 +00:00
return yqlib . NewJSONEncoder ( indent , colorsEnabled , unwrapScalar ) , nil
2022-01-15 00:57:59 +00:00
case yqlib . PropsOutputFormat :
2023-03-01 02:19:06 +00:00
return yqlib . NewPropertiesEncoder ( unwrapScalar ) , nil
2022-02-07 00:55:55 +00:00
case yqlib . CSVOutputFormat :
2023-03-01 02:19:06 +00:00
return yqlib . NewCsvEncoder ( ',' ) , nil
2022-02-07 00:55:55 +00:00
case yqlib . TSVOutputFormat :
2023-03-01 02:19:06 +00:00
return yqlib . NewCsvEncoder ( '\t' ) , nil
2022-01-15 00:57:59 +00:00
case yqlib . YamlOutputFormat :
2023-03-01 02:19:06 +00:00
return yqlib . NewYamlEncoder ( indent , colorsEnabled , yqlib . ConfiguredYamlPreferences ) , nil
2022-02-07 00:55:55 +00:00
case yqlib . XMLOutputFormat :
2023-03-01 02:19:06 +00:00
return yqlib . NewXMLEncoder ( indent , yqlib . ConfiguredXMLPreferences ) , nil
2022-01-15 00:57:59 +00:00
}
2023-03-01 02:19:06 +00:00
return nil , fmt . Errorf ( "invalid encoder: %v" , format )
2022-01-15 00:57:59 +00:00
}
2022-01-27 04:54:26 +00:00
// this is a hack to enable backwards compatibility with githubactions (which pipe /dev/null into everything)
// and being able to call yq with the filename as a single parameter
//
// without this - yq detects there is stdin (thanks githubactions),
// then tries to parse the filename as an expression
func maybeFile ( str string ) bool {
2022-02-06 21:46:43 +00:00
yqlib . GetLogger ( ) . Debugf ( "checking '%v' is a file" , str )
2022-02-06 21:04:26 +00:00
stat , err := os . Stat ( str ) // #nosec
2022-02-06 21:46:43 +00:00
result := err == nil && ! stat . IsDir ( )
if yqlib . GetLogger ( ) . IsEnabledFor ( logging . DEBUG ) {
if err != nil {
yqlib . GetLogger ( ) . Debugf ( "error: %v" , err )
} else {
yqlib . GetLogger ( ) . Debugf ( "error: %v, dir: %v" , err , stat . IsDir ( ) )
}
yqlib . GetLogger ( ) . Debugf ( "result: %v" , result )
}
return result
}
2022-04-26 23:08:50 +00:00
func processStdInArgs ( args [ ] string ) [ ] string {
stat , _ := os . Stdin . Stat ( )
pipingStdin := ( stat . Mode ( ) & os . ModeCharDevice ) == 0
2022-02-20 02:15:21 +00:00
// if we've been given a file, don't automatically
// read from stdin.
// this happens if there is more than one argument
// or only one argument and its a file
2022-04-26 23:24:25 +00:00
if nullInput || ! pipingStdin || len ( args ) > 1 || ( len ( args ) > 0 && maybeFile ( args [ 0 ] ) ) {
2022-02-06 21:46:43 +00:00
return args
}
for _ , arg := range args {
if arg == "-" {
return args
}
}
yqlib . GetLogger ( ) . Debugf ( "missing '-', adding it to the end" )
// we're piping from stdin, but there's no '-' arg
// lets add one to the end
return append ( args , "-" )
2022-01-27 04:54:26 +00:00
}
2022-02-06 22:27:52 +00:00
2022-04-26 23:08:50 +00:00
func processArgs ( originalArgs [ ] string ) ( string , [ ] string , error ) {
2022-03-01 00:50:09 +00:00
expression := forceExpression
if expressionFile != "" {
expressionBytes , err := os . ReadFile ( expressionFile )
if err != nil {
return "" , nil , err
}
expression = string ( expressionBytes )
}
2022-04-26 23:08:50 +00:00
args := processStdInArgs ( originalArgs )
2022-02-06 22:27:52 +00:00
yqlib . GetLogger ( ) . Debugf ( "processed args: %v" , args )
if expression == "" && len ( args ) > 0 && args [ 0 ] != "-" && ! maybeFile ( args [ 0 ] ) {
yqlib . GetLogger ( ) . Debug ( "assuming expression is '%v'" , args [ 0 ] )
expression = args [ 0 ]
args = args [ 1 : ]
}
2022-03-01 00:50:09 +00:00
return expression , args , nil
2022-02-06 22:27:52 +00:00
}