2021-10-30 02:04:05 +00:00
package cmd
import (
"fmt"
"io"
"os"
2023-11-09 22:31:29 +00:00
"strings"
2021-10-30 02:04:05 +00:00
"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 ] == "-" ) {
2023-09-18 23:52:36 +00:00
return "" , nil , fmt . Errorf ( "write in place flag only applicable when giving an expression and at least one file" )
2022-04-26 23:08:50 +00:00
}
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 != "" {
2023-09-18 23:52:36 +00:00
return "" , nil , fmt . Errorf ( "write in place 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" {
2024-02-24 05:37:13 +00:00
inputFormat = yqlib . FormatStringFromFilename ( inputFilename )
2023-03-19 22:16:20 +00:00
2024-02-24 05:37:13 +00:00
_ , err := yqlib . FormatFromString ( inputFormat )
2023-03-19 22:16:20 +00:00
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 ( ) {
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.
//
2024-02-24 05:37:13 +00:00
outputFormat = yqlib . FormatStringFromFilename ( inputFilename )
2023-03-15 02:22:58 +00:00
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"
}
2024-02-24 05:37:13 +00:00
outputFormatType , err := yqlib . FormatFromString ( outputFormat )
2023-03-15 02:22:58 +00:00
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
2024-02-24 05:37:13 +00:00
if outputFormatType == yqlib . YamlFormat ||
outputFormatType == yqlib . PropertiesFormat {
2023-03-15 02:22:58 +00:00
unwrapScalar = true
}
2023-09-18 23:52:36 +00:00
if unwrapScalarFlag . IsExplicitlySet ( ) {
2023-03-15 02:22:58 +00:00
unwrapScalar = unwrapScalarFlag . IsSet ( )
}
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 ) {
2024-02-24 05:37:13 +00:00
format , err := yqlib . FormatFromString ( inputFormat )
2021-12-21 04:02:07 +00:00
if err != nil {
return nil , err
}
2024-02-24 05:37:13 +00:00
yqlib . ConfiguredYamlPreferences . EvaluateTogether = evaluateTogether
yqlibDecoder := format . DecoderFactory ( )
2023-03-01 02:19:06 +00:00
if yqlibDecoder == nil {
return nil , fmt . Errorf ( "no support for %s input format" , inputFormat )
}
2024-02-24 05:37:13 +00:00
return yqlibDecoder , nil
2021-12-21 04:02:07 +00:00
}
2024-02-24 05:37:13 +00:00
func configurePrinterWriter ( format * yqlib . Format , 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 ) {
2024-02-24 05:37:13 +00:00
yqlibOutputFormat , err := yqlib . FormatFromString ( outputFormat )
2023-03-01 02:19:06 +00:00
if err != nil {
return nil , err
}
2024-02-24 04:03:30 +00:00
yqlib . ConfiguredXMLPreferences . Indent = indent
2024-02-24 04:36:16 +00:00
yqlib . ConfiguredYamlPreferences . Indent = indent
2024-02-24 04:59:12 +00:00
yqlib . ConfiguredJSONPreferences . Indent = indent
2024-02-24 04:36:16 +00:00
yqlib . ConfiguredYamlPreferences . UnwrapScalar = unwrapScalar
2024-02-24 04:14:21 +00:00
yqlib . ConfiguredPropertiesPreferences . UnwrapScalar = unwrapScalar
2024-02-24 04:59:12 +00:00
yqlib . ConfiguredJSONPreferences . UnwrapScalar = unwrapScalar
2024-02-24 04:03:30 +00:00
2024-02-24 04:36:16 +00:00
yqlib . ConfiguredYamlPreferences . ColorsEnabled = colorsEnabled
2024-02-24 04:59:12 +00:00
yqlib . ConfiguredJSONPreferences . ColorsEnabled = colorsEnabled
2024-02-24 04:36:16 +00:00
yqlib . ConfiguredYamlPreferences . PrintDocSeparators = ! noDocSeparators
2024-02-24 05:37:13 +00:00
encoder := yqlibOutputFormat . EncoderFactory ( )
2024-02-24 04:59:12 +00:00
if encoder == nil {
2024-02-24 05:37:13 +00:00
return nil , fmt . Errorf ( "no support for %s output format" , outputFormat )
2024-02-24 04:59:12 +00:00
}
2024-02-24 05:37:13 +00:00
return encoder , err
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 {
2023-11-18 01:20:12 +00:00
stat , err := os . Stdin . Stat ( )
if err != nil {
yqlib . GetLogger ( ) . Debugf ( "error getting stdin: %v" , err )
}
pipingStdin := stat != nil && ( stat . Mode ( ) & os . ModeCharDevice ) == 0
2022-04-26 23:08:50 +00:00
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
2024-02-15 23:47:50 +00:00
args := processStdInArgs ( originalArgs )
maybeFirstArgIsAFile := len ( args ) > 0 && maybeFile ( args [ 0 ] )
if expressionFile == "" && maybeFirstArgIsAFile && strings . HasSuffix ( args [ 0 ] , ".yq" ) {
// lets check if an expression file was given
yqlib . GetLogger ( ) . Debug ( "Assuming arg %v is an expression file" , args [ 0 ] )
expressionFile = args [ 0 ]
args = args [ 1 : ]
}
2022-03-01 00:50:09 +00:00
if expressionFile != "" {
expressionBytes , err := os . ReadFile ( expressionFile )
if err != nil {
return "" , nil , err
}
2023-11-09 22:31:29 +00:00
//replace \r\n (windows) with good ol' unix file endings.
expression = strings . ReplaceAll ( string ( expressionBytes ) , "\r\n" , "\n" )
2022-03-01 00:50:09 +00:00
}
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
}