CLI 命令行实用程序开发实战 - Agenda

项目介绍

参考资料:https://www.cnblogs.com/welhzh/p/8962489.html

开发环境

安装和使用cobra

安装cobra

打开vsc的终端,转到gopath路径(我的是 C:\Users\LLL\go)里面,输入命令安装cobra:

go get -v github.com/spf13/cobra/cobra

https://blog.csdn.net/snowin1994/article/details/88358836

下载过程中发生了以下错误:
在这里插入图片描述
参考了老师所给教程和另一个博客中所给的提示,先到$GOPATH/src/golang.org/x目录下用 git clone 下载 sys 和 text 项目
具体指令为:

mkdir $GOPATH/src/golang.org/x  //创建文件夹
cd $GOPATH/src/golang.org/x  //转至该文件夹
git clone https://github.com/golang/text
git clone https://github.com/golang/sys

改错
发现windows下环境变量有问题,
在这里插入图片描述
所以将后两个命令改为

mkdir C:\Users\LLL\go/src/golang.org/x 
cd C:\Users\LLL\go/src/golang.org/x 

之后便可按原命令go get -v github.com/spf13/cobra/cobra下载。

下载成功后使用命令生成cobra可执行文件

go install github.com/spf13/cobra/cobra

此时,就以安装成功了,查看bin文件夹,可在下面发现已经生成cobra.exe:
在这里插入图片描述

Cobra 的使用

创建一个处理命令 agenda register -uTestUser 或 agenda register --user=TestUser 的小程序。
首先进入src文件夹,用cobra init新建一个demo(cobra只能够在gopath的目录下使用):

cobra init demo --pkg-name=demo

在这里插入图片描述

进入生成的demo文件夹,使用命令来增加register命令:

cobra add register

在这里插入图片描述
之后便产生了需要的文件:
在这里插入图片描述

修改register.go

阅读【main.go】和【root.go】、【register.go】三个文件可得知,然后我们可以通过修改【register.go】来添加相应的操作:

  • init() 函数:
    registerCmd.Flags().StringP("user", "u", "Anonymous", "Help message for username")1
  • Run 匿名回调函数:
    username, _ := cmd.Flags().GetString("user")fmt.Println("register called by " + username)

测试命令

使用以下命令测试:

go run main.go register --user=TestUser
register called by TestUser??

出现register被调用的输出,结果正确:
在这里插入图片描述

Agenda 开发项目

Agenda项目目录

cmd :存放命令实现代码
entity :存放 User 对象读写与处理逻辑
data : 存储User信息和生成log
service:存放命令实现代码所需借用的功能函数
loghelper:存放实现log的相关代码
deepcopy:存放复制功能代码

实现指令

要求

实现2条指令即可,不需要协作。
之前已实现register用户注册指令,这里实现的是login和logout指令。

  • 用户注册
    注册新用户时,用户需设置一个唯一的用户名和一个密码。另外,还需登记邮箱及电话信息。
    如果注册时提供的用户名已由其他用户使用,应反馈一个适当的出错信息;成功注册后,亦应反馈一个成功注册的信息。

  • 用户登录
    用户使用用户名和密码登录 Agenda 系统。
    用户名和密码同时正确则登录成功并反馈一个成功登录的信息。否则,登录失败并反馈一个失败登录的信息。

  • 用户登出
    已登录的用户登出系统后,只能使用用户注册和用户登录功能。

添加命令

使用命令

cobra add login
cobra add logout

在这里插入图片描述

用户注册

绑定参数:

register a new user, a unique username, a password, an email and a phone required

Usage:
  Agenda register [flags]

Flags:
  -c, --contact string    phone number
  -e, --email string      email address
  -h, --help              help for register
  -p, --password string   password
  -u, --username string   username

register.go如下:

package entity

import (
	"os"
	"io"
	"bufio"
	"path/filepath"
	"errors"
	"log"
	"encoding/json"
	"demo/loghelper"
	"demo/deepcopy"
)

type UserFilter func (*User) bool

var userinfoPath = "/src/demo/data/userinfo"
var curUserPath = "/src/demo/data/curuser.txt"

var curUserName *string
var dirty bool
var uData []User
var errLog *log.Logger

