0%

cobra使用

1.1开始

Cobra 是一个用于 Go 的 CLI 框架。它包含一个用于创建功能强大的现代 CLI 应用程序的库,以及一个用于快速生成基于 Cobra 的应用程序和命令文件的工具。

它是由 Go 团队成员 spf13hugo 创建的,并且已被最流行的 Go 项目采用。

安装

1
go get -u github.com/spf13/cobra

概念

Cobra 建立在命令、参数和标志的结构之上。

命令 代表操作,参数 是事物,标志 是这些操作的修饰符。

最好的应用程序在使用时会像句子一样易于理解。用户会知道如何使用该应用程序,因为他们会本能地理解如何使用它。

要遵循的模式是 APPNAME VERB NOUN --ADJECTIVE.APPNAME COMMAND ARG --FLAG

一些好的现实世界示例可以更好地说明这一点。

比如:

1
git clone URL

1.2解释

概念 定义与作用 代码示例 (结构体字段)
命令 (Commands) 代表一个具体的执行动作。可以拥有子命令,形成树状结构。 UseShortLongRun / RunE
标志 (Flags) 用于修改命令的行为,分为 全局(Persistent)局部(Local) 两种。 定义在 init() 中,使用 PersistentFlags()Flags()
参数 (Args) 命令执行时传递的、不依赖于“-”或“–”的参数。 通过 Args 字段进行验证,在 Run 函数中通过 args 数组访问
1
2
3
4
5
6
▾ yourApp/
▾ cmd/
root.go # 根命令定义
add.go # 子命令示例
list.go # 子命令示例
main.go # 程序入口

root.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package cmd

import (
"fmt"
"os"
"github.com/spf13/cobra"
)

var rootCmd = &cobra.Command{
Use: "myapp", // 工具名称
Short: "这是一个简短的应用描述",
Long: `这是详细的应用描述,可以跨多行。`,
Run: func(cmd *cobra.Command, args []string) {
// 当直接运行 `myapp` 时执行的逻辑
fmt.Println("Hello, Cobra!")
},
}

func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}

main.go

1
2
3
4
5
package main
import "yourApp/cmd"
func main() {
cmd.Execute()
}

终端

1
2
PS D:\project-temp\cobra-test> .\cobra-test.exe myapp
Hello, Cobra!

1.3实践–TodoCLI项目

Todo CLI 是一个专为开发者和命令行爱好者设计的轻量级待办事项管理工具。基于 Go 语言和 Cobra 框架构建,它提供了快速、直观的任务管理体验,让你无需离开终端就能高效组织日常工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@localhost todo-cli]# tree
.
├── cmd
│   ├── add.go
│   ├── delete.go
│   ├── done.go
│   ├── edit.go
│   ├── list.go
│   └── root.go
├── go.mod
├── go.sum
├── main.go

main.go

1
2
3
4
5
6
7
package main

import "todo-cli/cmd"

func main() {
cmd.Execute()
}

add.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package cmd

import (
"encoding/json"
"fmt"
"github.com/spf13/cobra"
"os"
"time"
)

type TodoItem struct {
ID int `json:"id"`
Task string `json:"task"`
Done bool `json:"done"`
CreatedAt time.Time `json:"created_at"`
Priority string `json:"priority,omitempty"`
}

var (
priority string // 局部标志变量
urgent bool // 局部标志变量
)

var addCmd = &cobra.Command{
Use: "add [任务描述]",
Short: "添加一个新的待办事项",
Long: `添加一个待办事项到列表中。可以指定优先级和紧急标志。`,
Args: cobra.MinimumNArgs(1), // 至少需要一个参数
Example: `todo add "买牛奶" #添加简单任务
todo add "写报告" -p high #高优先级任务
todo add "紧急会议" -u #紧急任务`,
RunE: func(cmd *cobra.Command, args []string) error {
// 合并所有参数为任务描述
taskDescription := args[0]
if len(args) > 1 {
for i := 1; i < len(args); i++ {
taskDescription += " " + args[i]
}
}

// 读取现有任务
todos, err := readTodos()
if err != nil {
return fmt.Errorf("读取任务失败: %v", err)
}

// 创建新任务
newTodo := TodoItem{
ID: len(todos) + 1,
Task: taskDescription,
Done: false,
CreatedAt: time.Now(),
Priority: priority,
}

// 如果是紧急任务,添加标识
if urgent {
newTodo.Task = "[紧急] " + newTodo.Task
}

todos = append(todos, newTodo)

// 保存到文件
if err := saveTodos(todos); err != nil {
return fmt.Errorf("保存任务失败: %v", err)
}

fmt.Printf("✅ 已添加任务 #%d: %s\n", newTodo.ID, newTodo.Task)
return nil
},
}

