Home >Backend Development >Golang >Build a CLI program using Go

Build a CLI program using Go

Guanhui
Guanhuiforward
2020-06-12 18:28:173637browse

Build a CLI program using Go

You may have practiced Go syntax again and again, but unless you build an application yourself, you have not experienced the real feel of writing applications in Go. .

In this blog post, we will build a CLI application in Go, let’s call it go-grab-xkcd. The application pulls comics from XKCD and provides them for you through command line parameters Provide various operation options.

We will build the entire application using only the Go standard library without using external dependencies.

The idea of ​​this application may seem a bit silly, but the purpose is Practice writing production-level (sort of) code in Go without trying to get acquired by Google.

There’s a bonus at the end

Note: This article assumes that the reader is already familiar with Go syntax and terminology, and Somewhere between beginner and intermediate.

Let's run the app first and then move on

$ go-grab-xkcd --help
Usage of go-grab-xkcd:
  -n int
        Comic number to fetch (default latest)
  -o string
        Print output in format: text/json (default "text")
  -s    Save image to current directory
  -t int
        Client timeout in seconds (default 30)
$ go-grab-xkcd -n 323
Title: Ballmer Peak
Comic No: 323
Date: 1-10-2007
Description: Apple uses automated schnapps IVs.
Image: https://imgs.xkcd.com/comics/ballmer_peak.png
$ go-grab-xkcd -n 323 -o json
{
  "title": "Ballmer Peak",
  "number": 323,
  "date": "1-10-2007",
  "description": "Apple uses automated schnapps IVs.",
  "image": "https://imgs.xkcd.com/comics/ballmer_peak.png"
}

You can try other options by downloading and running the app on your computer .

After the end of this tutorial you will be familiar with the following topics:

Receiving command line parameters

Converting between JSON and Go structures

Make API calls

Create files (download and save from the network)

String operations

The following is the project structure

$ tree go-grab-xkcd
go-grab-xkcd
├── client
│   └── xkcd.go
└── model
    └── comic.go
├── main.go
└── go.mod
go.mod - Go Modules file used in Go for package management
main.go - Main entrypoint of the application
comic.go - Go representation of the data as a struct and operations on it
xkcd.go - xkcd client for making HTTP calls to the API, parsing response and saving to disk

1: Initialize project

Create a go.mod file -

$ go mod init

This will help you with package management (think package.json files in JS).

2: xkcd API

xkcd is amazing and you don’t need any registration or access keys to use their API. Open the xkcd API "Documentation" and you will find that there are two endpoints -

http://xkcd.com/info.0.json - Get the latest comics

http://xkcd. com/614/info.0.json - Get the specified comic by comic number

The following is the JSON response of these endpoints-

{
  "num": 2311,
  "month": "5",
  "day": "25",
  "year": "2020",
  "title": "Confidence Interval",
  "alt": "The worst part is that's the millisigma interval.",
  "img": "https://imgs.xkcd.com/comics/confidence_interval.png",
  "safe_title": "Confidence Interval",
  "link": "",
  "news": "",
  "transcript": ""
}

Article related xkcd

2: Comic creation model

According to the above JSON response, we create a struct called ComicResponse in comic.go in the model package

type ComicResponse struct {
    Month      string `json:"month"`
    Num        int    `json:"num"`
    Link       string `json:"link"`
    Year       string `json:"year"`
    News       string `json:"news"`
    SafeTitle  string `json:"safe_title"`
    Transcript string `json:"transcript"`
    Alt        string `json:"alt"`
    Img        string `json:"img"`
    Title      string `json:"title"`
    Day        string `json:"day"`
}

You can use the JSON-to-Go tool to automatically generate it from JSON Structure.

By the way create another structure that will be used to output data from our application.

type Comic struct {
    Title       string `json:"title"`
    Number      int    `json:"number"`
    Date        string `json:"date"`
    Description string `json:"description"`
    Image       string `json:"image"`
}

Add the following two methods to the ComicResponse structure

// FormattedDate 函数将格式化日期元素为一个字符串
func (cr ComicResponse) FormattedDate() string {
    return fmt.Sprintf("%s-%s-%s", cr.Day, cr.Month, cr.Year)
}
// Comic 函数将从 API 接收到的 ComicResponse 转换为应用程序的输出格式, Comic 结构体
func (cr ComicResponse) Comic() Comic {
    return Comic{
        Title:       cr.Title,
        Number:      cr.Num,
        Date:        cr.FormattedDate(),
        Description: cr.Alt,
        Image:       cr.Img,
    }
}

Then add the following two methods to the Comic structure

// PrettyString 函数创建一个漂亮的 Comic 字符串并用于输出
func (c Comic) PrettyString() string {
    p := fmt.Sprintf(
        "Title: %s\nComic No: %d\nDate: %s\nDescription: %s\nImage: %s\n",
        c.Title, c.Number, c.Date, c.Description, c.Image)
    return p
}
// JSON 函数将 Comic 结构体转换为 JSON, 我们将使用 JSON 字符串作为输出内容
func (c Comic) JSON() string {
    cJSON, err := json.Marshal(c)
    if err != nil {
        return ""
    }
    return string(cJSON)
}