func init()  {
	errLog = loghelp.Error
	dirty = false
	userinfoPath  = filepath.Join(loghelp.GoPath, userinfoPath)
	curUserPath = filepath.Join(loghelp.GoPath, curUserPath)
	if err := readFromFile(); err != nil {
		errLog.Println("readFromFile fail: ",err)
	}
}

func Sync() error {
	if err := writeToFile(); err != nil {
		errLog.Println("writeToFile fail: ",err)
		return err
	}
	return nil
}
func CreateUser(v *User)  {
	uData = append(uData, deepcopy.Copy(*v).(User))
	dirty = true
}
func QueryUser(filter UserFilter)  []User{
	var user []User
	for _, v := range uData {
		if filter(&v) {
			user = append(user,v)
		}
	}
	return user
}

func SetCurUser(u *User)  {
	if u == nil {
		curUserName = nil
		return
	}
	if curUserName == nil {
		p := u.Name
		curUserName = &p
	} else {
		*curUserName = u.Name
	}
}

//return current user
func GetCurUser() (User, error) {
	if curUserName == nil {
		return User{}, errors.New("Current user doesn't exist")
	}
	for _, v := range uData {
		if v.Name == *curUserName {
			return v, nil
		}
	}
	return User{}, errors.New("Current user doesn't exist")
}

func Logout() error {
	curUserName = nil
	return Sync()
}
func readFromFile() error {
	var e []error
	str, err1 := readString(curUserPath)
	if err1 != nil {
		e = append(e, err1)
	}
	curUserName = str
	if err := readUser(); err != nil {
		e = append(e, err)
	}
	//if err := readMet(); err != nil {
	//	e = append(e, err)
	//}
	if len(e) == 0 {
		return nil
	}
	er := e[0]
	for i := 1; i < len(e); i++ {
		er = errors.New(er.Error() + e[i].Error())
	}
	return er
}

// writeToFile : write file content from memory
// @return if fail, error will be returned
func writeToFile() error {
	var e []error
	if err := writeString(curUserPath, curUserName); err != nil {
		e = append(e, err)
	}
	if dirty {
		if err := writeJSON(userinfoPath, uData); err != nil {
			e = append(e, err)
		}
		//if err := writeJSON(metinfoPath, mData); err != nil {
		//	e = append(e, err)
		//}
	}
	if len(e) == 0 {
		return nil
	}
	er := e[0]
	for i := 1; i < len(e); i++ {
		er = errors.New(er.Error() + e[i].Error())
	}
	return er
}

func readUser() error {
	file, err := os.Open(userinfoPath);
	if err != nil {
		errLog.Println("Open File Fail:", userinfoPath, err)
		return err
	}
	defer file.Close()

	dec := json.NewDecoder(file)
	switch err := dec.Decode(&uData); err {
	case nil, io.EOF:
		return nil
	default:
		errLog.Println("Decode User Fail:", err)
		return err
	}
}

/*
func readMet() error {
	file, err := os.Open(metinfoPath);
	if err != nil {
		errLog.Println("Open File Fail:", metinfoPath, err)
		return err
	}
	defer file.Close()

	dec := json.NewDecoder(file)
	switch err := dec.Decode(&mData); err {
	case nil, io.EOF:
		return nil
	default:
		errLog.Println("Decode Met Fail:", err)
		return err
	}
}
*/

func writeJSON(fpath string, data interface{}) error {
	file, err := os.Create(fpath);
	if err != nil {
		return err
	}
	defer file.Close()

	enc := json.NewEncoder(file)

	if err := enc.Encode(&data); err != nil {
		errLog.Println("writeJSON:", err)
		return err
	}
	return nil
}

func writeString(path string, data *string) error {
	file, err := os.Create(path)
	if err != nil {
		loghelp.Error.Println("Create file error:", path)
		return err
	}
	defer file.Close()

	writer := bufio.NewWriter(file)
	if data != nil {
		if _, err := writer.WriteString(*data); err != nil {
			loghelp.Error.Println("Write file fail:", path)
			return err
		}
	}
	if err := writer.Flush(); err != nil {
		loghelp.Error.Println("Flush file fail:", path)
		return err
	}
	return nil
}