func init() {
rootCmd.AddCommand(addCmd)

// 局部标志:仅对 add 命令有效
addCmd.Flags().StringVarP(&priority, "priority", "p", "normal",
"任务优先级 (low, normal, high)")
addCmd.Flags().BoolVarP(&urgent, "urgent", "u", false, "标记为紧急任务")

// 标志验证
addCmd.PreRunE = func(cmd *cobra.Command, args []string) error {
if priority != "low" && priority != "normal" && priority != "high" {
return fmt.Errorf("优先级必须是 low, normal 或 high")
}
return nil
}
}

// 读取任务列表
func readTodos() ([]TodoItem, error) {
var todos []TodoItem

if _, err := os.Stat(todoFile); os.IsNotExist(err) {
return todos, nil // 文件不存在时返回空列表
}

data, err := os.ReadFile(todoFile)
if err != nil {
return nil, err
}

if err := json.Unmarshal(data, &todos); err != nil {
return nil, err
}

return todos, nil
}

// 保存任务列表
func saveTodos(todos []TodoItem) error {
data, err := json.MarshalIndent(todos, "", " ")
if err != nil {
return err
}

return os.WriteFile(todoFile, data, 0644)
}

delete.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
package cmd

import (
"fmt"
"strconv"
"strings"

"github.com/spf13/cobra"
)

var (
deleteAll bool // 是否删除所有任务
forceDelete bool // 强制删除(不确认)
)

var deleteCmd = &cobra.Command{
Use: "delete [任务ID...]",
Short: "删除一个或多个待办事项",
Long: `删除指定的待办事项。
可以删除单个任务、多个任务或所有任务。
支持使用逗号分隔的ID列表或ID范围。`,
Example: ` todo delete 1 # 删除任务1
todo delete 1 3 5 # 删除任务1,3,5
todo delete 1-3 # 删除任务1到3
todo delete --all # 删除所有任务
todo delete 2,4,6-8 # 混合使用`,
Args: cobra.MinimumNArgs(0), // 允许0个参数(使用--all时)
RunE: func(cmd *cobra.Command, args []string) error {
todos, err := readTodos()
if err != nil {
return fmt.Errorf("读取任务失败: %v", err)
}

if len(todos) == 0 {
fmt.Println("📭 没有可删除的任务")
return nil
}

// 情况1: 删除所有任务
if deleteAll {
if !forceDelete {
fmt.Printf("⚠️ 即将删除所有 %d 个任务,确定吗? (y/N): ", len(todos))
var confirm string
fmt.Scanln(&confirm)
if strings.ToLower(confirm) != "y" {
fmt.Println("操作已取消")
return nil
}
}

// 清空文件
if err := saveTodos([]TodoItem{}); err != nil {
return fmt.Errorf("清空任务失败: %v", err)
}

fmt.Printf("🗑️ 已删除所有 %d 个任务\n", len(todos))
return nil
}

// 情况2: 没有指定任务ID
if len(args) == 0 {
return fmt.Errorf("请指定要删除的任务ID,或使用 --all 删除所有任务")
}

// 解析任务ID(支持多种格式)
idsToDelete, err := parseTaskIDs(args, todos)
if err != nil {
return err
}

if len(idsToDelete) == 0 {
fmt.Println("没有找到匹配的任务")
return nil
}

// 显示将要删除的任务
if !forceDelete {
fmt.Println("将要删除以下任务:")
for _, id := range idsToDelete {
for _, todo := range todos {
if todo.ID == id {
status := "待办"
if todo.Done {
status = "已完成"
}
fmt.Printf(" #%d [%s] %s\n", todo.ID, status, todo.Task)
break
}
}
}

fmt.Printf("\n确认删除 %d 个任务吗? (y/N): ", len(idsToDelete))
var confirm string
fmt.Scanln(&confirm)
if strings.ToLower(confirm) != "y" {
fmt.Println("操作已取消")
return nil
}
}

// 删除任务
var newTodos []TodoItem
deletedCount := 0
for _, todo := range todos {
shouldDelete := false
for _, id := range idsToDelete {
if todo.ID == id {
shouldDelete = true
deletedCount++
break
}
}
if !shouldDelete {
newTodos = append(newTodos, todo)
}
}

// 重新编号(可选,保持ID连续)
for i := range newTodos {
newTodos[i].ID = i + 1
}

// 保存
if err := saveTodos(newTodos); err != nil {
return fmt.Errorf("保存任务失败: %v", err)
}

fmt.Printf("🗑️ 已删除 %d 个任务,剩余 %d 个任务\n", deletedCount, len(newTodos))
return nil
},
}

