-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathversion.go
256 lines (234 loc) · 7.04 KB
/
version.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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
package versions
import (
"encoding/json"
"errors"
"time"
compare_anything "github.com/golang-infrastructure/go-compare-anything"
)
var (
// ErrVersionInvalid 表示版本号格式无效的错误
//
// 当尝试解析不符合要求的版本号字符串时返回此错误
ErrVersionInvalid = errors.New("version invalid")
)
// Version 用于表示一个版本号
//
// Version 结构体封装了版本号的各个组成部分,包括原始字符串、发布时间、数字部分、
// 前缀和后缀。它支持版本号的解析、比较和分组等操作,实现了 Comparable 接口以便
// 进行版本排序。
//
// 一个典型的版本号格式可能为:v1.2.3-beta1,其中:
// - "v" 是前缀
// - "1.2.3" 是数字部分
// - "-beta1" 是后缀
//
// 使用示例:
//
// // 创建一个版本对象
// version := versions.NewVersion("v1.2.3-rc1")
//
// // 检查版本是否有效
// if version.IsValid() {
// fmt.Printf("版本号有效: %s\n", version.Raw)
// fmt.Printf("版本号数字部分: %v\n", version.VersionNumbers)
// }
//
// // 比较两个版本
// v1 := versions.NewVersion("1.2.3")
// v2 := versions.NewVersion("1.3.0")
// if v1.CompareTo(v2) < 0 {
// fmt.Println("v1 比 v2 旧")
// }
type Version struct {
// Raw 原始的版本号字符串
Raw string `json:"raw"`
// PublicTime 此版本的发布时间
PublicTime time.Time `json:"public_time"`
// VersionNumbers 版本号中的数字部分
// 例如对于版本号 "v1.2.3-beta1",VersionNumbers 为 [1,2,3]
VersionNumbers VersionNumbers `json:"version_numbers"`
// Prefix 版本号数字部分之前的前缀
// 例如对于版本号 "v1.2.3",Prefix 为 "v"
Prefix VersionPrefix `json:"prefix"`
// Suffix 版本号数字部分之后的后缀
// 例如对于版本号 "1.2.3-beta1",Suffix 为 "-beta1"
Suffix VersionSuffix `json:"suffix"`
}
var _ compare_anything.Comparable[*Version] = &Version{}
// NewVersion 从版本字符串创建一个新的 Version 对象
//
// 该方法解析给定的版本字符串,并返回一个填充了相应字段的 Version 对象。
// 即使版本字符串格式不正确,该方法也会返回一个对象,但其 IsValid() 方法可能返回 false。
//
// 参数:
// - versionStr: 要解析的版本号字符串,如 "1.2.3" 或 "v1.2.3-rc1"
//
// 返回:
// - *Version: 解析后的 Version 对象
//
// 使用示例:
//
// version := versions.NewVersion("v1.2.3-beta1")
func NewVersion(versionStr string) *Version {
return NewVersionStringParser(versionStr).Parse()
}
// NewVersionE 从版本字符串创建一个新的 Version 对象,并返回可能的错误
//
// 与 NewVersion 不同,该方法会在版本字符串格式不正确时返回错误。
//
// 参数:
// - versionStr: 要解析的版本号字符串
//
// 返回:
// - *Version: 解析后的 Version 对象,如果解析失败则为 nil
// - error: 如果版本号无效,则返回 ErrVersionInvalid 错误
//
// 使用示例:
//
// version, err := versions.NewVersionE("v1.2.3-beta1")
// if err != nil {
// log.Fatalf("无效的版本号: %v", err)
// }
func NewVersionE(versionStr string) (*Version, error) {
v := NewVersionStringParser(versionStr).Parse()
if v.IsValid() {
return v, nil
} else {
return nil, ErrVersionInvalid
}
}
// NewVersions 批量创建多个 Version 对象
//
// 该方法接受多个版本字符串,并返回相应的 Version 对象数组。
//
// 参数:
// - versionStringSlice: 一个或多个版本号字符串
//
// 返回:
// - []*Version: 解析后的 Version 对象数组
//
// 使用示例:
//
// versions := versions.NewVersions("1.0.0", "1.1.0", "2.0.0")
// for _, v := range versions {
// fmt.Println(v.Raw)
// }
func NewVersions(versionStringSlice ...string) []*Version {
versions := make([]*Version, len(versionStringSlice))
for i, versionStr := range versionStringSlice {
versions[i] = NewVersion(versionStr)
}
return versions
}
// IsValid 检查版本号是否有效
//
// 判断依据是版本号中是否包含数字部分。只有当解析到了版本号数字时才认为是有效的版本号。
//
// 返回:
// - bool: 如果版本号有效则返回 true,否则返回 false
//
// 使用示例:
//
// version := versions.NewVersion("not-a-version")
// if !version.IsValid() {
// fmt.Println("无效的版本号")
// }
func (v *Version) IsValid() bool {
// 版本号数组不为空,则表示为有效版本
return len(v.VersionNumbers) > 0
}
// BuildGroupID 构造版本所属的组的ID
//
// 该方法根据版本号的数字部分生成一个组ID,用于将相似版本分组。
//
// 返回:
// - string: 表示版本组的ID字符串
//
// 使用示例:
//
// version := versions.NewVersion("1.2.3")
// groupID := version.BuildGroupID()
// fmt.Printf("版本组ID: %s\n", groupID)
func (x *Version) BuildGroupID() string {
return x.VersionNumbers.BuildGroupID()
}
// CompareTo 比较两个版本号
//
// 该方法按以下顺序比较两个版本号:
// 1. 首先比较主版本号数字部分
// 2. 其次比较发布时间
// 3. 然后比较后缀
// 4. 最后比较原始版本号字符串
//
// 参数:
// - target: 要比较的目标版本对象
//
// 返回:
// - int: 如果当前版本小于目标版本,返回-1;如果相等,返回0;如果大于,返回1
//
// 使用示例:
//
// v1 := versions.NewVersion("1.0.0")
// v2 := versions.NewVersion("1.1.0")
//
// switch v1.CompareTo(v2) {
// case -1:
// fmt.Println("v1 < v2")
// case 0:
// fmt.Println("v1 = v2")
// case 1:
// fmt.Println("v1 > v2")
// }
func (x *Version) CompareTo(target *Version) int {
// 1. 先按照主版本号排序,仅当两个的主版本号都存在的时候才会进行比较,它们的长度不必相等,但是不能有为空的
if len(x.VersionNumbers) != 0 && len(target.VersionNumbers) != 0 {
r := x.VersionNumbers.CompareTo(target.VersionNumbers)
if r != 0 {
return r
}
}
// 2. 然后按照发布时间排序
if !target.PublicTime.IsZero() && !x.PublicTime.IsZero() {
r2 := x.PublicTime.UnixMilli() - target.PublicTime.UnixMilli()
if r2 != 0 {
// 不做类型转换是为了避免特殊情况下因为类型转换而丢失精度结果错误,而采用比较的方式
if r2 > 0 {
return 1
} else {
return -1
}
}
}
// 3. 然后按照后缀的字典序排序,加入有后缀的话
if x.Suffix != EmptyVersionSuffix && target.Suffix != EmptyVersionSuffix {
r := x.Suffix.CompareTo(target.Suffix)
if r != 0 {
return r
}
}
// 4. 最后实在不行就是比较原始版本号的字典序吧
if x.Raw == target.Raw {
return 0
} else if x.Raw < target.Raw {
return -1
} else if x.Raw > target.Raw {
return 1
}
// unreachable
return 0
}
// String 返回版本的JSON字符串表示
//
// 该方法将Version对象序列化为JSON字符串,便于打印和调试。
//
// 返回:
// - string: 版本的JSON字符串表示
//
// 使用示例:
//
// version := versions.NewVersion("1.2.3")
// fmt.Println(version.String()) // 输出JSON格式的版本信息
func (x *Version) String() string {
marshal, _ := json.Marshal(x)
return string(marshal)
}