func readString(path string) (*string, error) {
	file, err := os.Open(path)
	if err != nil {
		loghelp.Error.Println("Open file error:", path)
		return nil, err
	}
	defer file.Close()

	reader := bufio.NewReader(file)
	str, err := reader.ReadString('\n');
	if err != nil && err != io.EOF {
		loghelp.Error.Println("Read file fail:", path)
		return nil, err
	}
	return &str, nil
}

用户登录

用命令login -u -p来实现用户登录功能。
login.go如下:

/*
Copyright ? 2019 NAME HERE <EMAIL ADDRESS>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd

import (
	"fmt"
	"demo/service"
	"github.com/spf13/cobra"
)

var (
	username *string
	password *string
)
// loginCmd represents the login command
var loginCmd = &cobra.Command{
	Use:   "login",
	Short: "For User To Login",
	Run: func(cmd *cobra.Command, args []string) {
		tmpu, _ := cmd.Flags().GetString("username")
		tmppw, _ := cmd.Flags().GetString("password")

		if tmpu == "" || tmppw == "" {
			fmt.Println("Input the username[-u],password[-p]")
			return;
		}
		if _, flag := service.GetCurUser(); flag == true {
			fmt.Println("Please logout firstly")
			return
		}
		if tf := service.UserLogin(tmpu,tmppw); tf == true {
			fmt.Println("Login Successfully!")
		} else {
			fmt.Println("Login Fail: wront username or password")
		}
		return
	},
}

func init() {
	rootCmd.AddCommand(loginCmd)
	loginCmd.Flags().StringP("username","u","","agenda account username")
	loginCmd.Flags().StringP("password","p","","agenda account password")
}

用户登出

logout.go如下:

/*
Copyright ? 2019 NAME HERE <EMAIL ADDRESS>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd

import (
	"fmt"
	"demo/service"
	"github.com/spf13/cobra"
)

// logoutCmd represents the logout command
var logoutCmd = &cobra.Command{
	Use:   "logout",
	Short: "For User To Logout",
	Run: func(cmd *cobra.Command, args []string) {
		if err := service.UserLogout(); err != true {
			fmt.Println("Error happened, please check the error log")
		} else {
			fmt.Println("Logout Successfully!")
		}
	},
}

func init() {
	rootCmd.AddCommand(logoutCmd)
}

service中的函数

service.go如下:

package service

import (
	"demo/entity"
	"demo/loghelper"
	"log"
)

var curuserinfo = "/src/demo/data/curuser.txt"
var errLog *log.Logger
type User entity.User

func  init()  {
	errLog = loghelp.Error
}

func GetCurUser() (entity.User, bool) {
	if cu, err := entity.GetCurUser(); err != nil {
		return cu, false
	} else {
		return cu, true
	}
}
//Regist
func UserRegister(username string, password string,  email string, phone string) (bool, error)  {
	user := entity.QueryUser(func (u *entity.User) bool {
		return u.Name == username
	})
	if len(user) == 1 {
		errLog.Println("User Register: username already exists")
		return false, nil
	}
	entity.CreateUser(&entity.User{username,password,email,phone})
	if err := entity.Sync(); err != nil {
		return true, err
	}
	return true, nil
}

//Login
func UserLogin(username, password string) bool  {
	user := entity.QueryUser(func (u *entity.User) bool {
		if u.Name == username && u.Password == password {
			return true
		}
		return false
	})
	if len(user) == 0 {
		errLog.Println("Login: User not exist")
		return false
	}
	entity.SetCurUser(&user[0])
	if err := entity.Sync(); err != nil {
		errLog.Println("Login: error happened when set curuser")
		return false
	}
	return true
}

//Logout
func UserLogout() bool {
	if err := entity.Logout(); err != nil {
		return false
	} else {
		return true
	}
}

运行结果

创建新用户:
在这里插入图片描述
当创建新用户时用了重复的用户名时报错:
在这里插入图片描述
用户登录:
在这里插入图片描述
重复登录报错:
在这里插入图片描述
用户密码错误报错:
在这里插入图片描述
登出:
在这里插入图片描述

完整代码

请见我的GitHub

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值