func init() {
rootCmd.AddCommand(deleteCmd)

// 添加命令别名
deleteCmd.Aliases = []string{"del", "rm"}

// 标志定义
deleteCmd.Flags().BoolVarP(&deleteAll, "all", "a", false, "删除所有任务")
deleteCmd.Flags().BoolVarP(&forceDelete, "force", "y", false, "强制删除,不确认")

// 删除所有任务时,强制标志自动为true
deleteCmd.PreRunE = func(cmd *cobra.Command, args []string) error {
if deleteAll && len(args) > 0 {
return fmt.Errorf("不能同时使用 --all 和指定任务ID")
}
return nil
}
}

// 解析任务ID(支持多种格式)
func parseTaskIDs(args []string, todos []TodoItem) ([]int, error) {
var ids []int
maxID := 0
for _, todo := range todos {
if todo.ID > maxID {
maxID = todo.ID
}
}

for _, arg := range args {
// 检查是否是范围格式 "1-3"
if strings.Contains(arg, "-") {
parts := strings.Split(arg, "-")
if len(parts) != 2 {
return nil, fmt.Errorf("无效的范围格式: %s,请使用如 1-3 的格式", arg)
}

start, err1 := strconv.Atoi(parts[0])
end, err2 := strconv.Atoi(parts[1])
if err1 != nil || err2 != nil {
return nil, fmt.Errorf("无效的范围: %s,必须是数字", arg)
}

if start < 1 || end > maxID || start > end {
return nil, fmt.Errorf("无效的范围: %d-%d,有效ID为1-%d", start, end, maxID)
}

for i := start; i <= end; i++ {
ids = append(ids, i)
}
continue
}

// 检查是否是用逗号分隔的列表 "1,3,5"
if strings.Contains(arg, ",") {
parts := strings.Split(arg, ",")
for _, part := range parts {
id, err := strconv.Atoi(strings.TrimSpace(part))
if err != nil {
return nil, fmt.Errorf("无效的ID: %s", part)
}
if id < 1 || id > maxID {
return nil, fmt.Errorf("任务ID %d 不存在,有效ID为1-%d", id, maxID)
}
ids = append(ids, id)
}
continue
}

// 单个ID
id, err := strconv.Atoi(arg)
if err != nil {
return nil, fmt.Errorf("无效的ID: %s", arg)
}
if id < 1 || id > maxID {
return nil, fmt.Errorf("任务ID %d 不存在,有效ID为1-%d", id, maxID)
}
ids = append(ids, id)
}

// 去重
return removeDuplicates(ids), nil
}

// 去除重复的ID
func removeDuplicates(ids []int) []int {
seen := make(map[int]bool)
result := []int{}
for _, id := range ids {
if !seen[id] {
seen[id] = true
result = append(result, id)
}
}
return result
}

done.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package cmd

import (
"fmt"
"github.com/spf13/cobra"
"strconv"
)

var doneCmd = &cobra.Command{
Use: "done [任务ID]",
Short: "标记任务为已完成",
Args: cobra.ExactArgs(1), // 必须且只能有一个参数
RunE: func(cmd *cobra.Command, args []string) error {
idStr := args[0]
id, err := strconv.Atoi(idStr)
if err != nil {
return fmt.Errorf("任务ID必须是数字")
}

todos, err := readTodos()
if err != nil {
return fmt.Errorf("读取任务失败: %v", err)
}

found := false
for i := range todos {
if todos[i].ID == id {
if todos[i].Done {
fmt.Printf("⚠️ 任务 #%d 已经是完成状态\n", id)
} else {
todos[i].Done = true
fmt.Printf("🎉 已完成任务 #%d: %s\n", id, todos[i].Task)
}
found = true
break
}
}

if !found {
return fmt.Errorf("未找到任务 #%d", id)
}

// 保存更新
if err := saveTodos(todos); err != nil {
return fmt.Errorf("保存任务失败: %v", err)
}

return nil
},
}

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

edit.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
package cmd

import (
"fmt"
"strconv"
"strings"

"github.com/spf13/cobra"
)

