Building CLI application with GO
Tags: go, beginners
Published: 2021-07-31 06:46:21

---
title: Building CLI application with GO
published: true
date: 
tags: go, beginners
canonical_url: https://eternaldev.com/blog/building-cli-application-with-go
---

## Building CLI application with GO

This article explains how to create a simple application in Go and access the feature using a CLI interface

CLI (Command Line Interface) apps can be helpful for developers who are mostly working on the terminal and don't mind having a simple interface. This is also a great starting point for learning Go as we can build functionality and have a bunch of commands for your users to use.

## Overview

1. Introduction
2. Creating an app in Go
3. Creating business logic (Pomodoro Time management technique)
4. Creating CLI for users to interact
5. Building and Running the CLI
6. Summary

## Introduction

Building a simple go program that will take a `task` being performed by the user and create a timer for 25 minutes. During this time the user is meant to fully focus on the task which he wants to do. After the timer runs out, we will print out that the timer is done. Users can take a break once the timer is complete and start the cycle again until the task is complete

The above-mentioned activity is a time-management technique known as "The Pomodoro Technique". We are going to build a very bare-bones version of the technique which just one task which the user can add and then reminding the user that the timer is complete

[![Pomodoro_WhatAreWeBuilding](https://eternaldev.com/static/4c23aa2936a922bec36482e96935e6c9/f9218/podomoro_whatarewebuilding.jpg "Pomodoro\_WhatAreWeBuilding")](/static/4c23aa2936a922bec36482e96935e6c9/f9218/podomoro_whatarewebuilding.jpg)

Souce Code : [https://github.com/eternaldevgames/pomodoro-go-cli](https://github.com/eternaldevgames/pomodoro-go-cli)

## Creating an app in Go

Create a new directory for your app and initialize Go app using the `go mod init {packagename}` command

```
mkdir pomodoro-go-cli
go mod init pomodoro-go-cli
```

You will have `go.mod` file created in the same folder which will contain the package name and other metadata about our application.

We can start by creating a `main.go` file in the root directory and start by writing a simple print statement to print to the console.

```
package main

import "fmt"

func main() {
	fmt.Println("Pomodoro App !!")
}
```

Run the following command to see the print statement in the terminal.

```
go run main.go

Output: Pomodoro App !!
```

Great! Now that we have an app running in the console, we can start building the business logic of our app in a separate file.

The idea here is to separate your CMD (Command Line Interface) and the business logic into a separate file that will help you in the following ways

- Separation of concern
- Easy to test
- Expanding to other interfaces is easy in the future (HTTP API, UI Application)

## Creating business logic (Pomodoro Time management technique)

Add the function and business logic to create a new task, print the current task. Start by creating a new file `pomodoro.go` in root folder

### Creating a new task struct

We can start by writing a `struct` for our task which can hold more information about the task

> `struct` are a typed collection of fields for grouping data together

```
type Pomodoro struct {
	// Name of the task
	TaskName string

	// Start time of the task created by the user
	StartTime time.Time
}
```

Our simple application just need two fields in the struct

1. TaskName - Storing the name of the task entered by the user
2. StartTime - Time when the task was created. (For simplicity, we will assume the user starts working on the task immediately after it is created)

### Storing the current task

Multiple options are available to store the current task to some persistent storage and we are not going to take any of those :)

Keeping it simple for this article, we will just store the current task as an in-memory variable and so task value will be lost once we close the program.

> Extending this to storing the value in a database can be done as a _bonus_ exercise to learn further.

Create a variable at the top of the file after the struct declaration

```
var currentTask Pomodoro = Pomodoro{}
```

`currentTask` - Varaible name for storing the currentTask

We are assigning an `empty struct` to the variable to differentiate between no task and having a current task

### Adding the new task

`AddTask` function takes in taskName as input parameter of type `string` and returns a new Pomodoro struct. `StartTime` variable can be inferred without being passed since the value we are going to store is the current time when the AddTask function is called.

We can get the current time in Go using `time.Now()` function and import the `time` package

```
import (
	"fmt"
	"time"
)

func AddTask(taskName string) Pomodoro {

	return Pomodoro{
		TaskName: taskName,
		StartTime: time.Now(),
	}
}
```

We can use the struct returned to store it in the variable as `currentTask`

### Print the task to console

Implement a PrintTask function to print to the console.

```
func PrintTask(task Pomodoro) string {
	// Check for empty struct
	if (Pomodoro{} == task) {
		return "Current task is empty"
	} else {
		return "Task - " + task.TaskName
	}
}
```

Update your main function to call the new code to add and print the task

```
func main() {

	currentTask = AddTask("Write a blog")
	fmt.Println(PrintTask(currentTask))

}
```

### Testing your business logic

Testing your code plays a major role when you are adding more features and want to make sure that you are not affecting the existing code. It also helps in writing better code as we will refactor our code during tests

We can start by creating a new file `pomodoro_test.go` in the root folder. `testing` package from the standard library can be used to start with our basic test.

We are going to test the following

1. AddTask function - Simple test to check if the task name specified is the same as the task name created
2. PrintTask function - Test to check if it returns the current task
3. PrintTask function - Test to check if it returns the proper test for empty task

```
package main

import "testing"

func TestAddTask(t *testing.T) {
	expectedTaskName := "Testing task"
	newTask := AddTask(expectedTaskName)

	if newTask.TaskName != expectedTaskName {
		t.Errorf("TestAddTask Failed - Expected: %s Got: %s", expectedTaskName, newTask.TaskName)
	}
}

func TestPrintTask_WithTask(t *testing.T) {
	expectedTaskName := "Testing Task"
	newTask := AddTask("Testing Task")

	printOutput := PrintTask(newTask)
	expectedPrintOutput := "Task - " + expectedTaskName

	if printOutput != expectedPrintOutput {
		t.Errorf("PrintTask Failed - Expected: %s Got: %s", expectedPrintOutput, printOutput)
	}
}

func TestPrintTask_Empty(t *testing.T) {

	printOutput := PrintTask(Pomodoro{})
	expectedPrintOutput := "Current task is empty"

	if printOutput != expectedPrintOutput {
		t.Errorf("PrintTask Failed - Expected: %s Got: %s", expectedPrintOutput, printOutput)
	}
}
```

In all of the tests, we are following a simple pattern.

1. Call the function with the intended inputs - `newTask := AddTask(expectedTaskName)`
2. Create a expectedOutput variable with the expected output - `expectedTaskName := "Testing task"`
3. Check if the output of the function and expected output are equal - `if newTask.TaskName != expectedTaskName`

Run the `go test` command to run the tests

## Creating CLI for users to interact

We have the business logic for our application completed and so now let's focus on creating the CLI for the users to add task

We are going to just use standard packages of Go to get the input. There are other packages that are suitable for creating a full-fledged CLI but those are outside the scope of this article. You can refer [cobra](https://pkg.go.dev/github.com/spf13/cobra) if you want a more powerful package

In this article, we will use the `flags` package of the standard library to get the input from the user.

```
func main() {
	taskPtr := flag.String("task", "", "Task to add. (Required)")
	flag.Parse()
}
```

### Getting input from the user

`flag` package has a few functions to help in getting the input. We are using the `flag.String` function to get input. We are passing in three arguments as follows

1. Name of the flag - Name used by the user in the CLI to send input
2. Default value - Provide a default value if the user has not entered the argument
3. Usage - String to help explain the argument to the user

flag package returns a pointer to a string and not the actual value. So to access the value inside the pointer, we need to use the `*{variable_name}`If you are unfamiliar with pointers, you can refer to this [link](https://gobyexample.com/pointers)

`Parse()` function needs to be called after defining all the flags and before reading the input from the flags.

### Checking for required input flag

```
	if *taskPtr == "" {
		flag.PrintDefaults()
		os.Exit(1)
	}

	currentTask = AddTask(*taskPtr)
```

Since we want the "task" to be a required input from the user, we can have an if condition checking the value of the flag and exit if it's empty.

Calling the `flag.PrintDefaults()` will print the usage string of the flag and tell the user that "task" input is required

After this, we can assign the task to our `currentTask` variable by calling the `AddTask` function and passing the value of the task pointer

### Creating the timer for the task

Following the pattern of this article and keeping the idea of simplicity, we will use the standard `time` package again to create a timer and wait for the timer to complete.

`NewTimer` function is present in the `time` package which can be used to create a timer

```
	currentTask = AddTask(*taskPtr)
	
	fmt.Printf("Start the task! Focus on %s\n", *taskPtr)

	timer1 := time.NewTimer(25 * time.Minute)
	<-timer1.C

	fmt.Println("Congrats! Task time is complete. Take a break")
```

timer object will return a pointer which will contain a channel that notifies how long timer will have to wait

The syntax for the NewTimer function

```
func NewTimer(d Duration) *Timer
```

We are providing a duration of 25 minutes and so we can multiply 25 with the `time.Minute` constant to let the timer run for 25 minutes

> For testing the code, change the time.Minute to time.Second

The app will now wait for the timer to complete since we are waiting for the notification from the timer channel. So the final `Println` statement will only get printed after the timer is complete.

This method is a naive way to just wait for the asynchronous timer. Note that if the user exits the CLI in the middle, CLI won't be able to respond after that and we are not persisting the task provided by the user.

Here is the full main.go file for clarity

```
package main

import (
	"flag"
	"fmt"
	"os"
	"time"
)

var currentTask Pomodoro = Pomodoro{}

func main() {

	taskPtr := flag.String("task", "", "Task to add. (Required)")
	flag.Parse()

	if *taskPtr == "" {
		flag.PrintDefaults()
		os.Exit(1)
	}

	currentTask = AddTask(*taskPtr)
	fmt.Println(PrintTask(currentTask))

	fmt.Printf("Start the task! Focus on %s\n", *taskPtr)

	timer1 := time.NewTimer(25 * time.Minute)
	<-timer1.C

	fmt.Println("Congrats! Task time is complete. Take a break")
}
```

## Building and Running the CLI

Application is ready to be built and we can test how the user will run the app by passing the task as an argument

To build the app, run the following command in the root folder

```
go build .
```

The above command will generate `pomodoro-go-cli` file. This name is defined in the go.mod file and it will be the initialized module name

If you are in linux based environment, before executing the CLI you need to add the execute permission for that file

```
chmod +x ./pomodoro-go-cli
```

After adding the execute permission for the file, you can run the file using the cmd and pass the `task` argument for our app to run

```
./pomodoro-go-cli --task "Writing blog"

Output:
Task - Writing blog
Start the task! Focus on Writing blog
(After 25 min.....)
Congrats! Task time is complete. Take a break 
```

Let's check the other case when the doesn't specify the task in the cmd

```
./pomodoro-go-cli

Output:
  -task string
        Task to add. (Required)
```

## Summary

Go provides a lot of good packages from the standard library which can help build applications with ease and without a lot of dependencies. These packages are simple to use hence using them to learn basic concepts is useful

In this article, we discussed the following items

1. Initialize a go application
2. Build and Test the business logic
3. Building a CLI interface for users to consume the application
4. Running the CLI

Full source code is available in the following GitHub link[https://github.com/eternaldevgames/pomodoro-go-cli](https://github.com/eternaldevgames/pomodoro-go-cli)

Stay tuned by subscribing to our mailing list and joining our Discord community

[Discord](https://discord.gg/AUjrcK6eep)