diff --git a/runner/config.go b/runner/config.go index d21b85e6..de251c8c 100644 --- a/runner/config.go +++ b/runner/config.go @@ -23,8 +23,8 @@ const ( // Config is the main configuration structure for Air. type Config struct { - Root string `toml:"root"` - TmpDir string `toml:"tmp_dir"` + Root string `toml:"root" usage:"Working directory, . or absolute path, please note that the directories following must be under root"` + TmpDir string `toml:"tmp_dir" usage:"Temporary directory for air"` TestDataDir string `toml:"testdata_dir"` Build cfgBuild `toml:"build"` Color cfgColor `toml:"color"` @@ -35,29 +35,29 @@ type Config struct { } type cfgBuild struct { - PreCmd []string `toml:"pre_cmd"` - Cmd string `toml:"cmd"` - PostCmd []string `toml:"post_cmd"` - Bin string `toml:"bin"` - FullBin string `toml:"full_bin"` - ArgsBin []string `toml:"args_bin"` - Log string `toml:"log"` - IncludeExt []string `toml:"include_ext"` - ExcludeDir []string `toml:"exclude_dir"` - IncludeDir []string `toml:"include_dir"` - ExcludeFile []string `toml:"exclude_file"` - IncludeFile []string `toml:"include_file"` - ExcludeRegex []string `toml:"exclude_regex"` - ExcludeUnchanged bool `toml:"exclude_unchanged"` - FollowSymlink bool `toml:"follow_symlink"` - Poll bool `toml:"poll"` - PollInterval int `toml:"poll_interval"` - Delay int `toml:"delay"` - StopOnError bool `toml:"stop_on_error"` - SendInterrupt bool `toml:"send_interrupt"` - KillDelay time.Duration `toml:"kill_delay"` - Rerun bool `toml:"rerun"` - RerunDelay int `toml:"rerun_delay"` + PreCmd []string `toml:"pre_cmd" usage:"Array of commands to run before each build"` + Cmd string `toml:"cmd" usage:"Just plain old shell command. You could use 'make' as well"` + PostCmd []string `toml:"post_cmd" usage:"Array of commands to run after ^C"` + Bin string `toml:"bin" usage:"Binary file yields from 'cmd'"` + FullBin string `toml:"full_bin" usage:"Customize binary, can setup environment variables when run your app"` + ArgsBin []string `toml:"args_bin" usage:"Add additional arguments when running binary (bin/full_bin)."` + Log string `toml:"log" usage:"This log file is placed in your tmp_dir"` + IncludeExt []string `toml:"include_ext" usage:"Watch these filename extensions"` + ExcludeDir []string `toml:"exclude_dir" usage:"Ignore these filename extensions or directories"` + IncludeDir []string `toml:"include_dir" usage:"Watch these directories if you specified"` + ExcludeFile []string `toml:"exclude_file" usage:"Exclude files"` + IncludeFile []string `toml:"include_file" usage:"Watch these files"` + ExcludeRegex []string `toml:"exclude_regex" usage:"Exclude specific regular expressions"` + ExcludeUnchanged bool `toml:"exclude_unchanged" usage:"Exclude unchanged files"` + FollowSymlink bool `toml:"follow_symlink" usage:"Follow symlink for directories"` + Poll bool `toml:"poll" usage:"Poll files for changes instead of using fsnotify"` + PollInterval int `toml:"poll_interval" usage:"Poll interval (defaults to the minimum interval of 500ms)"` + Delay int `toml:"delay" usage:"It's not necessary to trigger build each time file changes if it's too frequent"` + StopOnError bool `toml:"stop_on_error" usage:"Stop running old binary when build errors occur"` + SendInterrupt bool `toml:"send_interrupt" usage:"Send Interrupt signal before killing process (windows does not support this feature)"` + KillDelay time.Duration `toml:"kill_delay" usage:"Delay after sending Interrupt signal"` + Rerun bool `toml:"rerun" usage:"Rerun binary or not"` + RerunDelay int `toml:"rerun_delay" usage:"Delay after each execution"` regexCompiled []*regexp.Regexp } @@ -66,32 +66,32 @@ func (c *cfgBuild) RegexCompiled() ([]*regexp.Regexp, error) { } type cfgLog struct { - AddTime bool `toml:"time"` - MainOnly bool `toml:"main_only"` - Silent bool `toml:"silent"` + AddTime bool `toml:"time" usage:"Show log time"` + MainOnly bool `toml:"main_only" usage:"Only show main log (silences watcher, build, runner)"` + Silent bool `toml:"silent" usage:"silence all logs produced by air"` } type cfgColor struct { - Main string `toml:"main"` - Watcher string `toml:"watcher"` - Build string `toml:"build"` - Runner string `toml:"runner"` + Main string `toml:"main" usage:"Customize main part's color. If no color found, use the raw app log"` + Watcher string `toml:"watcher" usage:"Customize watcher part's color"` + Build string `toml:"build" usage:"Customize build part's color"` + Runner string `toml:"runner" usage:"Customize runner part's color"` App string `toml:"app"` } type cfgMisc struct { - CleanOnExit bool `toml:"clean_on_exit"` + CleanOnExit bool `toml:"clean_on_exit" usage:"Delete tmp directory on exit"` } type cfgScreen struct { - ClearOnRebuild bool `toml:"clear_on_rebuild"` - KeepScroll bool `toml:"keep_scroll"` + ClearOnRebuild bool `toml:"clear_on_rebuild" usage:"Clear screen on rebuild"` + KeepScroll bool `toml:"keep_scroll" usage:"Keep scroll position after rebuild"` } type cfgProxy struct { - Enabled bool `toml:"enabled"` - ProxyPort int `toml:"proxy_port"` - AppPort int `toml:"app_port"` + Enabled bool `toml:"enabled" usage:"Enable live-reloading on the browser"` + ProxyPort int `toml:"proxy_port" usage:"Port for proxy server"` + AppPort int `toml:"app_port" usage:"Port for your app"` } type sliceTransformer struct{} diff --git a/runner/flag.go b/runner/flag.go index af8f478b..44c568b2 100644 --- a/runner/flag.go +++ b/runner/flag.go @@ -8,10 +8,10 @@ const unsetDefault = "DEFAULT" // ParseConfigFlag parse toml information for flag func ParseConfigFlag(f *flag.FlagSet) map[string]TomlInfo { - c := Config{} + c := defaultConfig() m := flatConfig(c) for k, v := range m { - f.StringVar(v.Value, k, unsetDefault, "") + f.StringVar(v.Value, k, v.fieldValue, v.usage) } return m } diff --git a/runner/util.go b/runner/util.go index 2c6151a6..03d32afe 100644 --- a/runner/util.go +++ b/runner/util.go @@ -4,6 +4,7 @@ import ( "crypto/sha256" "encoding/hex" "errors" + "fmt" "log" "os" "path/filepath" @@ -315,9 +316,11 @@ func (a *checksumMap) updateFileChecksum(filename, newChecksum string) (ok bool) // TomlInfo is a struct for toml config file type TomlInfo struct { - fieldPath string - field reflect.StructField - Value *string + fieldPath string + field reflect.StructField + Value *string + fieldValue string + usage string } func setValue2Struct(v reflect.Value, fieldName string, value string) { @@ -372,29 +375,50 @@ func setValue2Struct(v reflect.Value, fieldName string, value string) { func flatConfig(stut interface{}) map[string]TomlInfo { m := make(map[string]TomlInfo) t := reflect.TypeOf(stut) - setTage2Map("", t, m, "") + v := reflect.ValueOf(stut) + setTage2Map("", t, v, m, "") return m } -func setTage2Map(root string, t reflect.Type, m map[string]TomlInfo, fieldPath string) { +func getFieldValueString(fieldValue reflect.Value) string { + switch fieldValue.Kind() { + case reflect.Slice: + sliceLen := fieldValue.Len() + strSlice := make([]string, sliceLen) + for j := 0; j < sliceLen; j++ { + strSlice[j] = fmt.Sprintf("%v", fieldValue.Index(j).Interface()) + } + return strings.Join(strSlice, ",") + default: + return fmt.Sprintf("%v", fieldValue.Interface()) + } +} + +func setTage2Map(root string, t reflect.Type, v reflect.Value, m map[string]TomlInfo, fieldPath string) { for i := 0; i < t.NumField(); i++ { field := t.Field(i) + fieldValue := v.Field(i) tomlVal := field.Tag.Get("toml") - switch field.Type.Kind() { - case reflect.Struct: + + if field.Type.Kind() == reflect.Struct { path := fieldPath + field.Name + "." - setTage2Map(root+tomlVal+".", field.Type, m, path) - default: - if tomlVal == "" { - continue - } - tomlPath := root + tomlVal - path := fieldPath + field.Name - var v *string - str := "" - v = &str - m[tomlPath] = TomlInfo{field: field, Value: v, fieldPath: path} + setTage2Map(root+tomlVal+".", field.Type, fieldValue, m, path) + continue + } + + if tomlVal == "" { + continue } + + tomlPath := root + tomlVal + path := fieldPath + field.Name + var v *string + str := "" + v = &str + + fieldValueStr := getFieldValueString(fieldValue) + usage := field.Tag.Get("usage") + m[tomlPath] = TomlInfo{field: field, Value: v, fieldPath: path, fieldValue: fieldValueStr, usage: usage} } }