diff --git a/ipinfo/cmd_tool.go b/ipinfo/cmd_tool.go index f561eafb..255764ae 100644 --- a/ipinfo/cmd_tool.go +++ b/ipinfo/cmd_tool.go @@ -15,6 +15,8 @@ var completionsTool = &complete.Command{ "next": completionsToolNext, "prev": completionsToolPrev, "is_v4": completionsToolIsV4, + "is_v6": completionsToolIsV6, + "is_one_ip": completionsToolIsOneIp, "lower": completionsToolLower, "upper": completionsToolUpper, "ip2n": completionsToolIP2n, @@ -38,6 +40,7 @@ Commands: prev get the previous IP of the input IP is_v4 reports whether input is an IPv4 address. is_v6 reports whether input is an IPv6 address. + is_one_ip checks whether a CIDR or IP Range contains exactly one IP. lower get start IP of IPs, IP ranges, and CIDRs. upper get end IP of IPs, IP ranges, and CIDRs. ip2n converts an IPv4 or IPv6 address to its decimal representation. @@ -81,6 +84,8 @@ func cmdTool() error { err = cmdToolIsV4() case cmd == "is_v6": err = cmdToolIsV6() + case cmd == "is_one_ip": + err = cmdToolIsOneIp() case cmd == "lower": err = cmdToolLower() case cmd == "upper": diff --git a/ipinfo/cmd_tool_is_one_ip.go b/ipinfo/cmd_tool_is_one_ip.go new file mode 100644 index 00000000..793b8366 --- /dev/null +++ b/ipinfo/cmd_tool_is_one_ip.go @@ -0,0 +1,54 @@ +package main + +import ( + "fmt" + + "github.com/ipinfo/cli/lib" + "github.com/ipinfo/cli/lib/complete" + "github.com/ipinfo/cli/lib/complete/predict" + "github.com/spf13/pflag" +) + +var completionsToolIsOneIp = &complete.Command{ + Flags: map[string]complete.Predictor{ + "-h": predict.Nothing, + "--help": predict.Nothing, + }, +} + +func printHelpToolIsOneIp() { + fmt.Printf( + `Usage: %s tool is_one_ip [] + +Description: + Checks whether a CIDR or IP Range contains exactly one IP. + +Examples: + # Check CIDR. + $ %[1]s tool is_one_ip 1.1.1.0/30 + + # Check IP. + $ %[1]s tool is_one_ip 1.1.1.1 + + # Check IP range. + $ %[1]s tool is_one_ip 1.1.1.1-2.2.2.2 + + # Check for file. + $ %[1]s tool is_one_ip /path/to/file.txt + + # Check entries from stdin. + $ cat /path/to/file1.txt | %[1]s tool is_one_ip + +Options: + --help, -h + show help. +`, progBase) +} + +func cmdToolIsOneIp() (err error) { + f := lib.CmdToolIsOneIpFlags{} + f.Init() + pflag.Parse() + + return lib.CmdToolIsOneIp(f, pflag.Args()[2:], printHelpToolIsOneIp) +} diff --git a/lib/cmd_tool_is_one_ip.go b/lib/cmd_tool_is_one_ip.go new file mode 100644 index 00000000..c3031b54 --- /dev/null +++ b/lib/cmd_tool_is_one_ip.go @@ -0,0 +1,59 @@ +package lib + +import ( + "fmt" + "github.com/spf13/pflag" + "net/netip" +) + +// CmdToolIsOneIpFlags are flags expected by CmdToolIP2n +type CmdToolIsOneIpFlags struct { + Help bool +} + +// Init initializes the common flags available to CmdToolIsOneIp with sensible +func (f *CmdToolIsOneIpFlags) Init() { + pflag.BoolVarP( + &f.Help, + "help", "h", false, + "show help.", + ) +} + +func CmdToolIsOneIp(f CmdToolIsOneIpFlags, args []string, printHelp func()) error { + if f.Help { + printHelp() + return nil + } + + op := func(input string, inputType INPUT_TYPE) error { + isOneIp := false + switch inputType { + case INPUT_TYPE_CIDR: + prefix, err := netip.ParsePrefix(input) + if err != nil { + return ErrInvalidInput + } + isOneIp = prefix.IsSingleIP() + case INPUT_TYPE_IP: + isOneIp = true + case INPUT_TYPE_IP_RANGE: + isOneIp = ipRangeContainsExactlyOneIP(input) + default: + return ErrInvalidInput + } + fmt.Printf("%s,%v\n", input, isOneIp) + return nil + } + + return GetInputFrom(args, true, true, op) +} + +func ipRangeContainsExactlyOneIP(ipRangeStr string) bool { + ipRange, err := IPRangeStrFromStr(ipRangeStr) + if err != nil { + return false + } + + return ipRange.Start == ipRange.End +} diff --git a/lib/cmd_tool_next.go b/lib/cmd_tool_next.go index 3de07415..b4e7c511 100644 --- a/lib/cmd_tool_next.go +++ b/lib/cmd_tool_next.go @@ -2,7 +2,6 @@ package lib import ( "fmt" - "net" "github.com/spf13/pflag" ) @@ -48,6 +47,6 @@ func CmdToolNext( if err != nil { fmt.Println(err) } - + return nil }