diff --git a/README.md b/README.md index 10b7fe5..d5866ab 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ COMMANDS: GLOBAL OPTIONS: --url value source url to parse [$URL_PARSER_URL] + --stdin read url to parse from stdin (default: false) [$URL_PARSER_STDIN] --help, -h show help --version, -v print the version ``` diff --git a/cmd/url-parser/main.go b/cmd/url-parser/main.go index c775c79..9123e1e 100644 --- a/cmd/url-parser/main.go +++ b/cmd/url-parser/main.go @@ -2,7 +2,9 @@ package main import ( "fmt" + "io" "os" + "strings" "github.com/sirupsen/logrus" "github.com/thegeeklab/url-parser/command" @@ -21,19 +23,25 @@ func main() { fmt.Printf("%s version=%s date=%s\n", c.App.Name, c.App.Version, BuildDate) } - config := &config.Config{} + cfg := &config.Config{} app := &cli.App{ Name: "url-parser", Usage: "Parse URL and shows the part of it.", Version: BuildVersion, - Action: command.Run(config), + Action: command.Run(cfg), Flags: []cli.Flag{ &cli.StringFlag{ Name: "url", Usage: "source url to parse", EnvVars: []string{"URL_PARSER_URL"}, - Destination: &config.URL, + Destination: &cfg.URL, + }, + &cli.BoolFlag{ + Name: "stdin", + Usage: "read url to parse from stdin", + EnvVars: []string{"URL_PARSER_STDIN"}, + Destination: &cfg.Stdin, }, }, Commands: []*cli.Command{ @@ -41,59 +49,89 @@ func main() { Name: "all", Aliases: []string{"a"}, Usage: "Get all parts from url", - Action: command.Run(config), + Action: command.Run(cfg), }, { Name: "scheme", Aliases: []string{"s"}, Usage: "Get scheme from url", - Action: command.Scheme(config), + Action: command.Scheme(cfg), }, { Name: "user", Aliases: []string{"u"}, Usage: "Get username from url", - Action: command.User(config), + Action: command.User(cfg), }, { Name: "password", Aliases: []string{"pw"}, Usage: "Get password from url", - Action: command.Password(config), + Action: command.Password(cfg), }, { Name: "path", Aliases: []string{"pt"}, Usage: "Get path from url", - Action: command.Path(config), - Flags: command.PathFlags(config), + Action: command.Path(cfg), + Flags: command.PathFlags(cfg), }, { Name: "host", Aliases: []string{"h"}, Usage: "Get hostname from url", - Action: command.Host(config), + Action: command.Host(cfg), }, { Name: "port", Aliases: []string{"p"}, Usage: "Get port from url", - Action: command.Port(config), + Action: command.Port(cfg), }, { Name: "query", Aliases: []string{"q"}, Usage: "Get query from url", - Action: command.Query(config), - Flags: command.QueryFlags(config), + Action: command.Query(cfg), + Flags: command.QueryFlags(cfg), }, { Name: "fragment", Aliases: []string{"f"}, Usage: "Get fragment from url", - Action: command.Fragment(config), + Action: command.Fragment(cfg), }, }, + Before: func(ctx *cli.Context) error { + if cfg.URL == "" && !cfg.Stdin { + _ = cli.ShowAppHelp(ctx) + + return fmt.Errorf("error: %w", config.ErrRequiredFlagsNotSet) + } + + if cfg.URL != "" && cfg.Stdin { + _ = cli.ShowAppHelp(ctx) + + return fmt.Errorf("error: %w", config.ErrExclusiveFlags) + } + + if cfg.Stdin { + stat, _ := os.Stdin.Stat() + if (stat.Mode() & os.ModeCharDevice) == 0 { + stdin, err := io.ReadAll(os.Stdin) + if err != nil { + return fmt.Errorf("error: %w: %w", config.ErrEmptyStdin, err) + } + cfg.URL = strings.TrimSuffix(string(stdin), "\n") + } + + if cfg.URL == "" { + return fmt.Errorf("error: %w", config.ErrEmptyStdin) + } + } + + return nil + }, } if err := app.Run(os.Args); err != nil { diff --git a/command/commands.go b/command/commands.go index 7137296..040acd8 100644 --- a/command/commands.go +++ b/command/commands.go @@ -1,10 +1,12 @@ package command import ( + "fmt" "net/url" "strings" "github.com/sirupsen/logrus" + "github.com/thegeeklab/url-parser/config" ) func parseURL(raw string) *url.URL { @@ -12,7 +14,7 @@ func parseURL(raw string) *url.URL { url, err := url.Parse(urlString) if err != nil { - logrus.Fatal(err) + logrus.Fatal(fmt.Errorf("%w: %w", config.ErrParseURL, err)) } return url diff --git a/command/fragment.go b/command/fragment.go index 49720fb..dcf34ba 100644 --- a/command/fragment.go +++ b/command/fragment.go @@ -8,9 +8,9 @@ import ( ) // Fragment prints out the fragment part from the url. -func Fragment(config *config.Config) cli.ActionFunc { +func Fragment(cfg *config.Config) cli.ActionFunc { return func(ctx *cli.Context) error { - parts := parseURL(config.URL) + parts := parseURL(cfg.URL) if len(parts.Scheme) > 0 { fmt.Println(parts.Fragment) diff --git a/command/host.go b/command/host.go index 9950c4e..03e20f1 100644 --- a/command/host.go +++ b/command/host.go @@ -8,9 +8,9 @@ import ( ) // Host prints out the host part from the url. -func Host(config *config.Config) cli.ActionFunc { +func Host(cfg *config.Config) cli.ActionFunc { return func(ctx *cli.Context) error { - parts := parseURL(config.URL) + parts := parseURL(cfg.URL) if len(parts.Scheme) > 0 { fmt.Println(parts.Hostname()) diff --git a/command/password.go b/command/password.go index 5c03ed3..5e75b3b 100644 --- a/command/password.go +++ b/command/password.go @@ -8,9 +8,9 @@ import ( ) // Password prints out the password part from url. -func Password(config *config.Config) cli.ActionFunc { +func Password(cfg *config.Config) cli.ActionFunc { return func(ctx *cli.Context) error { - parts := parseURL(config.URL) + parts := parseURL(cfg.URL) if parts.User != nil { pw, _ := parts.User.Password() diff --git a/command/path.go b/command/path.go index 2ffa421..499c08d 100644 --- a/command/path.go +++ b/command/path.go @@ -9,23 +9,23 @@ import ( ) // PathFlags defines flags for path subcommand. -func PathFlags(config *config.Config) []cli.Flag { +func PathFlags(cfg *config.Config) []cli.Flag { return []cli.Flag{ &cli.IntFlag{ Name: "path-index", Usage: "filter parsed path by index", EnvVars: []string{"URL_PARSER_PATH_INDEX"}, Value: -1, - Destination: &config.PathIndex, + Destination: &cfg.PathIndex, }, } } // Path prints out the path part from url. -func Path(config *config.Config) cli.ActionFunc { +func Path(cfg *config.Config) cli.ActionFunc { return func(ctx *cli.Context) error { - parts := parseURL(config.URL) - i := config.PathIndex + parts := parseURL(cfg.URL) + i := cfg.PathIndex if len(parts.Path) > 0 { if i > -1 { diff --git a/command/port.go b/command/port.go index 5f76d6a..4bd71e6 100644 --- a/command/port.go +++ b/command/port.go @@ -8,9 +8,9 @@ import ( ) // Port prints out the port from the url. -func Port(config *config.Config) cli.ActionFunc { +func Port(cfg *config.Config) cli.ActionFunc { return func(ctx *cli.Context) error { - parts := parseURL(config.URL) + parts := parseURL(cfg.URL) if len(parts.Scheme) > 0 { fmt.Println(parts.Port()) diff --git a/command/query.go b/command/query.go index ab1db68..aa26766 100644 --- a/command/query.go +++ b/command/query.go @@ -8,22 +8,22 @@ import ( ) // QueryFlags defines flags for query subcommand. -func QueryFlags(config *config.Config) []cli.Flag { +func QueryFlags(cfg *config.Config) []cli.Flag { return []cli.Flag{ &cli.StringFlag{ Name: "query-field", Usage: "filter parsed query string by field name", EnvVars: []string{"URL_PARSER_QUERY_FIELD"}, - Destination: &config.QueryField, + Destination: &cfg.QueryField, }, } } // Query prints out the query part from url. -func Query(config *config.Config) cli.ActionFunc { +func Query(cfg *config.Config) cli.ActionFunc { return func(ctx *cli.Context) error { - parts := parseURL(config.URL) - f := config.QueryField + parts := parseURL(cfg.URL) + f := cfg.QueryField if len(parts.RawQuery) > 0 { if f != "" { diff --git a/command/run.go b/command/run.go index 75d3438..440b937 100644 --- a/command/run.go +++ b/command/run.go @@ -8,9 +8,9 @@ import ( ) // Run default command and print out full url. -func Run(config *config.Config) cli.ActionFunc { +func Run(cfg *config.Config) cli.ActionFunc { return func(ctx *cli.Context) error { - parts := parseURL(config.URL) + parts := parseURL(cfg.URL) if len(parts.String()) > 0 { fmt.Println(parts) diff --git a/command/scheme.go b/command/scheme.go index 7f4451f..f63ad25 100644 --- a/command/scheme.go +++ b/command/scheme.go @@ -8,9 +8,9 @@ import ( ) // Scheme prints out the scheme part from the url. -func Scheme(config *config.Config) cli.ActionFunc { +func Scheme(cfg *config.Config) cli.ActionFunc { return func(ctx *cli.Context) error { - parts := parseURL(config.URL) + parts := parseURL(cfg.URL) if len(parts.Scheme) > 0 { fmt.Println(parts.Scheme) diff --git a/command/user.go b/command/user.go index b564a9a..44022e1 100644 --- a/command/user.go +++ b/command/user.go @@ -8,9 +8,9 @@ import ( ) // User prints out the user part from url. -func User(config *config.Config) cli.ActionFunc { +func User(cfg *config.Config) cli.ActionFunc { return func(ctx *cli.Context) error { - parts := parseURL(config.URL) + parts := parseURL(cfg.URL) if parts.User != nil { if len(parts.User.Username()) > 0 { diff --git a/config/config.go b/config/config.go index e81b56f..7ba2a90 100644 --- a/config/config.go +++ b/config/config.go @@ -1,7 +1,18 @@ package config +import "errors" + +var ( + ErrRequiredFlagsNotSet = errors.New("either \"url\" or \"stdin\" must be set") + ErrExclusiveFlags = errors.New("\"url\" and \"stdin\" are mutually exclusive") + ErrEmptyStdin = errors.New("\"stdin\" must not be empty") + ErrReadStdin = errors.New("failed to read \"stdin\"") + ErrParseURL = errors.New("failed to parse url") +) + type Config struct { URL string + Stdin bool QueryField string PathIndex int }