// 编辑模式选项
var (
newDescription string // 新任务描述
newPriority string // 新优先级
toggleStatus bool // 切换完成状态
markUrgent bool // 标记为紧急
markNormal bool // 取消紧急标记
)

var editCmd = &cobra.Command{
Use: "edit [任务ID]",
Short: "编辑待办事项",
Long: `编辑待办事项。
可以修改任务描述、优先级、状态和紧急标记。`,
Example: ` todo edit 1 -d "新描述" # 修改描述
todo edit 2 -p high # 修改优先级为高
todo edit 3 -t # 切换完成状态
todo edit 4 -u # 标记为紧急
todo edit 5 -n # 取消紧急标记

# 组合使用
todo edit 1 -d "新任务" -p low -t`,
Args: cobra.ExactArgs(1), // 必须有一个参数(任务ID)
RunE: func(cmd *cobra.Command, args []string) error {
idStr := args[0]
id, err := strconv.Atoi(idStr)
if err != nil {
return fmt.Errorf("任务ID必须是数字")
}

todos, err := readTodos()
if err != nil {
return fmt.Errorf("读取任务失败: %v", err)
}

// 查找要编辑的任务
taskIndex := -1
for i, todo := range todos {
if todo.ID == id {
taskIndex = i
break
}
}

if taskIndex == -1 {
return fmt.Errorf("未找到任务 #%d", id)
}

originalTodo := todos[taskIndex]

// 如果没有指定任何修改参数,显示错误
if newDescription == "" && newPriority == "" && !toggleStatus && !markUrgent && !markNormal {
fmt.Println("当前任务信息:")
displayTodoDetails(originalTodo)
fmt.Println("\n请至少指定一个修改选项:")
fmt.Println(" -d, --desc 新的任务描述")
fmt.Println(" -p, --priority 设置优先级 (low/normal/high)")
fmt.Println(" -t, --toggle 切换完成状态")
fmt.Println(" -u, --urgent 标记为紧急任务")
fmt.Println(" -n, --normal 取消紧急标记")
return fmt.Errorf("未指定修改内容")
}

modified := false

// 1. 修改描述
if newDescription != "" {
todos[taskIndex].Task = newDescription
modified = true
fmt.Printf("📝 描述已更新: %s\n", newDescription)
}

// 2. 修改优先级
if newPriority != "" {
todos[taskIndex].Priority = newPriority
modified = true
fmt.Printf("📊 优先级已更新为: %s\n", newPriority)
}

// 3. 切换状态
if toggleStatus {
todos[taskIndex].Done = !todos[taskIndex].Done
modified = true
if todos[taskIndex].Done {
fmt.Printf("✅ 任务标记为已完成\n")
} else {
fmt.Printf("🔄 任务重新标记为待办\n")
}
}

// 4. 标记为紧急
if markUrgent {
if !strings.HasPrefix(todos[taskIndex].Task, "[紧急] ") {
todos[taskIndex].Task = "[紧急] " + todos[taskIndex].Task
modified = true
fmt.Printf("🚨 标记为紧急任务\n")
}
}

// 5. 取消紧急标记
if markNormal {
if strings.HasPrefix(todos[taskIndex].Task, "[紧急] ") {
todos[taskIndex].Task = strings.TrimPrefix(todos[taskIndex].Task, "[紧急] ")
modified = true
fmt.Printf("📌 取消紧急标记\n")
}
}

// 如果没有任何修改(所有指定的修改都是冗余的)
if !modified {
fmt.Println("⚠️ 没有进行任何有效修改")
return nil
}

// 保存修改
if err := saveTodos(todos); err != nil {
return fmt.Errorf("保存任务失败: %v", err)
}

// 显示修改后的结果
fmt.Println("\n✅ 任务已更新:")
displayTodoDetails(todos[taskIndex])

return nil
},
}

func init() {
rootCmd.AddCommand(editCmd)

// 命令别名
editCmd.Aliases = []string{"modify", "update", "e"}

// 标志定义
editCmd.Flags().StringVarP(&newDescription, "desc", "d", "", "新的任务描述")
editCmd.Flags().StringVarP(&newPriority, "priority", "p", "",
"设置优先级 (low/normal/high)")
editCmd.Flags().BoolVarP(&toggleStatus, "toggle", "t", false, "切换完成状态")
editCmd.Flags().BoolVarP(&markUrgent, "urgent", "u", false, "标记为紧急任务")
editCmd.Flags().BoolVarP(&markNormal, "normal", "n", false, "取消紧急标记")

// 标志验证
editCmd.PreRunE = func(cmd *cobra.Command, args []string) error {
// 验证优先级值
if newPriority != "" && newPriority != "low" &&
newPriority != "normal" && newPriority != "high" {
return fmt.Errorf("优先级必须是 low, normal 或 high")
}

// 紧急和普通标记不能同时使用
if markUrgent && markNormal {
return fmt.Errorf("不能同时使用 --urgent 和 --normal")
}

return nil
}
}

