mirror of
https://github.com/asciimoo/wuzz
synced 2024-11-10 13:10:29 +00:00
Merge pull request #51 from Benaiah/config-file
Add configuration system
This commit is contained in:
commit
46994a8c6c
85
config/config.go
Normal file
85
config/config.go
Normal file
@ -0,0 +1,85 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
// Duration is used to automatically unmarshal timeout strings to
|
||||
// time.Duration values
|
||||
type Duration struct {
|
||||
time.Duration
|
||||
}
|
||||
|
||||
func (d *Duration) UnmarshalText(text []byte) error {
|
||||
var err error
|
||||
d.Duration, err = time.ParseDuration(string(text))
|
||||
return err
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
General GeneralOptions
|
||||
Keys map[string]map[string]string
|
||||
}
|
||||
|
||||
type GeneralOptions struct {
|
||||
Timeout Duration
|
||||
FormatJSON bool
|
||||
PreserveScrollPosition bool
|
||||
DefaultURLScheme string
|
||||
}
|
||||
|
||||
var defaultTimeoutDuration, _ = time.ParseDuration("1m")
|
||||
|
||||
var DefaultConfig = Config{
|
||||
General: GeneralOptions{
|
||||
Timeout: Duration{
|
||||
defaultTimeoutDuration,
|
||||
},
|
||||
FormatJSON: true,
|
||||
PreserveScrollPosition: true,
|
||||
DefaultURLScheme: "https",
|
||||
},
|
||||
}
|
||||
|
||||
func LoadConfig(configFile string) (*Config, error) {
|
||||
if _, err := os.Stat(configFile); os.IsNotExist(err) {
|
||||
return nil, errors.New("Config file does not exist.")
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conf := DefaultConfig
|
||||
if _, err := toml.DecodeFile(configFile, &conf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &conf, nil
|
||||
}
|
||||
|
||||
func GetDefaultConfigLocation() string {
|
||||
var configFolderLocation string
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
// Use the XDG_CONFIG_HOME variable if it is set, otherwise
|
||||
// $HOME/.config/wuzz/config.toml
|
||||
xdgConfigHome := os.Getenv("XDG_CONFIG_HOME")
|
||||
if xdgConfigHome != "" {
|
||||
configFolderLocation = xdgConfigHome
|
||||
} else {
|
||||
configFolderLocation, _ = homedir.Expand("~/.config/wuzz/")
|
||||
}
|
||||
|
||||
default:
|
||||
// On other platforms we just use $HOME/.wuzz
|
||||
configFolderLocation, _ = homedir.Expand("~/.wuzz/")
|
||||
}
|
||||
|
||||
return filepath.Join(configFolderLocation, "config.toml")
|
||||
}
|
6
sample-config.toml
Normal file
6
sample-config.toml
Normal file
@ -0,0 +1,6 @@
|
||||
[general]
|
||||
|
||||
timeout = "1m"
|
||||
defaultURLScheme = "https"
|
||||
formatJSON = true
|
||||
preserveScrollPosition = true
|
82
wuzz.go
82
wuzz.go
@ -19,6 +19,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/asciimoo/wuzz/config"
|
||||
|
||||
"github.com/jroimartin/gocui"
|
||||
"github.com/mattn/go-runewidth"
|
||||
)
|
||||
@ -88,6 +90,7 @@ type App struct {
|
||||
historyIndex int
|
||||
currentPopup string
|
||||
history []*Request
|
||||
config *config.Config
|
||||
}
|
||||
|
||||
type ViewEditor struct {
|
||||
@ -201,7 +204,7 @@ func (a *App) Layout(g *gocui.Gui) error {
|
||||
v.Editable = true
|
||||
v.Overwrite = false
|
||||
v.Editor = &singlelineEditor{&defaultEditor}
|
||||
setViewTextAndCursor(v, "https://")
|
||||
setViewTextAndCursor(v, a.config.General.DefaultURLScheme+"://")
|
||||
}
|
||||
if v, err := g.SetView("get", 0, 3, splitX, splitY+1); err != nil {
|
||||
if err != gocui.ErrUnknownView {
|
||||
@ -425,7 +428,8 @@ func (a *App) SubmitRequest(g *gocui.Gui, _ *gocui.View) error {
|
||||
}
|
||||
|
||||
// pretty-print json
|
||||
if strings.Contains(response.Header.Get("Content-Type"), "application/json") {
|
||||
if strings.Contains(response.Header.Get("Content-Type"), "application/json") &&
|
||||
a.config.General.FormatJSON {
|
||||
var prettyJSON bytes.Buffer
|
||||
err := json.Indent(&prettyJSON, r.RawResponseBody, "", " ")
|
||||
if err == nil {
|
||||
@ -831,7 +835,29 @@ func (a *App) restoreRequest(g *gocui.Gui, idx int) {
|
||||
|
||||
}
|
||||
|
||||
func (a *App) ParseArgs(g *gocui.Gui) error {
|
||||
func (a *App) LoadConfig(configPath string) error {
|
||||
if configPath == "" {
|
||||
// Load config from default path
|
||||
configPath = config.GetDefaultConfigLocation()
|
||||
}
|
||||
|
||||
// If the config file doesn't exist, load the default config
|
||||
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
||||
a.config = &config.DefaultConfig
|
||||
return nil
|
||||
}
|
||||
|
||||
conf, err := config.LoadConfig(configPath)
|
||||
if err != nil {
|
||||
a.config = &config.DefaultConfig
|
||||
return err
|
||||
}
|
||||
|
||||
a.config = conf
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) ParseArgs(g *gocui.Gui, args []string) error {
|
||||
a.Layout(g)
|
||||
g.SetCurrentView(VIEWS[a.viewIndex])
|
||||
vheader, err := g.View("headers")
|
||||
@ -843,16 +869,16 @@ func (a *App) ParseArgs(g *gocui.Gui) error {
|
||||
vget.Clear()
|
||||
add_content_type := false
|
||||
arg_index := 1
|
||||
args_len := len(os.Args)
|
||||
args_len := len(args)
|
||||
for arg_index < args_len {
|
||||
arg := os.Args[arg_index]
|
||||
arg := args[arg_index]
|
||||
switch arg {
|
||||
case "-H", "--header":
|
||||
if arg_index == args_len-1 {
|
||||
return errors.New("No header value specified")
|
||||
}
|
||||
arg_index += 1
|
||||
header := os.Args[arg_index]
|
||||
header := args[arg_index]
|
||||
fmt.Fprintf(vheader, "%v\n", header)
|
||||
case "-d", "--data":
|
||||
if arg_index == args_len-1 {
|
||||
@ -865,7 +891,7 @@ func (a *App) ParseArgs(g *gocui.Gui) error {
|
||||
arg_index += 1
|
||||
add_content_type = true
|
||||
|
||||
data, _ := url.QueryUnescape(os.Args[arg_index])
|
||||
data, _ := url.QueryUnescape(args[arg_index])
|
||||
vdata, _ := g.View("data")
|
||||
setViewTextAndCursor(vdata, data)
|
||||
case "-X", "--request":
|
||||
@ -873,7 +899,7 @@ func (a *App) ParseArgs(g *gocui.Gui) error {
|
||||
return errors.New("No HTTP method specified")
|
||||
}
|
||||
arg_index++
|
||||
method := os.Args[arg_index]
|
||||
method := args[arg_index]
|
||||
if method == "POST" || method == "PUT" {
|
||||
add_content_type = true
|
||||
}
|
||||
@ -884,18 +910,18 @@ func (a *App) ParseArgs(g *gocui.Gui) error {
|
||||
return errors.New("No timeout value specified")
|
||||
}
|
||||
arg_index += 1
|
||||
timeout, err := strconv.Atoi(os.Args[arg_index])
|
||||
timeout, err := strconv.Atoi(args[arg_index])
|
||||
if err != nil || timeout <= 0 {
|
||||
return errors.New("Invalid timeout value")
|
||||
}
|
||||
CLIENT.Timeout = time.Duration(timeout) * time.Millisecond
|
||||
a.config.General.Timeout = config.Duration{time.Duration(timeout) * time.Millisecond}
|
||||
case "--compressed":
|
||||
vh, _ := g.View("headers")
|
||||
if strings.Index(getViewValue(g, "headers"), "Accept-Encoding") == -1 {
|
||||
fmt.Fprintln(vh, "Accept-Encoding: gzip, deflate")
|
||||
}
|
||||
default:
|
||||
u := os.Args[arg_index]
|
||||
u := args[arg_index]
|
||||
if strings.Index(u, "http://") != 0 && strings.Index(u, "https://") != 0 {
|
||||
u = "http://" + u
|
||||
}
|
||||
@ -922,6 +948,12 @@ func (a *App) ParseArgs(g *gocui.Gui) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Apply startup config values. This is run after a.ParseArgs, so that
|
||||
// args can override the provided config values
|
||||
func (a *App) InitConfig() {
|
||||
CLIENT.Timeout = a.config.General.Timeout.Duration
|
||||
}
|
||||
|
||||
func initApp(a *App, g *gocui.Gui) {
|
||||
g.Cursor = true
|
||||
g.InputEsc = false
|
||||
@ -995,7 +1027,9 @@ Key bindings:
|
||||
}
|
||||
|
||||
func main() {
|
||||
for _, arg := range os.Args {
|
||||
configPath := ""
|
||||
args := os.Args
|
||||
for i, arg := range os.Args {
|
||||
switch arg {
|
||||
case "-h", "--help":
|
||||
help()
|
||||
@ -1003,6 +1037,12 @@ func main() {
|
||||
case "-v", "--version":
|
||||
fmt.Printf("wuzz %v\n", VERSION)
|
||||
return
|
||||
case "-c", "--config":
|
||||
configPath = os.Args[i+1]
|
||||
args = append(os.Args[:i], os.Args[i+2:]...)
|
||||
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
||||
log.Fatal("Config file specified but does not exist: \"" + configPath + "\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
g, err := gocui.NewGui(gocui.Output256)
|
||||
@ -1020,7 +1060,23 @@ func main() {
|
||||
|
||||
initApp(app, g)
|
||||
|
||||
err = app.ParseArgs(g)
|
||||
// load config (must be done *before* app.ParseArgs, as arguments
|
||||
// should be able to override config values). An empty string passed
|
||||
// to LoadConfig results in LoadConfig loading the default config
|
||||
// location. If there is no config, the values in
|
||||
// config.DefaultConfig will be used.
|
||||
err = app.LoadConfig(configPath)
|
||||
if err != nil {
|
||||
g.Close()
|
||||
log.Fatalf("Error loading config file: %v", err)
|
||||
}
|
||||
|
||||
err = app.ParseArgs(g, args)
|
||||
|
||||
// Some of the values in the config need to have some startup
|
||||
// behavior associated with them. This is run after ParseArgs so
|
||||
// that command-line arguments can override configuration values.
|
||||
app.InitConfig()
|
||||
|
||||
if err != nil {
|
||||
g.Close()
|
||||
|
Loading…
Reference in New Issue
Block a user