From d79274f4288f7c4bdd384a67c27604632d28b1a1 Mon Sep 17 00:00:00 2001 From: Robert Kaussow Date: Tue, 5 Nov 2024 14:35:26 +0100 Subject: [PATCH] feat: add option for json output (#109) --- README.md | 8 +++- cmd/url-parser/main.go | 1 + command/commands.go | 97 ++++++++++++++++++++++++++++++++++++++-- command/commands_test.go | 62 ++++++++++++++++++------- command/fragment.go | 2 +- command/fragment_test.go | 24 +++++----- command/host.go | 4 +- command/host_test.go | 24 +++++----- command/password.go | 9 ++-- command/password_test.go | 24 +++++----- command/path.go | 2 +- command/path_test.go | 25 ++++++----- command/port.go | 4 +- command/port_test.go | 24 +++++----- command/query.go | 13 ++---- command/query_test.go | 30 ++++++------- command/run.go | 22 ++++++++- command/run_test.go | 73 +++++++++++++++++++++++++----- command/scheme.go | 2 +- command/scheme_test.go | 24 +++++----- command/user.go | 8 ++-- command/user_test.go | 24 +++++----- config/config.go | 2 + go.mod | 4 ++ go.sum | 10 +++++ 25 files changed, 361 insertions(+), 161 deletions(-) diff --git a/README.md b/README.md index f305570..3531c70 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ NAME: url-parser - Parse URL and shows the part of it. USAGE: - url-parser [global options] command [command options] [arguments...] + url-parser [global options] command [command options] VERSION: devel @@ -45,7 +45,7 @@ COMMANDS: user, u Get username from url password, pw Get password from url path, pt Get path from url - host, h Get hostname from url + host, ht Get hostname from url port, p Get port from url query, q Get query from url fragment, f Get fragment from url @@ -81,6 +81,10 @@ somevalue # It is also possible to read the URL from stdin $ echo "https://somedomain.com" | url-parser host somedomain.com + +# Get json output or all parsed parts +$ url-parser --url https://somedomain.com/?some-key=somevalue all --json +{"scheme":"https","hostname":"somedomain.com","port":"","path":"/","fragment":"","rawQuery":"some-key=somevalue","queryParams":[{"key":"some-key","value":"somevalue"}],"username":"","password":""} ``` ## Contributors diff --git a/cmd/url-parser/main.go b/cmd/url-parser/main.go index add0a72..1bc82ba 100644 --- a/cmd/url-parser/main.go +++ b/cmd/url-parser/main.go @@ -47,6 +47,7 @@ func main() { Aliases: []string{"a"}, Usage: "Get all parts from url", Action: command.Run(cfg), + Flags: command.AllFlags(cfg), }, { Name: "scheme", diff --git a/command/commands.go b/command/commands.go index f3825ba..a3fc530 100644 --- a/command/commands.go +++ b/command/commands.go @@ -8,13 +8,102 @@ import ( "github.com/thegeeklab/url-parser/config" ) -func parseURL(raw string) *url.URL { - urlString := strings.TrimSpace(raw) +type QueryParam struct { + Key string `json:"key"` + Value string `json:"value"` +} - url, err := url.Parse(urlString) +type URL struct { + url *url.URL + + Scheme string `json:"scheme"` + Hostname string `json:"hostname"` + Port string `json:"port"` + Path string `json:"path"` + Fragment string `json:"fragment"` + RawQuery string `json:"rawQuery"` + Query string `json:"-"` + QueryParams []QueryParam `json:"queryParams"` + Username string `json:"username"` + Password string `json:"password"` +} + +func (u *URL) String() string { + return u.url.String() +} + +type Parser struct { + URL string + QueryField string + QuerySplit bool +} + +func NewURLParser(url, queryField string, querySplit bool) *Parser { + return &Parser{ + URL: url, + QueryField: queryField, + QuerySplit: querySplit, + } +} + +func (p *Parser) parse() *URL { + urlString := strings.TrimSpace(p.URL) + + parts, err := url.Parse(urlString) if err != nil { log.Fatal().Err(err).Msg(config.ErrParseURL.Error()) } - return url + extURL := &URL{ + url: parts, + Scheme: parts.Scheme, + Hostname: parts.Hostname(), + Path: parts.Path, + Fragment: parts.Fragment, + QueryParams: []QueryParam{}, + } + + if len(parts.Scheme) > 0 { + extURL.Hostname = parts.Hostname() + extURL.Port = parts.Port() + } + + if parts.User != nil { + if len(parts.User.Username()) > 0 { + extURL.Username = parts.User.Username() + } + } + + if parts.User != nil { + pw, _ := parts.User.Password() + if len(pw) > 0 { + extURL.Password = pw + } + } + + // Handle query field extraction + if parts.RawQuery != "" { + extURL.RawQuery = parts.RawQuery + } + + if p.QueryField != "" { + if result := parts.Query().Get(p.QueryField); result != "" { + extURL.Query = result + } + } else { + extURL.Query = parts.RawQuery + } + + // Handle query parameter splitting + values := parts.Query() + for k, v := range values { + if len(v) > 0 { + extURL.QueryParams = append(extURL.QueryParams, QueryParam{ + Key: k, + Value: v[0], + }) + } + } + + return extURL } diff --git a/command/commands_test.go b/command/commands_test.go index 867ab4e..8c31cf2 100644 --- a/command/commands_test.go +++ b/command/commands_test.go @@ -3,30 +3,62 @@ package command import ( "testing" + "github.com/stretchr/testify/assert" "github.com/thegeeklab/url-parser/config" ) -type TestParseData struct { - config *config.Config - expected string -} - -func TestParseURL(t *testing.T) { +func TestParse(t *testing.T) { //nolint:goconst urlString := "postgres://user:pass@host.com:5432/path/to?key=value&other=other%20value#some-fragment" - tables := []TestParseData{ + tests := []struct { + name string + config *config.Config + expected *URL + }{ { - config: &config.Config{URL: urlString}, - expected: urlString, + name: "parse url", + config: &config.Config{ + URL: urlString, + QuerySplit: true, + }, + expected: &URL{ + Scheme: "postgres", + Username: "user", + Password: "pass", + Hostname: "host.com", + Port: "5432", + Path: "/path/to", + Query: "key=value&other=other%20value", + RawQuery: "key=value&other=other%20value", + QueryParams: []QueryParam{ + { + Key: "key", + Value: "value", + }, + { + Key: "other", + Value: "other value", + }, + }, + Fragment: "some-fragment", + }, }, } - for _, table := range tables { - result := parseURL(urlString) - - if result.String() != table.expected { - t.Fatalf("URL `%v`, should be `%v`", result, table.expected) - } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := NewURLParser(urlString, "", false).parse() + assert.Equal(t, tt.expected.Scheme, result.Scheme) + assert.Equal(t, tt.expected.Username, result.Username) + assert.Equal(t, tt.expected.Password, result.Password) + assert.Equal(t, tt.expected.Hostname, result.Hostname) + assert.Equal(t, tt.expected.Port, result.Port) + assert.Equal(t, tt.expected.Path, result.Path) + assert.Equal(t, tt.expected.Fragment, result.Fragment) + assert.Equal(t, tt.expected.RawQuery, result.RawQuery) + assert.Equal(t, tt.expected.Query, result.Query) + assert.ElementsMatch(t, tt.expected.QueryParams, result.QueryParams) + }) } } diff --git a/command/fragment.go b/command/fragment.go index e8af47f..3529f31 100644 --- a/command/fragment.go +++ b/command/fragment.go @@ -10,7 +10,7 @@ import ( // Fragment prints out the fragment part from the url. func Fragment(cfg *config.Config) cli.ActionFunc { return func(_ *cli.Context) error { - parts := parseURL(cfg.URL) + parts := NewURLParser(cfg.URL, cfg.QueryField, cfg.QuerySplit).parse() if len(parts.Scheme) > 0 { fmt.Println(parts.Fragment) diff --git a/command/fragment_test.go b/command/fragment_test.go index 3a5a8a3..5c97b0f 100644 --- a/command/fragment_test.go +++ b/command/fragment_test.go @@ -4,34 +4,34 @@ import ( "strings" "testing" + "github.com/stretchr/testify/assert" "github.com/thegeeklab/url-parser/config" "github.com/urfave/cli/v2" "github.com/zenizh/go-capturer" ) -type TestFragmentData struct { - config *config.Config - expected string -} - func TestFragment(t *testing.T) { urlString := "postgres://user:pass@host.com:5432/path/to?key=value&other=other%20value#some-fragment" - tables := []TestFragmentData{ + tests := []struct { + name string + config *config.Config + expected string + }{ { + name: "get fragment", config: &config.Config{URL: urlString}, expected: "some-fragment", }, } - for _, table := range tables { + for _, tt := range tests { app := cli.NewApp() ctx := cli.NewContext(app, nil, nil) - result := strings.TrimSpace(capturer.CaptureStdout(func() { _ = Fragment(table.config)(ctx) })) - - if result != table.expected { - t.Fatalf("URL fragment `%v`, should be `%v`", result, table.expected) - } + t.Run(tt.name, func(t *testing.T) { + result := strings.TrimSpace(capturer.CaptureStdout(func() { _ = Fragment(tt.config)(ctx) })) + assert.Equal(t, tt.expected, result) + }) } } diff --git a/command/host.go b/command/host.go index 682c889..8e73458 100644 --- a/command/host.go +++ b/command/host.go @@ -10,10 +10,10 @@ import ( // Host prints out the host part from the url. func Host(cfg *config.Config) cli.ActionFunc { return func(_ *cli.Context) error { - parts := parseURL(cfg.URL) + parts := NewURLParser(cfg.URL, cfg.QueryField, cfg.QuerySplit).parse() if len(parts.Scheme) > 0 { - fmt.Println(parts.Hostname()) + fmt.Println(parts.Hostname) } return nil diff --git a/command/host_test.go b/command/host_test.go index 5f8b950..e0c7a5d 100644 --- a/command/host_test.go +++ b/command/host_test.go @@ -4,34 +4,34 @@ import ( "strings" "testing" + "github.com/stretchr/testify/assert" "github.com/thegeeklab/url-parser/config" "github.com/urfave/cli/v2" "github.com/zenizh/go-capturer" ) -type TestHostnameData struct { - config *config.Config - expected string -} - func TestHost(t *testing.T) { urlString := "postgres://user:pass@host.com:5432/path/to?key=value&other=other%20value#some-fragment" - tables := []TestHostnameData{ + tests := []struct { + name string + config *config.Config + expected string + }{ { + name: "get host", config: &config.Config{URL: urlString}, expected: "host.com", }, } - for _, table := range tables { + for _, tt := range tests { app := cli.NewApp() ctx := cli.NewContext(app, nil, nil) - result := strings.TrimSpace(capturer.CaptureStdout(func() { _ = Host(table.config)(ctx) })) - - if result != table.expected { - t.Fatalf("URL host `%v`, should be `%v`", result, table.expected) - } + t.Run(tt.name, func(t *testing.T) { + result := strings.TrimSpace(capturer.CaptureStdout(func() { _ = Host(tt.config)(ctx) })) + assert.Equal(t, tt.expected, result) + }) } } diff --git a/command/password.go b/command/password.go index f75055e..61ac6df 100644 --- a/command/password.go +++ b/command/password.go @@ -10,13 +10,10 @@ import ( // Password prints out the password part from url. func Password(cfg *config.Config) cli.ActionFunc { return func(_ *cli.Context) error { - parts := parseURL(cfg.URL) + parts := NewURLParser(cfg.URL, cfg.QueryField, cfg.QuerySplit).parse() - if parts.User != nil { - pw, _ := parts.User.Password() - if len(pw) > 0 { - fmt.Println(pw) - } + if parts.Password != "" { + fmt.Println(parts.Password) } return nil diff --git a/command/password_test.go b/command/password_test.go index 3a58b23..6ed611e 100644 --- a/command/password_test.go +++ b/command/password_test.go @@ -4,34 +4,34 @@ import ( "strings" "testing" + "github.com/stretchr/testify/assert" "github.com/thegeeklab/url-parser/config" "github.com/urfave/cli/v2" "github.com/zenizh/go-capturer" ) -type TestPasswordData struct { - config *config.Config - expected string -} - func TestPassword(t *testing.T) { urlString := "postgres://user:pass@host.com:5432/path/to?key=value&other=other%20value#some-fragment" - tables := []TestPasswordData{ + tests := []struct { + name string + config *config.Config + expected string + }{ { + name: "get password", config: &config.Config{URL: urlString}, expected: "pass", }, } - for _, table := range tables { + for _, tt := range tests { app := cli.NewApp() ctx := cli.NewContext(app, nil, nil) - result := strings.TrimSpace(capturer.CaptureStdout(func() { _ = Password(table.config)(ctx) })) - - if result != table.expected { - t.Fatalf("URL password `%v`, should be `%v`", result, table.expected) - } + t.Run(tt.name, func(t *testing.T) { + result := strings.TrimSpace(capturer.CaptureStdout(func() { _ = Password(tt.config)(ctx) })) + assert.Equal(t, tt.expected, result) + }) } } diff --git a/command/path.go b/command/path.go index 5a98196..3dc60ee 100644 --- a/command/path.go +++ b/command/path.go @@ -24,7 +24,7 @@ func PathFlags(cfg *config.Config) []cli.Flag { // Path prints out the path part from url. func Path(cfg *config.Config) cli.ActionFunc { return func(_ *cli.Context) error { - parts := parseURL(cfg.URL) + parts := NewURLParser(cfg.URL, cfg.QueryField, cfg.QuerySplit).parse() i := cfg.PathIndex if len(parts.Path) > 0 { diff --git a/command/path_test.go b/command/path_test.go index 3ad0a1e..53433cc 100644 --- a/command/path_test.go +++ b/command/path_test.go @@ -4,38 +4,39 @@ import ( "strings" "testing" + "github.com/stretchr/testify/assert" "github.com/thegeeklab/url-parser/config" "github.com/urfave/cli/v2" "github.com/zenizh/go-capturer" ) -type TestPathData struct { - config *config.Config - expected string -} - func TestPath(t *testing.T) { urlString := "postgres://user:pass@host.com:5432/path/to?key=value&other=other%20value#some-fragment" - tables := []TestPathData{ + tests := []struct { + name string + config *config.Config + expected string + }{ { + name: "get path", config: &config.Config{URL: urlString, PathIndex: -1}, expected: "/path/to", }, { + name: "get path at index", config: &config.Config{URL: urlString, PathIndex: 0}, expected: "path", }, } - for _, table := range tables { + for _, tt := range tests { app := cli.NewApp() ctx := cli.NewContext(app, nil, nil) - result := strings.TrimSpace(capturer.CaptureStdout(func() { _ = Path(table.config)(ctx) })) - - if result != table.expected { - t.Fatalf("URL path `%v`, should be `%v`", result, table.expected) - } + t.Run(tt.name, func(t *testing.T) { + result := strings.TrimSpace(capturer.CaptureStdout(func() { _ = Path(tt.config)(ctx) })) + assert.Equal(t, tt.expected, result) + }) } } diff --git a/command/port.go b/command/port.go index b4d728c..b501655 100644 --- a/command/port.go +++ b/command/port.go @@ -10,10 +10,10 @@ import ( // Port prints out the port from the url. func Port(cfg *config.Config) cli.ActionFunc { return func(_ *cli.Context) error { - parts := parseURL(cfg.URL) + parts := NewURLParser(cfg.URL, cfg.QueryField, cfg.QuerySplit).parse() if len(parts.Scheme) > 0 { - fmt.Println(parts.Port()) + fmt.Println(parts.Port) } return nil diff --git a/command/port_test.go b/command/port_test.go index 130b45b..8e1b28b 100644 --- a/command/port_test.go +++ b/command/port_test.go @@ -4,34 +4,34 @@ import ( "strings" "testing" + "github.com/stretchr/testify/assert" "github.com/thegeeklab/url-parser/config" "github.com/urfave/cli/v2" "github.com/zenizh/go-capturer" ) -type TestPortData struct { - config *config.Config - expected string -} - func TestPort(t *testing.T) { urlString := "postgres://user:pass@host.com:5432/path/to?key=value&other=other%20value#some-fragment" - tables := []TestPortData{ + tests := []struct { + name string + config *config.Config + expected string + }{ { + name: "get port", config: &config.Config{URL: urlString}, expected: "5432", }, } - for _, table := range tables { + for _, tt := range tests { app := cli.NewApp() ctx := cli.NewContext(app, nil, nil) - result := strings.TrimSpace(capturer.CaptureStdout(func() { _ = Port(table.config)(ctx) })) - - if result != table.expected { - t.Fatalf("URL port `%v`, should be `%v`", result, table.expected) - } + t.Run(tt.name, func(t *testing.T) { + result := strings.TrimSpace(capturer.CaptureStdout(func() { _ = Port(tt.config)(ctx) })) + assert.Equal(t, tt.expected, result) + }) } } diff --git a/command/query.go b/command/query.go index 5d2b5bb..f7d5747 100644 --- a/command/query.go +++ b/command/query.go @@ -22,17 +22,10 @@ func QueryFlags(cfg *config.Config) []cli.Flag { // Query prints out the query part from url. func Query(cfg *config.Config) cli.ActionFunc { return func(_ *cli.Context) error { - parts := parseURL(cfg.URL) - f := cfg.QueryField + parts := NewURLParser(cfg.URL, cfg.QueryField, cfg.QuerySplit).parse() - if len(parts.RawQuery) > 0 { - if f != "" { - if result := parts.Query().Get(f); result != "" { - fmt.Println(result) - } - } else { - fmt.Println(parts.RawQuery) - } + if parts.Query != "" { + fmt.Println(parts.Query) } return nil diff --git a/command/query_test.go b/command/query_test.go index 2c72320..0937d5b 100644 --- a/command/query_test.go +++ b/command/query_test.go @@ -4,40 +4,40 @@ import ( "strings" "testing" + "github.com/stretchr/testify/assert" "github.com/thegeeklab/url-parser/config" "github.com/urfave/cli/v2" "github.com/zenizh/go-capturer" ) -type TestQueryData struct { - config *config.Config - QueryField string - expected string -} - func TestQuery(t *testing.T) { urlString := "postgres://user:pass@host.com:5432/path/to?key=value&other=other%20value#some-fragment" - tables := []TestQueryData{ + tests := []struct { + name string + config *config.Config + QueryField string + expected string + }{ { + name: "get query", config: &config.Config{URL: urlString}, expected: "key=value&other=other%20value", }, { - config: &config.Config{URL: urlString, QueryField: "other"}, - + name: "get query field", + config: &config.Config{URL: urlString, QueryField: "other"}, expected: "other value", }, } - for _, table := range tables { + for _, tt := range tests { app := cli.NewApp() ctx := cli.NewContext(app, nil, nil) - result := strings.TrimSpace(capturer.CaptureStdout(func() { _ = Query(table.config)(ctx) })) - - if result != table.expected { - t.Fatalf("URL query `%v`, should be `%v`", result, table.expected) - } + t.Run(tt.name, func(t *testing.T) { + result := strings.TrimSpace(capturer.CaptureStdout(func() { _ = Query(tt.config)(ctx) })) + assert.Equal(t, tt.expected, result) + }) } } diff --git a/command/run.go b/command/run.go index 691a083..aebbb5e 100644 --- a/command/run.go +++ b/command/run.go @@ -1,6 +1,7 @@ package command import ( + "encoding/json" "fmt" "github.com/thegeeklab/url-parser/config" @@ -10,12 +11,29 @@ import ( // Run default command and print out full url. func Run(cfg *config.Config) cli.ActionFunc { return func(_ *cli.Context) error { - parts := parseURL(cfg.URL) + parts := NewURLParser(cfg.URL, cfg.QueryField, cfg.QuerySplit).parse() if len(parts.String()) > 0 { - fmt.Println(parts) + if cfg.JSONOutput { + json, _ := json.Marshal(parts) + fmt.Println(string(json)) + } else { + fmt.Println(parts) + } } return nil } } + +// AllFlags defines flags for all subcommand. +func AllFlags(cfg *config.Config) []cli.Flag { + return []cli.Flag{ + &cli.BoolFlag{ + Name: "json", + Usage: "output json", + EnvVars: []string{"URL_PARSER_JSON"}, + Destination: &cfg.JSONOutput, + }, + } +} diff --git a/command/run_test.go b/command/run_test.go index 300f391..91da3e8 100644 --- a/command/run_test.go +++ b/command/run_test.go @@ -1,37 +1,88 @@ package command import ( + "encoding/json" "strings" "testing" + "github.com/stretchr/testify/assert" "github.com/thegeeklab/url-parser/config" "github.com/urfave/cli/v2" "github.com/zenizh/go-capturer" ) -type TestRunData struct { - config *config.Config - expected string -} - func TestRun(t *testing.T) { urlString := "postgres://user:pass@host.com:5432/path/to?key=value&other=other%20value#some-fragment" - tables := []TestRunData{ + tests := []struct { + name string + config *config.Config + expected string + }{ { + name: "get url", config: &config.Config{URL: urlString}, expected: urlString, }, + { + name: "get url with query split", + config: &config.Config{ + URL: urlString, + QuerySplit: true, + JSONOutput: true, + }, + expected: `{ + "scheme": "postgres", + "hostname": "host.com", + "port": "5432", + "path": "/path/to", + "fragment": "some-fragment", + "rawQuery": "key=value&other=other%20value", + "queryParams": [ + { + "key": "key", + "value": "value" + }, + { + "key": "other", + "value": "other value" + } + ], + "username": "user", + "password": "pass" + }`, + }, } - for _, table := range tables { + for _, tt := range tests { app := cli.NewApp() ctx := cli.NewContext(app, nil, nil) - result := strings.TrimSpace(capturer.CaptureStdout(func() { _ = Run(table.config)(ctx) })) + t.Run(tt.name, func(t *testing.T) { + result := strings.TrimSpace(capturer.CaptureStdout(func() { _ = Run(tt.config)(ctx) })) - if result != table.expected { - t.Fatalf("URL `%v`, should be `%v`", result, table.expected) - } + if tt.config.JSONOutput { + got := &URL{} + expected := &URL{} + + _ = json.Unmarshal([]byte(result), &got) + _ = json.Unmarshal([]byte(tt.expected), &expected) + + assert.Equal(t, expected.Scheme, got.Scheme) + assert.Equal(t, expected.Username, got.Username) + assert.Equal(t, expected.Password, got.Password) + assert.Equal(t, expected.Hostname, got.Hostname) + assert.Equal(t, expected.Port, got.Port) + assert.Equal(t, expected.Path, got.Path) + assert.Equal(t, expected.Fragment, got.Fragment) + assert.Equal(t, expected.RawQuery, got.RawQuery) + assert.Equal(t, expected.Query, got.Query) + assert.ElementsMatch(t, expected.QueryParams, got.QueryParams) + + return + } + + assert.Equal(t, tt.expected, result) + }) } } diff --git a/command/scheme.go b/command/scheme.go index 3969761..93465d4 100644 --- a/command/scheme.go +++ b/command/scheme.go @@ -10,7 +10,7 @@ import ( // Scheme prints out the scheme part from the url. func Scheme(cfg *config.Config) cli.ActionFunc { return func(_ *cli.Context) error { - parts := parseURL(cfg.URL) + parts := NewURLParser(cfg.URL, cfg.QueryField, cfg.QuerySplit).parse() if len(parts.Scheme) > 0 { fmt.Println(parts.Scheme) diff --git a/command/scheme_test.go b/command/scheme_test.go index 6c40c02..ffe40a9 100644 --- a/command/scheme_test.go +++ b/command/scheme_test.go @@ -4,34 +4,34 @@ import ( "strings" "testing" + "github.com/stretchr/testify/assert" "github.com/thegeeklab/url-parser/config" "github.com/urfave/cli/v2" "github.com/zenizh/go-capturer" ) -type TestSchemeData struct { - config *config.Config - expected string -} - func TestScheme(t *testing.T) { urlString := "postgres://user:pass@host.com:5432/path/to?key=value&other=other%20value#some-fragment" - tables := []TestSchemeData{ + tests := []struct { + name string + config *config.Config + expected string + }{ { + name: "get scheme", config: &config.Config{URL: urlString}, expected: "postgres", }, } - for _, table := range tables { + for _, tt := range tests { app := cli.NewApp() ctx := cli.NewContext(app, nil, nil) - result := strings.TrimSpace(capturer.CaptureStdout(func() { _ = Scheme(table.config)(ctx) })) - - if result != table.expected { - t.Fatalf("URL scheme `%v`, should be `%v`", result, table.expected) - } + t.Run(tt.name, func(t *testing.T) { + result := strings.TrimSpace(capturer.CaptureStdout(func() { _ = Scheme(tt.config)(ctx) })) + assert.Equal(t, tt.expected, result) + }) } } diff --git a/command/user.go b/command/user.go index 5bbbf8b..866dedb 100644 --- a/command/user.go +++ b/command/user.go @@ -10,12 +10,10 @@ import ( // User prints out the user part from url. func User(cfg *config.Config) cli.ActionFunc { return func(_ *cli.Context) error { - parts := parseURL(cfg.URL) + parts := NewURLParser(cfg.URL, cfg.QueryField, cfg.QuerySplit).parse() - if parts.User != nil { - if len(parts.User.Username()) > 0 { - fmt.Println(parts.User.Username()) - } + if parts.Username != "" { + fmt.Println(parts.Username) } return nil diff --git a/command/user_test.go b/command/user_test.go index af2104e..5f6f02c 100644 --- a/command/user_test.go +++ b/command/user_test.go @@ -4,34 +4,34 @@ import ( "strings" "testing" + "github.com/stretchr/testify/assert" "github.com/thegeeklab/url-parser/config" "github.com/urfave/cli/v2" "github.com/zenizh/go-capturer" ) -type TestUserData struct { - config *config.Config - expected string -} - func TestUser(t *testing.T) { urlString := "postgres://user:pass@host.com:5432/path/to?key=value&other=other%20value#some-fragment" - tables := []TestUserData{ + tests := []struct { + name string + config *config.Config + expected string + }{ { + name: "get user", config: &config.Config{URL: urlString}, expected: "user", }, } - for _, table := range tables { + for _, tt := range tests { app := cli.NewApp() ctx := cli.NewContext(app, nil, nil) - result := strings.TrimSpace(capturer.CaptureStdout(func() { _ = User(table.config)(ctx) })) - - if result != table.expected { - t.Fatalf("URL user `%v`, should be `%v`", result, table.expected) - } + t.Run(tt.name, func(t *testing.T) { + result := strings.TrimSpace(capturer.CaptureStdout(func() { _ = User(tt.config)(ctx) })) + assert.Equal(t, tt.expected, result) + }) } } diff --git a/config/config.go b/config/config.go index fe23e89..1d7ee85 100644 --- a/config/config.go +++ b/config/config.go @@ -11,5 +11,7 @@ var ( type Config struct { URL string QueryField string + QuerySplit bool PathIndex int + JSONOutput bool } diff --git a/go.mod b/go.mod index aca2171..6b5f977 100644 --- a/go.mod +++ b/go.mod @@ -4,15 +4,19 @@ go 1.23.2 require ( github.com/rs/zerolog v1.33.0 + github.com/stretchr/testify v1.9.0 github.com/urfave/cli/v2 v2.27.5 github.com/zenizh/go-capturer v0.0.0-20211219060012-52ea6c8fed04 ) require ( github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect golang.org/x/sys v0.12.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 3a6e9e3..f186207 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,8 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= @@ -8,11 +10,15 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= @@ -23,3 +29,7 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=