每日一库之通过cobra开发天气查询小工具
介绍 cobra
cobra
是一个 Golang
包,它提供了简单的接口来创建命令行程序。同时它也是一个应用程序,用来生成应用框架,从而开发以 cobra
为基础的应用。在 GitHub
上,有更多关于 cobra
的介绍。网上也有关于此项目的一些文章,只是 10 篇文章到最后可能 9 篇都是重复的,仅此而已。
说明
为了不增加文章的重复率,也为了达到学习的效果,通过此项目来实现一些小功能。最终的效果是通过命令行输入城市名或者城市码获取当前城市的温度情况。
实现
入口文件的目的只有一个初始化 cobra
package main
import "github.com/wuqinqiang/go-weather/cmd"
func main() {
_ = cmd.Execute()
}
package cmd
import (
"github.com/spf13/cobra"
)
var (
weatherCmd = &cobra.Command{
Use: "weather",
}
)
func Execute() error {
return weatherCmd.Execute()
}
然后在其他的两个文件中,分别定义了此 cmd
的两个子命令。分别表示通过城市名称 NameCmd
和通过城市码 CodeCmd
来查询天气情况。
通过城市名称
package cmd
import (
"errors"
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/wuqinqiang/go-weather/server"
"github.com/wuqinqiang/go-weather/tools"
)
func init() {
NameCmd.PersistentFlags().StringP("name", "n", "", "input city name")
weatherCmd.AddCommand(NameCmd)
}
var NameCmd = &cobra.Command{
Use: "name",
Short: "check city weather by city name",
Args: func(cmd *cobra.Command, args []string) error {
name, err := cmd.Flags().GetString("name")
if err != nil {
return errors.New("please input city name")
}
if len(name) == 0 {
return errors.New("请携带参数-n 或者 --name")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
name, _ := cmd.Flags().GetString("name")
code := tools.CityMap[name]
if code == 0 {
fmt.Println("这个城市我不想查")
os.Exit(1)
}
info, err := server.GetWeather(code)
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
fmt.Println("查询的天气是:", info)
},
}
通过城市码
package cmd
import (
"errors"
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/wuqinqiang/go-weather/server"
)
func init() {
codeCmd.PersistentFlags().IntP("code", "c", 0, "城市码必须是6位的整数")
weatherCmd.AddCommand(codeCmd)
}
var codeCmd = &cobra.Command{
Use: "code",
Short: "check city weather by city code",
Args: func(cmd *cobra.Command, args []string) error {
code, err := cmd.Flags().GetInt("code")
if err != nil {
return errors.New("请输入城市码")
}
if code == 0 {
return errors.New("请携带参数-c 或者 --code")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
code, _ := cmd.Flags().GetInt("code")
info, err := server.GetWeather(code)
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
fmt.Println("查询的天气是:", info)
},
}
cobra.Command
里面的 Args
主要做一些参数验证,Run
就是实际工作功能,也是核心部分,大部分命令只会实现这一点。
至于获取天气,你可以看到 server.GetWeather
其实就是对接高德的 api
接口,发送了一个 http
请求罢了。
package server
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"time"
"github.com/wuqinqiang/go-weather/entity"
)
const (
Key = "xx"
Uri = "https://restapi.amap.com/v3/weather/weatherInfo"
)
func GetWeather(code int) (map[string]interface{}, error) {
info, err := GetWeatherRequest(code)
if err != nil {
return nil, err
}
infoMap := make(map[string]interface{})
infoMap["城市:"] = info.Lives[0].City
infoMap["天气现象:"] = info.Lives[0].Weather
infoMap["实时气温:"] = info.Lives[0].Temperature
infoMap["数据发布:"] = info.Lives[0].Reporttime
return infoMap, nil
}
func GetWeatherRequest(code int) (entity.ResponseInfo, error) {
info := entity.ResponseInfo{}
client := &http.Client{Timeout: 2 * time.Second}
url := fmt.Sprintf(Uri+"?key=%s&city=%d", Key, code)
resp, err := client.Get(url)
if err != nil {
fmt.Println("查询错误:", err)
return info, errors.New("查询失败")
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("read body err:", err)
return info, errors.New("查询失败")
}
if err := json.Unmarshal(body, &info); err != nil {
fmt.Println("unmarshal response err:", err)
return info, errors.New("查询失败,请输入正确的城市码")
}
if info.Status != "1" {
return info, errors.New("api 密钥key错误,请检查")
}
return info, nil
}
这一块没咋么设计,使得获取天气的信息高度依赖于高德的接口,假设我们现在用其他的方式来获取天气,那么就需要大调整当前的代码。这一块理应抽象化。
最后,整个项目的结构如下:
项目的地址在:github.com/wuqinqiang/go-weather ,感兴趣可以看下
推荐阅读
评论