Building the Application on Top of the Skeleton

In this lesson, we'll build our simple calculator on top of the skeleton. We will add a couple more options like pre-run and post-run hooks.

Building your application on top of the skeleton

We will use the same code from the previous lesson and just bolt it on the new skeleton. If you want to follow along, you can use the terminal at the bottom of the lesson. First, let’s add the calc package to pkg/calc/calc.go:

package calc

const (
	maxUint=^uint(0)
	maxInt = int(maxUint >> 1)
	minInt = -maxInt - 1
)

func checkAdd(a, b int) {
	if a > 0 {
		if b > maxInt - a {
			panic("overflow!")
		}
	} else {
		if b < minInt - a {
			panic("underflow!")
		}
	}
}

func checkSub(a, b int) {
	if a > 0 {
		if b < minInt + a {
			panic("overflow!")
		}
	} else {
		if b < a - maxInt {
			panic("underflow!")
		}
	}
}

func Add(a, b int, check bool) int {
	if check {
		checkAdd(a, b)
	}

	return a + b
}

func Subtract(a, b int, check bool) int {
	if check {
		checkSub(a, b)
	}
	return a - b
}

Then, let’s update the root command:

/*
Copyright © 2020

*/
package cmd

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"
)

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
	Use:   "calc-app",
	Short: "Calculate arithmetic expressions",
	Long: `Calculate arithmetic expressions.
           
           It can add integers and subtract integers`,
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

Last, but not least we need to update the arguments for the add and subtract commands and have them invoke the calc package. Here is the updated add.go file:

package cmd

import (
	"fmt"
	"calc-app/pkg/calc"
	"github.com/spf13/cobra"
	"strconv"
)

var check bool

var addCmd = &cobra.Command{
	Use:   "add",
	Short: "Add two integers",
	Long:  `Add two integers a and b; result = a + b`,
	Args:  cobra.ExactArgs(2),
	Run: func(cmd *cobra.Command, args []string) {
		var a, b int
		var err error
		a, err = strconv.Atoi(args[0])
		if err != nil {
			panic("Arguments to `add` must be integers")
		}
		b, err = strconv.Atoi(args[1])
		if err != nil {
			panic("Arguments to `add` must be integers")
		}

		result := calc.Add(a, b, check)
		fmt.Println(result)
	},
}

func init() {
	addCmd.Flags().BoolVar(
		&check,
		"check",
		false,
		"check controls if overflow/underflow check is performed")
	rootCmd.AddCommand(addCmd)
}

Similarly, we can update the subtract command:

package cmd

import (
	"fmt"
	"calc-app/pkg/calc"
	"github.com/spf13/cobra"
	"strconv"
)

var check bool

var subtractCmd = &cobra.Command{
	Use:   "subtract",
	Short: "Substract one integer from another",
	Long:  `Substract one integer a from another integer b; result = a -b`,
	Args:  cobra.ExactArgs(2),
	Run: func(cmd *cobra.Command, args []string) {
		var a, b int
		var err error
		a, err = strconv.Atoi(args[0])
		if err != nil {
			panic("Arguments to `subtract` must be integers")
		}
		b, err = strconv.Atoi(args[1])
		if err != nil {
			panic("Arguments to `subtract` must be integers")
		}

		result := calc.Subtract(a, b, check)
		fmt.Println(result)
	},
}

func init() {
	subtractCmd.Flags().BoolVar(
		&check,
		"check",
		false,
		"check controls if overflow/underflow check is performed")
	rootCmd.AddCommand(subtractCmd)
}

At this point we can build and test our calculator application:

$ go build .
$ ./calc-app add 6 19
25

$ ./calc-app subtract 18 7
11

Get hands-on with 1400+ tech skills courses.