github.com/xztaityozx/sel@v1.1.10/internal/option/option.go (about)

     1  package option
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"text/template"
     8  
     9  	"github.com/spf13/viper"
    10  )
    11  
    12  // Option is commandline options
    13  type Option struct {
    14  	// --input/output-delimiter option
    15  	DelimiterOption
    16  	// -f, --input-files option
    17  	InputFiles
    18  	// XSV support
    19  	Xsv
    20  	// --template
    21  	Template *template.Template
    22  }
    23  
    24  // DelimiterOption is setting for --input/output-delimiter option
    25  type DelimiterOption struct {
    26  	// --input-delimiter
    27  	InputDelimiter string
    28  	// --output-delimiter
    29  	OutPutDelimiter string
    30  	// --remove-empty
    31  	RemoveEmpty bool
    32  	// --use-regexp
    33  	UseRegexp bool
    34  	// --split-before
    35  	SplitBefore bool
    36  }
    37  
    38  // InputFiles is setting for -f, --input-files option
    39  type InputFiles struct {
    40  	Files []string
    41  }
    42  
    43  // Enumerate /path/to/input/files
    44  func (ifs InputFiles) Enumerate() ([]string, error) {
    45  	if ifs.Files == nil || len(ifs.Files) == 0 {
    46  		return nil, fmt.Errorf("there are no files")
    47  	}
    48  
    49  	var rt []string
    50  
    51  	for _, v := range ifs.Files {
    52  		expanded, err := filepath.Glob(v)
    53  		if err != nil {
    54  			return nil, err
    55  		}
    56  
    57  		for _, p := range expanded {
    58  			fi, err := os.Stat(p)
    59  			if err != nil {
    60  				return nil, err
    61  			} else if !fi.Mode().IsRegular() {
    62  				return nil, fmt.Errorf("%s is not regular file", p)
    63  			} else if fi.IsDir() {
    64  				return nil, fmt.Errorf("%s is directory", p)
    65  			}
    66  
    67  			rt = append(rt, p)
    68  		}
    69  	}
    70  
    71  	if len(rt) == 0 {
    72  		return nil, fmt.Errorf("no files(path/glob is wrong?)")
    73  	}
    74  
    75  	return rt, nil
    76  }
    77  
    78  const (
    79  	NameInputDelimiter  = "input-delimiter"
    80  	NameOutPutDelimiter = "output-delimiter"
    81  	NameRemoveEmpty     = "remove-empty"
    82  	NameUseRegexp       = "use-regexp"
    83  	NameInputFiles      = "input-files"
    84  	NameSplitBefore     = "split-before"
    85  	NameFieldSplit      = "field-split"
    86  	NameCsv             = "csv"
    87  	NameTsv             = "tsv"
    88  	NameTemplate        = "template"
    89  
    90  	DefaultTemplate = ""
    91  )
    92  
    93  type SplitStrategy int
    94  
    95  func GetOptionNames() []string {
    96  	return []string{
    97  		NameInputFiles,
    98  		NameInputDelimiter,
    99  		NameOutPutDelimiter,
   100  		NameUseRegexp,
   101  		NameRemoveEmpty,
   102  		NameSplitBefore,
   103  		NameFieldSplit,
   104  		NameCsv,
   105  		NameTsv,
   106  		NameTemplate,
   107  	}
   108  }
   109  
   110  // Xsv is option group for xsv support
   111  type Xsv struct {
   112  	Csv bool
   113  	Tsv bool
   114  }
   115  
   116  func (x Xsv) IsXsv() (bool, rune) {
   117  	if x.Csv {
   118  		return true, ','
   119  	} else if x.Tsv {
   120  		return true, '\t'
   121  	} else {
   122  		return false, ','
   123  	}
   124  }
   125  
   126  // NewOption は viper.Viper からフラグの値を取り出して Option を作って返す
   127  func NewOption(v *viper.Viper) (Option, error) {
   128  
   129  	// 入力のデリミターの受け取り、NameFieldSplit が指定されているときは `\s+` で上書き
   130  	inputDelimiter := v.GetString(NameInputDelimiter)
   131  	if v.GetBool(NameFieldSplit) {
   132  		inputDelimiter = `\s+`
   133  	}
   134  
   135  	// デリミターを正規表現として処理するかどうか。NameFieldSplit が指定されているときは強制的にON
   136  	useRegexp := v.GetBool(NameUseRegexp) || v.GetBool(NameFieldSplit)
   137  
   138  	// --templateオプションで出力のフォーマットを指定するやつ
   139  	var tmpl *template.Template
   140  	if v.GetString(NameTemplate) != DefaultTemplate {
   141  		var err error
   142  		tmpl, err = parseTemplate(v.GetString(NameTemplate))
   143  		if err != nil {
   144  			return Option{}, err
   145  		}
   146  	}
   147  
   148  	return Option{
   149  		DelimiterOption: DelimiterOption{
   150  			InputDelimiter:  inputDelimiter,
   151  			OutPutDelimiter: v.GetString(NameOutPutDelimiter),
   152  			RemoveEmpty:     v.GetBool(NameRemoveEmpty),
   153  			UseRegexp:       useRegexp,
   154  			SplitBefore:     v.GetBool(NameSplitBefore),
   155  		},
   156  		InputFiles: InputFiles{v.GetStringSlice(NameInputFiles)},
   157  		Xsv: Xsv{
   158  			Csv: v.GetBool(NameCsv),
   159  			Tsv: v.GetBool(NameTsv),
   160  		},
   161  		Template: tmpl,
   162  	}, nil
   163  }
   164  
   165  func parseTemplate(input string) (*template.Template, error) {
   166  	var result string
   167  
   168  	// input ::= char | marker | input
   169  	// char ::= 任意の文字
   170  	// marker ::= {}
   171  	cnt := 0
   172  	for i := 0; i < len(input); i++ {
   173  		switch {
   174  		case input[i] == '{' && i+1 < len(input) && input[i+1] == '}':
   175  			result += fmt.Sprintf("{{ index . %d }}", cnt)
   176  			cnt++
   177  			i++
   178  		default:
   179  			result += string(input[i])
   180  		}
   181  	}
   182  
   183  	return template.New("output").Parse(result)
   184  }