3: Set up the xkcd client to initiate a request, parse the response and save it to disk

Create xkcd in the client package. go file.

First define a custom type called ComicNumber, the data type is int

type ComicNumber int

Define constants

const (
    // xkcd 的 BaseURL
    BaseURL string = "https://xkcd.com"
    // DefaultClientTimeout 是取消请求之前要等待的时间
    DefaultClientTimeout time.Duration = 30 * time.Second
    // 根据 xkcd API, LatestComic 是最新的漫画编号
    LatestComic ComicNumber = 0
)

Create a structure XKCDClient, which will be used to make requests to the API.

// XKCDClient 是 XKCD 的客户端结构体
type XKCDClient struct {
    client  *http.Client
    baseURL string
}
// NewXKCDClient 创建一个新的 XKCDClient
func NewXKCDClient() *XKCDClient {
    return &XKCDClient{
        client: &http.Client{
            Timeout: DefaultClientTimeout,
        },
        baseURL: BaseURL,
    }
}

Add the following 4 methods to XKCDClient

SetTimeout()

// SetTimeout 重写了默认的 ClientTimeout
func (hc *XKCDClient) SetTimeout(d time.Duration) {
    hc.client.Timeout = d
}

Fetch()

// Fetch 根据提供的漫画编号检索漫画
func (hc *XKCDClient) Fetch(n ComicNumber, save bool) (model.Comic, error) {
    resp, err := hc.client.Get(hc.buildURL(n))
    if err != nil {
        return model.Comic{}, err
    }
    defer resp.Body.Close()
    var comicResp model.ComicResponse
    if err := json.NewDecoder(resp.Body).Decode(&comicResp); err != nil {
        return model.Comic{}, err
    }
    if save {
        if err := hc.SaveToDisk(comicResp.Img, "."); err != nil {
            fmt.Println("Failed to save image!")
        }
    }
    return comicResp.Comic(), nil
}

SaveToDisk()

// SaveToDisk 下载并保存漫画到本地磁盘
func (hc *XKCDClient) SaveToDisk(url, savePath string) error {
    resp, err := http.Get(url)
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    absSavePath, _ := filepath.Abs(savePath)
    filePath := fmt.Sprintf("%s/%s", absSavePath, path.Base(url))
    file, err := os.Create(filePath)
    if err != nil {
        return err
    }
    defer file.Close()
    _, err = io.Copy(file, resp.Body)
    if err != nil {
        return err
    }
    return nil
}

buildURL()

func (hc *XKCDClient) buildURL(n ComicNumber) string {
    var finalURL string
    if n == LatestComic {
        finalURL = fmt.Sprintf("%s/info.0.json", hc.baseURL)
    } else {
        finalURL = fmt.Sprintf("%s/%d/info.0.json", hc.baseURL, n)
    }
    return finalURL
}

4: Connect all

Inside the main() function we link all the content

read Command line parameters

Instantiate XKCDClient

Use XKCDClient to pull data from the API

Output

Read command line parameters

comicNo := flag.Int(
    "n", int(client.LatestComic), "Comic number to fetch (default latest)",
)
clientTimeout := flag.Int64(
    "t", int64(client.DefaultClientTimeout.Seconds()), "Client timeout in seconds",
)
saveImage := flag.Bool(
    "s", false, "Save image to current directory",
)
outputType := flag.String(
    "o", "text", "Print output in format: text/json",
)

flag.Parse()

Instantiate XKCDClient

xkcdClient := client.NewXKCDClient()
xkcdClient.SetTimeout(time.Duration(*clientTimeout) * time.Second)

Use XKCDClient to pull data from API

comic, err := xkcdClient.Fetch(client.ComicNumber(*comicNo), *saveImage)
if err != nil {
    log.Println(err)
}

Output

if *outputType == "json" {
    fmt.Println(comic.JSON())
} else {
    fmt.Println(comic.PrettyString())
}

The program runs as follows

$ go run main.go -n 323 -o json

Or build it as a binary executable on your laptop and run it

$ go build .
$ ./go-grab-xkcd -n 323 -s -o json

The complete source code can be found in this Github repository - go-grab-xkcd

Extra bonus

You can download multiple comics in sequence by using this simple shell magic tool

$ for i in {1..10}; do ./go-grab-xkcd -n $i -s; done;

The above shell code simply calls go-grab-xkcd in a for loop command, since xkcd uses sequence integers, replace the i value with the comic number as the comic number/ID.

Recommended tutorials: "PHP" "Go Tutorial"

The above is the detailed content of Build a CLI program using Go. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:learnku.com. If there is any infringement, please contact admin@php.cn delete
Previous article:Go String parsingNext article:Go String parsing