From b7101b40365f7738734fdc0c4dc55f0ae229a864 Mon Sep 17 00:00:00 2001 From: Frederik Martini Date: Wed, 2 Apr 2025 13:28:16 +0200 Subject: [PATCH 1/4] setup test cases --- internal/cidr/cidr_block_test.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/internal/cidr/cidr_block_test.go b/internal/cidr/cidr_block_test.go index b34cfc6..ea2eeba 100644 --- a/internal/cidr/cidr_block_test.go +++ b/internal/cidr/cidr_block_test.go @@ -79,7 +79,9 @@ func Test_NetworkPortionBinary(t *testing.T) { input *cidr.CIDRBlock expected string }{ - "/0": {input: cidr.NewBlock("10.0.0.0/0"), expected: "00001010.00000000.00000000.00000000"}, + "private range 1": {input: cidr.NewBlock("10.0.0.0/8"), expected: "00001010.00000000.00000000.00000000"}, + "private range 2": {input: cidr.NewBlock("172.16.0.0/12"), expected: "10101100.00010000.00000000.00000000"}, + "private range 3": {input: cidr.NewBlock("192.168.0.0/16"), expected: "11000000.10101000.00000000.00000000"}, } for name, test := range tests { @@ -206,7 +208,9 @@ func Test_StartAddressOfNextBlock(t *testing.T) { input *cidr.CIDRBlock expected string }{ - "/17": {input: cidr.NewBlock("10.0.0.0/17"), expected: "10.0.128.0"}, + "/17": {input: cidr.NewBlock("10.0.0.0/17"), expected: "10.0.128.0"}, + "private range 2": {input: cidr.NewBlock("172.16.0.0/12"), expected: "10.0.0.0"}, + "private range 3": {input: cidr.NewBlock("192.168.0.0/16"), expected: "10.0.0.0"}, } for name, test := range tests { From 258b830c50704f932ffbfce685b01e9908c9c17b Mon Sep 17 00:00:00 2001 From: fremartini Date: Wed, 2 Apr 2025 19:58:02 +0200 Subject: [PATCH 2/4] Add README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f3c22f9..4f7a838 100644 --- a/README.md +++ b/README.md @@ -1 +1,3 @@ # cider + +`cider` is a CLI tool for assisting with common IP-related tasks. \ No newline at end of file From 2eed93cfd90a60c6cd60bf4dc080ca4fc734ff93 Mon Sep 17 00:00:00 2001 From: fremartini Date: Wed, 2 Apr 2025 20:13:38 +0200 Subject: [PATCH 3/4] refactor commands --- internal/commands/in/command.go | 1 + internal/commands/info/command.go | 18 +++++++++--------- internal/commands/ranges/command.go | 24 ++++++++++++------------ internal/list/list.go | 7 ++++++- 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/internal/commands/in/command.go b/internal/commands/in/command.go index 357d9d5..18fbbdc 100644 --- a/internal/commands/in/command.go +++ b/internal/commands/in/command.go @@ -18,6 +18,7 @@ func (h *handler) Handle(args []string) error { } ip := args[0] + ranges := list.Map(args[1:], func(i string) *cidr.CIDRBlock { return cidr.NewBlock(i) }) diff --git a/internal/commands/info/command.go b/internal/commands/info/command.go index c34f016..36eec77 100644 --- a/internal/commands/info/command.go +++ b/internal/commands/info/command.go @@ -15,7 +15,7 @@ func New() *handler { } type pair struct { - a, b string + item1, item2 string } func (h *handler) Handle(args []string) error { @@ -28,12 +28,12 @@ func (h *handler) Handle(args []string) error { block := cidr.NewBlock(ip) entries := []pair{ - {a: "Address range", b: fmt.Sprintf("%s - %s", block.NetworkAddress(), block.BroadcastAddress())}, - {a: "Start of next block", b: block.StartAddressOfNextBlock()}, - {a: "Cidr", b: fmt.Sprintf("/%v", block.HostPortion)}, - {a: "Subnet mask", b: block.SubnetMask()}, - {a: "Addresses", b: fmt.Sprintf("%v", block.AvailableHosts())}, - {a: "Azure addresses", b: block.AvailableAzureHosts()}, + {item1: "Address range", item2: fmt.Sprintf("%s - %s", block.NetworkAddress(), block.BroadcastAddress())}, + {item1: "Start of next block", item2: block.StartAddressOfNextBlock()}, + {item1: "Cidr", item2: fmt.Sprintf("/%v", block.HostPortion)}, + {item1: "Subnet mask", item2: block.SubnetMask()}, + {item1: "Addresses", item2: fmt.Sprintf("%v", block.AvailableHosts())}, + {item1: "Azure addresses", item2: block.AvailableAzureHosts()}, } printOutput(entries) @@ -44,7 +44,7 @@ func (h *handler) Handle(args []string) error { func printOutput(entries []pair) { keys := []string{} for _, pair := range entries { - keys = append(keys, pair.a) + keys = append(keys, pair.item1) } longestTitle := list.Fold(keys, 0, func(title string, acc int) int { @@ -52,6 +52,6 @@ func printOutput(entries []pair) { }) for _, pair := range entries { - fmt.Printf("%s : %v\n", utils.PadRight(pair.a, ' ', longestTitle), pair.b) + fmt.Printf("%s : %v\n", utils.PadRight(pair.item1, ' ', longestTitle), pair.item2) } } diff --git a/internal/commands/ranges/command.go b/internal/commands/ranges/command.go index 45637b0..3aed70c 100644 --- a/internal/commands/ranges/command.go +++ b/internal/commands/ranges/command.go @@ -21,12 +21,12 @@ func New() *handler { func (*handler) Handle(arg string) error { // no args if arg == "" { - table := calculateAllCIDRBlocks() + table := calculateAllCidrBlocks() - return printCIDRBlocks(table) + return printCidrBlocks(table) } - // argument was given - try to parse it + // argument was given. Try to parse it hostPortion, err := strconv.ParseInt(arg, 10, INT_SIZE) if err != nil { @@ -34,20 +34,20 @@ func (*handler) Handle(arg string) error { } if hostPortion < 0 || hostPortion > INT_SIZE { - return fmt.Errorf("%v is not a valid size - must be between 0 and 32", hostPortion) + return fmt.Errorf("%v is not a valid size - must be between 0 and %d", hostPortion, INT_SIZE) } - block := calculateCIDRBlock(int(hostPortion)) + block := defaultCidrBlockFromHostPortion(int(hostPortion)) - table := []*cidr.CIDRBlock{block} + blocks := []*cidr.CIDRBlock{block} - return printCIDRBlocks(table) + return printCidrBlocks(blocks) } -func calculateAllCIDRBlocks() []*cidr.CIDRBlock { +func calculateAllCidrBlocks() []*cidr.CIDRBlock { blocks := []*cidr.CIDRBlock{} - for i := 0; i < INT_SIZE+1; i++ { - block := calculateCIDRBlock(i) + for i := range INT_SIZE + 1 { + block := defaultCidrBlockFromHostPortion(i) blocks = append(blocks, block) } @@ -55,11 +55,11 @@ func calculateAllCIDRBlocks() []*cidr.CIDRBlock { return blocks } -func calculateCIDRBlock(hostPortion int) *cidr.CIDRBlock { +func defaultCidrBlockFromHostPortion(hostPortion int) *cidr.CIDRBlock { return cidr.NewBlock(fmt.Sprintf("10.0.0.0/%v", hostPortion)) } -func printCIDRBlocks(blocks []*cidr.CIDRBlock) error { +func printCidrBlocks(blocks []*cidr.CIDRBlock) error { w := tabwriter.NewWriter(os.Stdout, 2, 4, 1, ' ', 0) fmt.Fprint(w, "Cidr\tSubnet mask\tAddresses\tAzure addresses\n") diff --git a/internal/list/list.go b/internal/list/list.go index fe7bd74..f32f893 100644 --- a/internal/list/list.go +++ b/internal/list/list.go @@ -30,7 +30,12 @@ func FlatMap[T, K any](v []T, f func(T) []K) []K { } func Contains[T any](v []T, f func(T) bool) bool { - return len(Filter(v, f)) > 0 + for _, i := range v { + if f(i) { + return true + } + } + return false } func All[T any](v []T, f func(T) bool) bool { From 51a01231095bf21789475197458fabcac3e558b2 Mon Sep 17 00:00:00 2001 From: Frederik Martini Date: Sun, 13 Apr 2025 10:48:16 +0200 Subject: [PATCH 4/4] add tests; change datatype --- internal/cidr/cidr_block.go | 28 ++++++++++++++-------------- internal/cidr/cidr_block_test.go | 15 +++++++++------ internal/commands/info/command.go | 3 +-- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/internal/cidr/cidr_block.go b/internal/cidr/cidr_block.go index 32d13f3..67c4fd9 100644 --- a/internal/cidr/cidr_block.go +++ b/internal/cidr/cidr_block.go @@ -66,10 +66,10 @@ func ipToDecimal(ip string) int { parts := strings.Split(ip, ".") base := 10 - octet1 := must(strconv.ParseInt(parts[0], base, INT_SIZE)) - octet2 := must(strconv.ParseInt(parts[1], base, INT_SIZE)) - octet3 := must(strconv.ParseInt(parts[2], base, INT_SIZE)) - octet4 := must(strconv.ParseInt(parts[3], base, INT_SIZE)) + octet1 := must(strconv.ParseUint(parts[0], base, INT_SIZE)) + octet2 := must(strconv.ParseUint(parts[1], base, INT_SIZE)) + octet3 := must(strconv.ParseUint(parts[2], base, INT_SIZE)) + octet4 := must(strconv.ParseUint(parts[3], base, INT_SIZE)) return int((octet1 * 16777216) + (octet2 * 65536) + (octet3 * 256) + octet4) } @@ -82,8 +82,8 @@ func (b *CIDRBlock) NetworkPortionBinary() string { } func toBin(s string) string { - asInt := must(strconv.ParseInt(s, 10, INT_SIZE)) - asBinaryString := strconv.FormatInt(asInt, 2) + asInt := must(strconv.ParseUint(s, 10, INT_SIZE)) + asBinaryString := strconv.FormatUint(asInt, 2) paddedBynaryString := utils.PadLeft(asBinaryString, '0', 8) return paddedBynaryString } @@ -120,9 +120,9 @@ func (b *CIDRBlock) StartAddressOfNextBlock() string { octets = list.Map(octets, toBin) binStr := strings.Join(octets, "") - next := must(strconv.ParseInt(binStr, 2, INT_SIZE)) + 1 + next := must(strconv.ParseUint(binStr, 2, INT_SIZE)) + 1 - asBinaryString := strconv.FormatInt(next, 2) + asBinaryString := strconv.FormatUint(next, 2) asBinaryString = utils.PadLeft(asBinaryString, '0', 32) octetsInt := stringToOctets(asBinaryString) @@ -151,14 +151,14 @@ func (b *CIDRBlock) BroadcastAddress() string { return fmt.Sprintf("%v.%v.%v.%v", octets[0], octets[1], octets[2], octets[3]) } -func stringToOctets(ipString string) []int64 { - octets := make([]int64, 4) +func stringToOctets(ipString string) []uint64 { + octets := make([]uint64, 4) base := 2 - octets[0] = must(strconv.ParseInt(ipString[0:8], base, INT_SIZE)) - octets[1] = must(strconv.ParseInt(ipString[8:16], base, INT_SIZE)) - octets[2] = must(strconv.ParseInt(ipString[16:24], base, INT_SIZE)) - octets[3] = must(strconv.ParseInt(ipString[24:32], base, INT_SIZE)) + octets[0] = must(strconv.ParseUint(ipString[0:8], base, INT_SIZE)) + octets[1] = must(strconv.ParseUint(ipString[8:16], base, INT_SIZE)) + octets[2] = must(strconv.ParseUint(ipString[16:24], base, INT_SIZE)) + octets[3] = must(strconv.ParseUint(ipString[24:32], base, INT_SIZE)) return octets } diff --git a/internal/cidr/cidr_block_test.go b/internal/cidr/cidr_block_test.go index ea2eeba..f3ab114 100644 --- a/internal/cidr/cidr_block_test.go +++ b/internal/cidr/cidr_block_test.go @@ -79,9 +79,9 @@ func Test_NetworkPortionBinary(t *testing.T) { input *cidr.CIDRBlock expected string }{ - "private range 1": {input: cidr.NewBlock("10.0.0.0/8"), expected: "00001010.00000000.00000000.00000000"}, - "private range 2": {input: cidr.NewBlock("172.16.0.0/12"), expected: "10101100.00010000.00000000.00000000"}, - "private range 3": {input: cidr.NewBlock("192.168.0.0/16"), expected: "11000000.10101000.00000000.00000000"}, + "10.0.0.0/8": {input: cidr.NewBlock("10.0.0.0/8"), expected: "00001010.00000000.00000000.00000000"}, + "172.16.0.0/12": {input: cidr.NewBlock("172.16.0.0/12"), expected: "10101100.00010000.00000000.00000000"}, + "192.168.0.0/16": {input: cidr.NewBlock("192.168.0.0/16"), expected: "11000000.10101000.00000000.00000000"}, } for name, test := range tests { @@ -208,9 +208,12 @@ func Test_StartAddressOfNextBlock(t *testing.T) { input *cidr.CIDRBlock expected string }{ - "/17": {input: cidr.NewBlock("10.0.0.0/17"), expected: "10.0.128.0"}, - "private range 2": {input: cidr.NewBlock("172.16.0.0/12"), expected: "10.0.0.0"}, - "private range 3": {input: cidr.NewBlock("192.168.0.0/16"), expected: "10.0.0.0"}, + "10.0.0.0/8": {input: cidr.NewBlock("10.0.0.0/8"), expected: "11.0.0.0"}, + "127.0.0.0/8": {input: cidr.NewBlock("127.0.0.0/8"), expected: "128.0.0.0"}, + "169.254.0.0/16": {input: cidr.NewBlock("169.254.0.0/16"), expected: "169.255.0.0"}, + "172.16.0.0/12": {input: cidr.NewBlock("172.16.0.0/12"), expected: "172.32.0.0"}, + "192.0.2.0/24": {input: cidr.NewBlock("192.0.2.0/24"), expected: "192.0.3.0"}, + "192.168.0.0/16": {input: cidr.NewBlock("192.168.0.0/16"), expected: "192.169.0.0"}, } for name, test := range tests { diff --git a/internal/commands/info/command.go b/internal/commands/info/command.go index 36eec77..f05b0fe 100644 --- a/internal/commands/info/command.go +++ b/internal/commands/info/command.go @@ -30,8 +30,7 @@ func (h *handler) Handle(args []string) error { entries := []pair{ {item1: "Address range", item2: fmt.Sprintf("%s - %s", block.NetworkAddress(), block.BroadcastAddress())}, {item1: "Start of next block", item2: block.StartAddressOfNextBlock()}, - {item1: "Cidr", item2: fmt.Sprintf("/%v", block.HostPortion)}, - {item1: "Subnet mask", item2: block.SubnetMask()}, + {item1: "Mask", item2: fmt.Sprintf("%s (%s)", fmt.Sprintf("/%v", block.HostPortion), block.SubnetMask())}, {item1: "Addresses", item2: fmt.Sprintf("%v", block.AvailableHosts())}, {item1: "Azure addresses", item2: block.AvailableAzureHosts()}, }