Skip to content

Commit

Permalink
feat: add support for json config and keyb files (#37)
Browse files Browse the repository at this point in the history
  • Loading branch information
kencx authored Aug 23, 2024
1 parent 665cd2f commit 57c6006
Show file tree
Hide file tree
Showing 6 changed files with 235 additions and 148 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
*.gitcommit

testdata/keyb*
!testdata/*.json
!testdata/*.yml
!testdata/*.yaml
keyb

dist/
106 changes: 61 additions & 45 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package config

import (
"encoding/json"
"errors"
"fmt"
"os"
Expand All @@ -16,69 +17,69 @@ const (
)

type Config struct {
Settings `yaml:"settings"`
Color `yaml:"color"`
Keys `yaml:"keys"`
Settings `yaml:"settings" json:"settings"`
Color `yaml:"color" json:"color"`
Keys `yaml:"keys" json:"keys"`
}

type Settings struct {
KeybPath string `yaml:"keyb_path"`
KeybPath string `yaml:"keyb_path" json:"keyb_path"`
Debug bool
Reverse bool
Mouse bool
SearchMode bool `yaml:"search_mode"`
SortKeys bool `yaml:"sort_keys"`
SearchMode bool `yaml:"search_mode" json:"search_mode"`
SortKeys bool `yaml:"sort_keys" json:"sort_keys"`
Title string
Prompt string
PromptLocation string `yaml:"prompt_location"`
PromptLocation string `yaml:"prompt_location" json:"prompt_location"`
Placeholder string
PrefixSep string `yaml:"prefix_sep"`
SepWidth int `yaml:"sep_width"`
PrefixSep string `yaml:"prefix_sep" json:"prefix_sep"`
SepWidth int `yaml:"sep_width" json:"sep_width"`
Margin int
Padding int
BorderStyle string `yaml:"border"`
BorderStyle string `yaml:"border" json:"border"`
}

type Color struct {
PromptColor string `yaml:"prompt"`
CursorFg string `yaml:"cursor_fg"`
CursorBg string `yaml:"cursor_bg"`
FilterFg string `yaml:"filter_fg"`
FilterBg string `yaml:"filter_bg"`
CounterFg string `yaml:"counter_fg"`
CounterBg string `yaml:"counter_bg"`
PlaceholderFg string `yaml:"placeholder_fg"`
PlaceholderBg string `yaml:"placeholder_bg"`
BorderColor string `yaml:"border_color"`
PromptColor string `yaml:"prompt" json:"prompt"`
CursorFg string `yaml:"cursor_fg" json:"cursor_fg"`
CursorBg string `yaml:"cursor_bg" json:"cursor_bg"`
FilterFg string `yaml:"filter_fg" json:"filter_fg"`
FilterBg string `yaml:"filter_bg" json:"filter_bg"`
CounterFg string `yaml:"counter_fg" json:"counter_fg"`
CounterBg string `yaml:"counter_bg" json:"counter_bg"`
PlaceholderFg string `yaml:"placeholder_fg" json:"placeholder_fg"`
PlaceholderBg string `yaml:"placeholder_bg" json:"placeholder_bg"`
BorderColor string `yaml:"border_color" json:"border_color"`
}

type Keys struct {
Quit string
Up string
Down string
UpFocus string `yaml:"up_focus"`
DownFocus string `yaml:"down_focus"`
HalfUp string `yaml:"half_up"`
HalfDown string `yaml:"half_down"`
FullUp string `yaml:"full_up"`
FullDown string `yaml:"full_bottom"`
GoToFirstLine string `yaml:"first_line"`
GoToLastLine string `yaml:"last_line"`
GoToTop string `yaml:"top"`
GoToMiddle string `yaml:"middle"`
GoToBottom string `yaml:"bottom"`
UpFocus string `yaml:"up_focus" json:"up_focus"`
DownFocus string `yaml:"down_focus" json:"down_focus"`
HalfUp string `yaml:"half_up" json:"half_up"`
HalfDown string `yaml:"half_down" json:"half_down"`
FullUp string `yaml:"full_up" json:"full_up"`
FullDown string `yaml:"full_bottom" json:"full_bottom"`
GoToFirstLine string `yaml:"first_line" json:"first_line"`
GoToLastLine string `yaml:"last_line" json:"last_line"`
GoToTop string `yaml:"top" json:"top"`
GoToMiddle string `yaml:"middle" json:"middle"`
GoToBottom string `yaml:"bottom" json:"bottom"`
Search string
ClearSearch string `yaml:"clear_search"`
ClearSearch string `yaml:"clear_search" json:"clear_search"`
Normal string
CursorWordForward string `yaml:"cursor_word_forward"`
CursorWordBackward string `yaml:"cursor_word_backward"`
CursorDeleteWordBackward string `yaml:"cursor_delete_word_backward"`
CursorDeleteWordForward string `yaml:"cursor_delete_word_forward"`
CursorDeleteAfterCursor string `yaml:"cursor_delete_after_cursor"`
CursorDeleteBeforeCursor string `yaml:"cursor_delete_before_cursor"`
CursorLineStart string `yaml:"cursor_line_start"`
CursorLineEnd string `yaml:"cursor_line_end"`
CursorPaste string `yaml:"cursor_paste"`
CursorWordForward string `yaml:"cursor_word_forward" json:"cursor_word_forward"`
CursorWordBackward string `yaml:"cursor_word_backward" json:"cursor_word_backward"`
CursorDeleteWordBackward string `yaml:"cursor_delete_word_backward" json:"cursor_delete_word_backward"`
CursorDeleteWordForward string `yaml:"cursor_delete_word_forward" json:"cursor_delete_word_forward"`
CursorDeleteAfterCursor string `yaml:"cursor_delete_after_cursor" json:"cursor_delete_after_cursor"`
CursorDeleteBeforeCursor string `yaml:"cursor_delete_before_cursor" json:"cursor_delete_before_cursor"`
CursorLineStart string `yaml:"cursor_line_start" json:"cursor_line_start"`
CursorLineEnd string `yaml:"cursor_line_end" json:"cursor_line_end"`
CursorPaste string `yaml:"cursor_paste" json:"cursor_paste"`
}

var DefaultConfig = &Config{
Expand Down Expand Up @@ -180,9 +181,17 @@ func UnmarshalConfig(configFile, basePath string) (*Config, error) {
}
}

if err = yaml.Unmarshal(file, &res); err != nil {
return nil, fmt.Errorf("failed to unmarshal config file \"%s\": %w", configFile, err)
switch filepath.Ext(configFile) {
case ".json":
if err = json.Unmarshal(file, &res); err != nil {
return nil, fmt.Errorf("failed to unmarshal config file \"%s\": %w", configFile, err)
}
case ".yaml", ".yml":
if err = yaml.Unmarshal(file, &res); err != nil {
return nil, fmt.Errorf("failed to unmarshal config file \"%s\": %w", configFile, err)
}
}

return res, nil
}

Expand Down Expand Up @@ -220,8 +229,15 @@ func UnmarshalKeyb(keybFile, basePath string) (Apps, error) {
}

var b Apps
if err := yaml.Unmarshal(file, &b); err != nil {
return nil, fmt.Errorf("failed to unmarshal keyb file: %w", err)
switch filepath.Ext(keybFile) {
case ".json":
if err = json.Unmarshal(file, &b); err != nil {
return nil, fmt.Errorf("failed to unmarshal keyb file: %w", err)
}
case ".yaml", ".yml":
if err := yaml.Unmarshal(file, &b); err != nil {
return nil, fmt.Errorf("failed to unmarshal keyb file: %w", err)
}
}
return b, nil
}
Expand Down
205 changes: 102 additions & 103 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,83 +10,84 @@ import (
const testBasePath = "../testdata"

func TestUnmarshalConfig(t *testing.T) {
t.Run("full config", func(t *testing.T) {
want := &Config{
Settings: Settings{
KeybPath: "./custom.yml",
Debug: true,
Reverse: true,
Mouse: false,
SearchMode: false,
SortKeys: true,
Title: "",
Prompt: "keys > ",
PromptLocation: "bottom",
Placeholder: "...",
PrefixSep: ";",
SepWidth: 4,
Margin: 1,
Padding: 1,
BorderStyle: "normal",
},
Color: Color{
FilterFg: "#FFA066",
},
Keys: Keys{
Quit: "q, ctrl+c",
Up: "k, up",
Down: "j, down",
UpFocus: "alt+k",
DownFocus: "alt+j",
HalfUp: "ctrl+u",
HalfDown: "ctrl+d",
FullUp: "ctrl+b",
FullDown: "ctrl+f",
GoToFirstLine: "g",
GoToLastLine: "G",
GoToTop: "H",
GoToMiddle: "M",
GoToBottom: "L",
Search: "/",
ClearSearch: "alt+d",
Normal: "esc",
CursorWordForward: "alt+right, alt+f",
CursorWordBackward: "alt+left, alt+b",
CursorDeleteWordBackward: "alt+backspace",
CursorDeleteWordForward: "alt+delete",
CursorDeleteAfterCursor: "alt+k",
CursorDeleteBeforeCursor: "alt+u",
CursorLineStart: "home, ctrl+a",
CursorLineEnd: "end, ctrl+e",
CursorPaste: "ctrl+v",
},
}
got, err := UnmarshalConfig(filepath.Join(testBasePath, "testConfig.yml"), testBasePath)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}

if !reflect.DeepEqual(got, want) {
t.Errorf("got %v, want %v", got, want)
}
})

t.Run("minimal config", func(t *testing.T) {
want := newDefaultConfig(testBasePath)

got, err := UnmarshalConfig(filepath.Join(testBasePath, "testConfigMinimal.yml"), testBasePath)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
testConfig := &Config{
Settings: Settings{
KeybPath: "./custom.yml",
Debug: true,
Reverse: true,
Mouse: false,
SearchMode: false,
SortKeys: true,
Title: "",
Prompt: "keys > ",
PromptLocation: "bottom",
Placeholder: "...",
PrefixSep: ";",
SepWidth: 4,
Margin: 1,
Padding: 1,
BorderStyle: "normal",
},
Color: Color{
FilterFg: "#FFA066",
},
Keys: Keys{
Quit: "q, ctrl+c",
Up: "k, up",
Down: "j, down",
UpFocus: "alt+k",
DownFocus: "alt+j",
HalfUp: "ctrl+u",
HalfDown: "ctrl+d",
FullUp: "ctrl+b",
FullDown: "ctrl+f",
GoToFirstLine: "g",
GoToLastLine: "G",
GoToTop: "H",
GoToMiddle: "M",
GoToBottom: "L",
Search: "/",
ClearSearch: "alt+d",
Normal: "esc",
CursorWordForward: "alt+right, alt+f",
CursorWordBackward: "alt+left, alt+b",
CursorDeleteWordBackward: "alt+backspace",
CursorDeleteWordForward: "alt+delete",
CursorDeleteAfterCursor: "alt+k",
CursorDeleteBeforeCursor: "alt+u",
CursorLineStart: "home, ctrl+a",
CursorLineEnd: "end, ctrl+e",
CursorPaste: "ctrl+v",
},
}

configFileTests := []struct {
name string
file string
want *Config
}{
{"full config yaml", "testConfig.yml", testConfig},
{"full config json", "testConfig.json", testConfig},
{"minimal config yaml", "testConfigMinimal.yml", newDefaultConfig(testBasePath)},
{"minimal config json", "testConfigMinimal.json", newDefaultConfig(testBasePath)},
{"config file absent", "testConfigAbsent.yml", newDefaultConfig(testBasePath)},
}

for _, tt := range configFileTests {
t.Run(tt.name, func(t *testing.T) {
got, err := UnmarshalConfig(filepath.Join(testBasePath, tt.file), testBasePath)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}

if !reflect.DeepEqual(got, want) {
t.Errorf("got %v, want %v", got, want)
}
})
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("got %v, want %v", got, tt.want)
}
})
}

t.Run("empty config file path", func(t *testing.T) {
want := newDefaultConfig(testBasePath)

got, err := UnmarshalConfig("", testBasePath)
if err != nil {
t.Fatalf("unexpected err: %v", err)
Expand All @@ -96,40 +97,38 @@ func TestUnmarshalConfig(t *testing.T) {
t.Errorf("got %v, want %v", got, want)
}
})

t.Run("config file absent", func(t *testing.T) {
want := newDefaultConfig(testBasePath)

got, err := UnmarshalConfig(filepath.Join(testBasePath, "testConfigAbsent.yml"), testBasePath)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}

if !reflect.DeepEqual(got, want) {
t.Errorf("got %v, want %v", got, want)
}
})
}

func TestUnmarshalKeyb(t *testing.T) {
t.Run("file present", func(t *testing.T) {
want := Apps{{
Name: "test",
Keybinds: []KeyBind{{
Name: "foo",
Key: "bar",
}},
}}

got, err := UnmarshalKeyb(filepath.Join(testBasePath, "testkeyb.yml"), testBasePath)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
apps := Apps{{
Name: "test",
Keybinds: []KeyBind{{
Name: "foo",
Key: "bar",
}},
}}

keybFileTests := []struct {
name string
file string
want Apps
}{
{"keyb file yaml", "testkeyb.yml", apps},
{"keyb file json", "testkeyb.json", apps},
}

for _, tt := range keybFileTests {
t.Run(tt.name, func(t *testing.T) {
got, err := UnmarshalKeyb(filepath.Join(testBasePath, tt.file), testBasePath)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}

if !reflect.DeepEqual(got, want) {
t.Errorf("got %v, want %v", got, want)
}
})
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("got %v, want %v", got, tt.want)
}
})
}

t.Run("file absent", func(t *testing.T) {
_, err := UnmarshalKeyb(filepath.Join(testBasePath, "temp.yml"), testBasePath)
Expand Down
Loading

0 comments on commit 57c6006

Please sign in to comment.