// 显示任务详情
func displayTodoDetails(todo TodoItem) {
fmt.Printf("ID: #%d\n", todo.ID)

// 状态和紧急标记
statusMark := "⏳"
if todo.Done {
statusMark = "✅"
}

urgentMark := ""
if strings.HasPrefix(todo.Task, "[紧急] ") {
urgentMark = "🚨 "
}

fmt.Printf("状态: %s ", statusMark)
if todo.Done {
fmt.Printf("已完成\n")
} else {
fmt.Printf("待办\n")
}

fmt.Printf("任务: %s%s\n", urgentMark, todo.Task)

// 优先级
priority := todo.Priority
if priority == "" {
priority = "normal"
}
priorityIcon := "📊"
switch priority {
case "low":
priorityIcon = "📉"
case "high":
priorityIcon = "📈"
}
fmt.Printf("优先级: %s %s\n", priorityIcon, priority)

fmt.Printf("创建时间: %s\n", todo.CreatedAt.Format("2006-01-02 15:04"))
}

list.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package cmd

import (
"fmt"
"github.com/spf13/cobra"
"os"
"text/tabwriter"
)

var (
showAll bool
onlyDone bool
sortBy string
)

var listCmd = &cobra.Command{
Use: "list",
Short: "列出所有待办事项",
Aliases: []string{"ls", "l"}, // 命令别名
RunE: func(cmd *cobra.Command, args []string) error {
todos, err := readTodos()
if err != nil {
return fmt.Errorf("读取任务失败: %v", err)
}

if len(todos) == 0 {
fmt.Println("📭 没有待办事项")
return nil
}

// 过滤任务
var filtered []TodoItem
for _, todo := range todos {
if onlyDone && !todo.Done {
continue
}
if !showAll && todo.Done && !onlyDone {
continue
}
filtered = append(filtered, todo)
}

if len(filtered) == 0 {
fmt.Println("没有符合条件的任务")
return nil
}

// 使用 tabwriter 格式化输出
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
fmt.Fprintln(w, "ID\t状态\t优先级\t任务\t创建时间")
fmt.Fprintln(w, "--\t----\t------\t----\t--------")

for _, todo := range filtered {
status := "待办"
if todo.Done {
status = "✅完成"
}

prio := todo.Priority
if prio == "" {
prio = "normal"
}

timeStr := todo.CreatedAt.Format("2006-01-02 15:04")
fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\n",
todo.ID, status, prio, todo.Task, timeStr)
}

w.Flush()
return nil
},
}

func init() {
rootCmd.AddCommand(listCmd)

// 互斥标志示例
listCmd.Flags().BoolVarP(&showAll, "all", "a", false, "显示所有任务(包括已完成)")
listCmd.Flags().BoolVarP(&onlyDone, "done", "d", false, "只显示已完成的任务")
listCmd.Flags().StringVarP(&sortBy, "sort", "s", "id", "排序方式 (id, time, priority)")

// 标志互斥:--all 和 --done 不能同时使用
listCmd.MarkFlagsMutuallyExclusive("all", "done")
}

root.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package cmd

import (
"fmt"
"github.com/spf13/cobra"
"os"
)

var (
todoFile = "todos.json" // 全局配置:存储文件
version = "1.0.0" // 版本号
)

var rootCmd = &cobra.Command{
Use: "todo",
Short: "一个简单的待办事项管理工具",
Long: `Todo CLI 是一个用于管理日常任务的命令行工具。
你可以添加、查看、标记完成你的待办事项。`,
Version: version, // 设置版本号
// 直接运行 `todo` 时显示帮助
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("使用 'todo --help' 查看所有可用命令")
},
}

// 添加全局标志(所有子命令都可使用)
func init() {
// 全局标志:指定不同的存储文件
rootCmd.PersistentFlags().StringVarP(&todoFile, "file", "f", "todos.json",
"指定待办事项存储文件")

// 设置版本显示模板
rootCmd.SetVersionTemplate(`{{printf "%s 版本 %s\n" .Name .Version}}`)
}

func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Fprintf(os.Stderr, "错误: %v\n", err)
os.Exit(1)
}
}