Compare commits

...

216 Commits

Author SHA1 Message Date
Kevin Wan
49f73265b9 chore: refactor (#3164) 2023-04-24 12:34:52 +08:00
Kevin Wan
7568674b2b chore: use %q instead of %s for unmarshaling (#3163) 2023-04-24 12:11:12 +08:00
Kevin Wan
3da740b7fc chore: remove unnecessary code (#3161) 2023-04-23 22:58:22 +08:00
chen quan
ce4eb6ed61 fix: fixed #2945 (#2953)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2023-04-23 14:22:03 +00:00
MarkJoyMa
9970ff55cd feat: refactor gateway code (#3160) 2023-04-23 22:05:10 +08:00
MarkJoyMa
d10740f871 feat: inheritance rewrite error prompt is more friendly (#3156) 2023-04-23 12:56:54 +00:00
Kevin Wan
027193dc99 chore: refactor gateway (#3157) 2023-04-22 23:25:51 +08:00
Kevin Wan
de1e0f2410 Update readme-cn.md 2023-04-22 22:36:55 +08:00
Kevin Wan
062073ce58 chore: update codecov to ignore mock files (#3155) 2023-04-22 14:07:15 +08:00
MarkJoyMa
e20b02f311 gateway: open timeout function cabinet (#3047)
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2023-04-22 05:23:47 +00:00
Kevin Wan
02357d2616 chore: make error message more readable (#3154) 2023-04-22 13:03:59 +08:00
guangwu
489d69f779 Add debug message on unmarshal errors (#3153) 2023-04-21 13:12:02 +00:00
Kevin Wan
117611a170 fix: default value for keepalive set to 0 (#3152) 2023-04-21 03:15:05 +00:00
Kevin Wan
0a46ad7ac1 chore: fix go 1.18 dep problem (#3149) 2023-04-21 01:01:17 +08:00
Liam Hao
bf905eaff3 fix default grpc-gateway connect timeout (#3142) 2023-04-20 15:13:13 +00:00
dependabot[bot]
88cb35e3d5 chore(deps): bump github.com/alicebob/miniredis/v2 from 2.30.1 to 2.30.2 (#3144)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-20 22:59:57 +08:00
fondoger
078825b4eb [dart-gen] Support Null-safe and omitempty json tag (#3134) 2023-04-17 05:06:52 +00:00
Kevin Wan
bbfce6abe9 chore: refactor max/min in fx (#3135) 2023-04-16 23:40:30 +08:00
suplalalala
0d11ce03a8 feature: two evaluate operate func addtion in package stream (#3129)
Co-authored-by: Riven <Riven.chen@hairobotics.com>
2023-04-16 15:14:25 +00:00
dependabot[bot]
757ed19dc5 chore(deps): bump k8s.io/apimachinery from 0.26.3 to 0.27.1 (#3131)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-15 22:29:46 +08:00
dependabot[bot]
c5fd074aac chore(deps): bump github.com/prometheus/client_golang from 1.14.0 to 1.15.0 (#3122)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-15 22:01:02 +08:00
LiKe
8fa0bd1f1c fix(goctl): test field (#3114) 2023-04-15 21:46:37 +08:00
dependabot[bot]
ede19a89ec chore(deps): bump go.etcd.io/etcd/client/v3 from 3.5.7 to 3.5.8 (#3123)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-15 21:24:11 +08:00
anqiansong
73664b92f0 fix(goctl): missing rpc --style flag (#3112) 2023-04-10 09:48:48 +00:00
Kevin Wan
8d9c2fa22a chore: update go-zero version in goctl (#3109) 2023-04-08 23:24:29 +08:00
cong
22fad4bb9c feat(trace): add trace test helpers (#3108) 2023-04-08 14:52:25 +00:00
lchjczw
189e9bd9da Fix the problem of package name for generated kt code. (#3082)
Co-authored-by: 李春华 <lichunhua@threesoft.cn>
2023-04-08 14:46:34 +00:00
Kevin Wan
98c9b5928a refactor: simplify zrpc stat config (#3107) 2023-04-08 14:45:05 +00:00
Kevin Wan
e13fd62d38 chore: coding style (#3106)
Co-authored-by: cong <zhangcong1992@gmail.com>
2023-04-08 21:37:53 +08:00
李登富
ffacae89eb fix:multipule flag client go package name (#3104)
Co-authored-by: lidengfu <lidengfu@excean.com>
2023-04-08 21:36:45 +08:00
cong
49135fe25e refactor(zrpc): prefer static config for zrpc server statinterceptor (#3105) 2023-04-08 13:03:32 +00:00
MarkJoyMa
2e6402f4b5 fix: fillDefault, handling of nested structs (#3072) 2023-04-08 11:43:13 +00:00
Kevin Wan
07f03ebd0c fix: should not conflict on lower members (#3095) 2023-04-08 14:47:57 +08:00
dependabot[bot]
92f2676afc chore(deps): bump golang.org/x/text from 0.8.0 to 0.9.0 in /tools/goctl (#3101) 2023-04-08 13:28:01 +08:00
dependabot[bot]
1807305e6d chore(deps): bump golang.org/x/net from 0.8.0 to 0.9.0 (#3100) 2023-04-08 13:27:14 +08:00
dependabot[bot]
38a97d4531 chore(deps): bump github.com/spf13/cobra from 1.6.1 to 1.7.0 in /tools/goctl (#3098)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-07 20:41:03 +08:00
dependabot[bot]
b9f98ecc4a chore(deps): bump go.mongodb.org/mongo-driver from 1.11.3 to 1.11.4 (#3097)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-07 20:22:51 +08:00
dependabot[bot]
1dc222f4b2 chore(deps): bump golang.org/x/sys from 0.6.0 to 0.7.0 (#3096)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-05 22:33:40 +08:00
cong
a79b8de24d feat(bloom): bloom support Ctx API (#3089) 2023-04-03 14:37:04 +00:00
cong
5da8a93c75 feat(redis): add ScriptRun API and migrate EvalCtx to ScriptRun for limit, lock and bloom (#3087) 2023-04-02 11:28:25 +08:00
cong
b49fc81618 refactor(redistest): simplify redistest.CreateRedis API (#3086) 2023-04-01 05:02:21 +00:00
cong
6a692453dc chore(ci): fail lint ci when gofmt or tidy check failed (#3085) 2023-04-01 03:41:17 +00:00
Kevin Wan
8d0cceb80c chore: add more tests (#3084) 2023-03-31 22:33:43 +08:00
heyehang
e06abf4f6f fixbug:superfluous response.WriteHeader (#3083) 2023-03-31 13:05:29 +00:00
Kevin Wan
ee555a85da chore: coding style (#3080) 2023-03-31 02:13:48 +00:00
anqiansong
1904af2323 feat(goctl): Support gateway sample generation (#3049) 2023-03-29 09:06:23 +00:00
cong
95b85336d6 refactor(redis): add NonBlock config, disable redis ping by default (#3073) 2023-03-29 02:28:12 +00:00
guoguangwu
ca4ce7bce8 fix : misspelled word (#3075) 2023-03-29 02:25:26 +00:00
Kevin Wan
9065eb90d9 chore: coding style (#3074) 2023-03-28 16:34:22 +00:00
anqiansong
50bc361430 feat(goctl): Add api parser (#2585) 2023-03-28 15:45:26 +00:00
Kevin Wan
455a6c8f97 Update readme-cn.md, add company users. 2023-03-27 12:22:32 +08:00
Kevin Wan
04434646eb chore: refactor zrpc setup (#3064) 2023-03-25 20:40:21 +08:00
dependabot[bot]
992a56e90b chore(deps): bump google.golang.org/grpc from 1.53.0 to 1.54.0 in /tools/goctl (#3061)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-24 16:14:44 +08:00
dependabot[bot]
ed4d5e5813 chore(deps): bump google.golang.org/grpc from 1.53.0 to 1.54.0 (#3060)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-24 11:44:26 +08:00
dependabot[bot]
fe85e7cb42 chore(deps): bump k8s.io/client-go from 0.26.2 to 0.26.3 (#3052)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-24 10:16:45 +08:00
Kevin Wan
9c6b516bb8 fix: #3058 (#3059) 2023-03-23 23:45:57 +08:00
dependabot[bot]
2e9063a9a1 chore(deps): bump go.mongodb.org/mongo-driver from 1.11.2 to 1.11.3 (#3054)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-23 22:46:50 +08:00
dependabot[bot]
c3648be533 chore(deps): bump go.uber.org/automaxprocs from 1.5.1 to 1.5.2 (#3051)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-23 22:23:47 +08:00
Kevin Wan
0ab06f62ca chore: add more tests (#3045) 2023-03-19 23:56:36 +08:00
Kevin Wan
6170d7b790 feat: rest validation on http requests (#3041) 2023-03-19 12:04:18 +00:00
cong
18d163c4f7 fix(executors): periodicalexecutor should handle crash correctly (#3043) 2023-03-18 15:04:15 +00:00
Snake
a561048d59 Fix bug: replace int and float with num type in dart (#3042)
Co-authored-by: zhoumingji <zhoumingji@cmsr.chinamobile.com>
2023-03-18 14:49:22 +00:00
dependabot[bot]
7a647ca40c chore(deps): bump google.golang.org/protobuf from 1.29.1 to 1.30.0 in /tools/goctl (#3037)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-17 20:40:36 +08:00
dependabot[bot]
3f6f14f976 chore(deps): bump google.golang.org/protobuf from 1.29.1 to 1.30.0 (#3036)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-17 20:25:43 +08:00
dependabot[bot]
a78d57bebd chore(deps): bump google.golang.org/protobuf from 1.28.2-0.20220831092852-f930b1dc76e8 to 1.29.1 in /tools/goctl (#3029)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-16 00:20:18 +08:00
dependabot[bot]
74452eb7b5 chore(deps): bump golang.org/x/text from 0.7.0 to 0.8.0 in /tools/goctl (#3028)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-15 23:56:58 +08:00
dependabot[bot]
a9e364a01a chore(deps): bump golang.org/x/net from 0.7.0 to 0.8.0 (#3027)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-15 23:32:23 +08:00
chen quan
29c2e20b41 chore(action): upgrade go 1.18 version in release (#3032) 2023-03-15 12:53:04 +00:00
dependabot[bot]
42c146bcbd chore(deps): bump google.golang.org/protobuf from 1.29.0 to 1.29.1 (#3026)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-15 11:30:19 +08:00
dependabot[bot]
b61e364458 chore(deps): bump github.com/zeromicro/go-zero from 1.4.4 to 1.5.0 in /tools/goctl (#3023) 2023-03-14 08:48:42 +08:00
dependabot[bot]
18a4dcb79f chore(deps): bump github.com/fatih/color from 1.14.1 to 1.15.0 (#3022) 2023-03-14 08:47:44 +08:00
Kevin Wan
60a13f1e53 chore: add more tests (#3018) 2023-03-12 20:42:50 +08:00
sniperwzq
3e093bf34e defines the method to customize http server (#2171) 2023-03-11 23:15:00 +08:00
Kevin Wan
211b9498ef chore: add more tests (#3016) 2023-03-11 22:22:39 +08:00
Kevin Wan
cca45be3c5 chore: refactor orm code (#3015) 2023-03-11 18:03:20 +08:00
YK.xiong
e735915d89 fix QueryRowsPartial getTaggedFieldValueMap func (#2884)
Co-authored-by: yongkun.xiong <weilone@vip.qq.com>
2023-03-11 07:28:09 +00:00
Kevin Wan
f77e2c9cfa chore: add more tests (#3014) 2023-03-11 14:57:56 +08:00
Shyunn
544aa7c432 Added zrpc server custom serverID for custom registration Key when the service is registered on ETCD. (#3008) 2023-03-11 14:34:28 +08:00
Kevin Wan
4cef2b412c fix: avoid unmarshal panic with incorrect map keys #3002 (#3013) 2023-03-11 07:53:57 +08:00
Kevin Wan
123c61ad12 chore: update readme (#3011) 2023-03-10 22:18:48 +08:00
Kevin Wan
fbf129d535 chore: add more tests (#3010) 2023-03-10 21:56:19 +08:00
Kevin Wan
c8a17a97be chore: add more tests (#3009) 2023-03-10 20:48:10 +08:00
Kevin Wan
3a493cd6a6 chore: add more tests (#3006) 2023-03-10 17:36:39 +08:00
Kevin Wan
7a0c04bc21 feat: unique redis addrs and trim spaces (#3004) 2023-03-10 16:09:07 +08:00
iyyzh
3c9fe0b381 init postgresql err (#3003) 2023-03-10 05:58:07 +00:00
文豆芽
f8b2dc8c9f reids cluster bug (#2986)
Co-authored-by: shaocongcong <shao.congcong@yalla.live>
2023-03-09 21:37:34 +08:00
taobig
37cb00d789 Export cache.Option param to NewXXXModel() (#2995) 2023-03-09 13:34:58 +00:00
dependabot[bot]
e3e7bc736b chore(deps): bump google.golang.org/protobuf from 1.28.2-0.20230222093303-bc1253ad3743 to 1.29.0 (#2993)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-09 19:56:14 +08:00
dependabot[bot]
fafbee24b8 chore(deps): bump github.com/golang/protobuf from 1.5.2 to 1.5.3 (#2992)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-09 16:24:37 +08:00
dependabot[bot]
8ec29d29ce chore(deps): bump golang.org/x/sys from 0.5.0 to 0.6.0 (#2983)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-09 15:23:02 +08:00
fabio
cb7f3e8a17 feat(redis):add LpopCount,RpopCount (#2990) 2023-03-09 05:45:45 +00:00
dependabot[bot]
03391b48ca chore(deps): bump github.com/alicebob/miniredis/v2 from 2.30.0 to 2.30.1 (#2991)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-09 13:34:48 +08:00
dependabot[bot]
d0dedb0624 chore(deps): bump github.com/jhump/protoreflect from 1.15.0 to 1.15.1 (#2982)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-08 23:55:43 +08:00
Snake
e136deb3a7 Update goclt dart gen: Add scheme config and content-type header (#2987)
Co-authored-by: zhoumingji <zhoumingji@cmsr.chinamobile.com>
2023-03-08 15:40:55 +00:00
anqiansong
a2592a17e9 format code 2023-03-08 15:32:08 +00:00
anqiansong
05abf4a2ff fix typo 2023-03-08 15:32:08 +00:00
anqiansong
d40000d4b9 fix typo 2023-03-08 15:32:08 +00:00
anqiansong
4620924105 Fix typo 2023-03-08 15:32:08 +00:00
Kevin Wan
a05fe7bf0a chore: remove optional in redis config (#2979) 2023-03-07 14:58:04 +08:00
kevin
dd347e96b0 chore: add comments 2023-03-07 01:27:41 +00:00
kevin
a972f400c6 fix: test failure 2023-03-07 01:27:41 +00:00
kevin.wan
fb7664a764 fix: config map with json tag 2023-03-07 01:27:41 +00:00
Kevin Wan
7d5d7d9085 chore: clear errors on conf conflict keys (#2972)
Co-authored-by: kevin.wan <kevin.wan@yijinin.com>
2023-03-06 12:45:17 +08:00
Kevin Wan
9911c11e9c Update FUNDING.yml 2023-03-05 22:47:17 +08:00
Kevin Wan
0d5a68869d fix: gateway conf doesn't work (#2968) 2023-03-05 22:19:58 +08:00
Kevin Wan
d9d79e930d Merge pull request from GHSA-fgxv-gw55-r5fq
* fix: Authorization Bypass Through User-Controlled Key

* chore: add not safe domain test
2023-03-04 23:34:11 +08:00
Kevin Wan
d953675085 chore: add tests (#2960) 2023-03-04 20:34:15 +08:00
yangjinheng
dbc8f9faca timeout writer add hijack 2023-03-04 11:50:04 +00:00
yangjinheng
96998ae570 Update timeouthandler.go 2023-03-04 11:50:04 +00:00
MarkJoyMa
7086fb6dda x 2023-03-04 11:44:47 +00:00
MarkJoyMa
1ad7809fde x 2023-03-04 11:44:47 +00:00
MarkJoyMa
142c46228b x 2023-03-04 11:44:47 +00:00
MarkJoyMa
ba771f8ff1 add ut 2023-03-04 11:44:47 +00:00
MarkJoyMa
f3cf891d4f feat: conf add FillDefault func 2023-03-04 11:44:47 +00:00
zhoumingji
ba71964b16 Fix bug in dartgen: The property 'isEmpty' can't be unconditionally accessed because the receiver can be 'null' 2023-03-04 11:37:38 +00:00
zhoumingji
97ada59175 Fix bug in dartgen: Increase the processing logic when route.RequestType is empty 2023-03-04 04:31:27 +00:00
cui fliter
b41ccc5992 fix functiom name
Signed-off-by: cui fliter <imcusg@gmail.com>
2023-03-04 04:29:19 +00:00
dependabot[bot]
e23f421976 chore(deps): bump k8s.io/client-go from 0.26.1 to 0.26.2 (#2955)
Bumps [k8s.io/client-go](https://github.com/kubernetes/client-go) from 0.26.1 to 0.26.2.
- [Release notes](https://github.com/kubernetes/client-go/releases)
- [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md)
- [Commits](https://github.com/kubernetes/client-go/compare/v0.26.1...v0.26.2)

---
updated-dependencies:
- dependency-name: k8s.io/client-go
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-04 12:28:25 +08:00
Kevin Wan
dc5b8dd716 feat: support grpc client keepalive config (#2950) 2023-03-03 23:40:17 +08:00
tanglihao
a40d8b0684 fix code format style use const config.DefaultFormat 2023-03-03 14:58:18 +00:00
tanglihao
cb39b5836f fix log name conflict 2023-03-03 14:58:18 +00:00
dependabot[bot]
4988f2a4da chore(deps): bump k8s.io/apimachinery from 0.26.1 to 0.26.2 (#2954)
Bumps [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) from 0.26.1 to 0.26.2.
- [Release notes](https://github.com/kubernetes/apimachinery/releases)
- [Commits](https://github.com/kubernetes/apimachinery/compare/v0.26.1...v0.26.2)

---
updated-dependencies:
- dependency-name: k8s.io/apimachinery
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-03 22:56:07 +08:00
anqiansong
7ca89a85ab format code 2023-03-03 07:11:13 +00:00
anqiansong
1ac2384750 remove unused code 2023-03-03 07:11:13 +00:00
anqiansong
0e040ec5b4 remove unused code 2023-03-03 07:11:13 +00:00
anqiansong
4bc1b78a91 Fix #2879 2023-03-03 07:11:13 +00:00
qiying.wang
148afcf1a7 chore: remove redundant prefix of "error: " in error creation 2023-03-02 12:46:19 +00:00
Kevin Wan
1cd1b17f70 fix: security #9 (#2949)
* fix: security #9

* fix: missing packages

* chore: update deps
2023-03-02 16:31:39 +08:00
dependabot[bot]
59c110688d chore(deps): bump github.com/pelletier/go-toml/v2 from 2.0.6 to 2.0.7 (#2943)
Bumps [github.com/pelletier/go-toml/v2](https://github.com/pelletier/go-toml) from 2.0.6 to 2.0.7.
- [Release notes](https://github.com/pelletier/go-toml/releases)
- [Changelog](https://github.com/pelletier/go-toml/blob/v2/.goreleaser.yaml)
- [Commits](https://github.com/pelletier/go-toml/compare/v2.0.6...v2.0.7)

---
updated-dependencies:
- dependency-name: github.com/pelletier/go-toml/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-02 14:22:00 +08:00
dependabot[bot]
6a25323467 chore(deps): bump go.opentelemetry.io/otel/exporters/jaeger (#2942)
Bumps [go.opentelemetry.io/otel/exporters/jaeger](https://github.com/open-telemetry/opentelemetry-go) from 1.13.0 to 1.14.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.13.0...v1.14.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/jaeger
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-02 13:51:17 +08:00
dependabot[bot]
6aeb3dfb1c chore(deps): bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp (#2941)
Bumps [go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp](https://github.com/open-telemetry/opentelemetry-go) from 1.13.0 to 1.14.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.13.0...v1.14.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-02 12:55:25 +08:00
qiying.wang
0cb61b9a9c chore: add tests for logc debug 2023-03-02 04:38:54 +00:00
qiying.wang
10d263395c feat: add debug log for logc 2023-03-02 04:38:54 +00:00
kevin
d65801f258 chore: add comments 2023-03-02 04:32:24 +00:00
kevin
eaac0ba8de chore: add more tests 2023-03-02 04:32:24 +00:00
kevin
b449f2f39e chore: add more tests 2023-03-02 04:32:24 +00:00
kevin
c57b0b8f90 feat: check key overwritten 2023-03-02 04:32:24 +00:00
dependabot[bot]
696406b887 chore(deps): bump go.opentelemetry.io/otel/exporters/zipkin (#2940)
Bumps [go.opentelemetry.io/otel/exporters/zipkin](https://github.com/open-telemetry/opentelemetry-go) from 1.13.0 to 1.14.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.13.0...v1.14.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/zipkin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-02 12:26:58 +08:00
dependabot[bot]
cc1779936e chore(deps): bump github.com/jackc/pgx/v5 from 5.2.0 to 5.3.1 (#2939)
Bumps [github.com/jackc/pgx/v5](https://github.com/jackc/pgx) from 5.2.0 to 5.3.1.
- [Release notes](https://github.com/jackc/pgx/releases)
- [Changelog](https://github.com/jackc/pgx/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jackc/pgx/compare/v5.2.0...v5.3.1)

---
updated-dependencies:
- dependency-name: github.com/jackc/pgx/v5
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-02 12:11:53 +08:00
Kevin Wan
de4924a274 fix: config map cannot handle case-insensitive keys. (#2932)
* fix: #2922

* chore: rename const

* feat: support anonymous map field

* feat: support anonymous map field
2023-02-28 23:32:08 +08:00
kevin
2b08e0510c chore: go mod tidy and update deps 2023-02-28 04:38:56 +00:00
kevin
afac48a8ea chore: go mod tidy and update deps 2023-02-28 04:38:56 +00:00
dependabot[bot]
a50b604dc9 chore(deps): bump github.com/jhump/protoreflect from 1.14.1 to 1.15.0
Bumps [github.com/jhump/protoreflect](https://github.com/jhump/protoreflect) from 1.14.1 to 1.15.0.
- [Release notes](https://github.com/jhump/protoreflect/releases)
- [Commits](https://github.com/jhump/protoreflect/compare/v1.14.1...v1.15.0)

---
updated-dependencies:
- dependency-name: github.com/jhump/protoreflect
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-28 03:19:53 +00:00
dependabot[bot]
eda44b6ae8 chore(deps): bump github.com/stretchr/testify from 1.8.1 to 1.8.2
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.1 to 1.8.2.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.1...v1.8.2)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-28 03:19:04 +00:00
Kevin Wan
284331b7b1 fix: #2899, using autoscaling/v2beta2 instead of v2beta1 (#2900)
* fix: #2899, using autoscaling/v2 instead of v2beta1

* chore: change hpa definition

---------

Co-authored-by: kevin.wan <kevin.wan@yijinin.com>
2023-02-26 22:32:27 +08:00
Kevin Wan
66be213346 chore: refine rest validator (#2928)
* chore: refine rest validator

* chore: add more tests

* chore: reformat code

* chore: add comments
2023-02-26 21:58:58 +08:00
Qiying Wang
92c8899f47 feat: add configurable validator for httpx.Parse (#2923)
Co-authored-by: qiying.wang <qiying.wang@highlight.mobi>
2023-02-26 20:40:22 +08:00
Kevin Wan
238c830f17 fix: timeout not working if greater than global rest timeout (#2926) 2023-02-26 20:22:20 +08:00
raymonder jin
ace125f189 del unnecessary blank 2023-02-26 04:59:23 +00:00
Kevin Wan
a5e5f04bcf chore: reformat code (#2925) 2023-02-26 09:35:59 +08:00
chenquan
3bc40d9eaf fix: fixed the bug that old trace instances may be fetched 2023-02-22 13:01:22 +00:00
yiwu
133c40ac1c Pgx (#2902)
* change postgres driver  to  pgx

* modified:   go.mod
	modified:   go.sum

* chore: tidy go.sum

---------

Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2023-02-22 14:11:49 +08:00
xiandong
eaaf87cdeb rm parseErr when kindJaeger 2023-02-22 05:41:07 +00:00
xiandong
6dbcfb5e5d rm kindJaegerUdp 2023-02-22 05:41:07 +00:00
xiandong
16a5f30b0c add parseEndpoint 2023-02-22 05:41:07 +00:00
xiandong
4e6d800877 add parseEndpoint 2023-02-22 05:41:07 +00:00
xiandong
af19addf47 add test for Endpoint of kindJaegerUdp 2023-02-22 05:41:07 +00:00
xiandong
ebc425b797 add test for Endpoint of kindJaegerUdp 2023-02-22 05:41:07 +00:00
xiandong
b6bedcd522 add kindJaegerUdp 2023-02-22 05:41:07 +00:00
xiandong
12060c9c0c opentelemetry support AgentHost, AgentPort 2023-02-22 05:41:07 +00:00
xiandong
e575bf8317 add jaeger.WithAgentEndpoint 2023-02-22 05:41:07 +00:00
strong
0fe84b225c fix(goctl): unsupported database type: bpchar (#2840) 2023-02-21 19:40:29 +08:00
dependabot[bot]
33af0745a0 chore(deps): bump golang.org/x/net from 0.5.0 to 0.7.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.5.0 to 0.7.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.5.0...v0.7.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-21 06:19:21 +00:00
dependabot[bot]
1d0265a77e chore(deps): bump github.com/golang-jwt/jwt/v4 from 4.4.3 to 4.5.0 (#2904)
Bumps [github.com/golang-jwt/jwt/v4](https://github.com/golang-jwt/jwt) from 4.4.3 to 4.5.0.
- [Release notes](https://github.com/golang-jwt/jwt/releases)
- [Changelog](https://github.com/golang-jwt/jwt/blob/main/VERSION_HISTORY.md)
- [Commits](https://github.com/golang-jwt/jwt/compare/v4.4.3...v4.5.0)

---
updated-dependencies:
- dependency-name: github.com/golang-jwt/jwt/v4
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-21 14:16:47 +08:00
Kevin Wan
03fe036204 chore: reformat code (#2903) 2023-02-20 23:37:05 +08:00
anqiansong
03d073a884 feat: Add request.ts (#2901)
* Add request.ts

* Update comments

* Refactor request filename
2023-02-20 22:54:25 +08:00
cong
64ab00e8e3 refactor: simplify sqlx fail fast ping and simplify miniredis setup in test (#2897)
* chore(redistest): simplify miniredis setup in test

* refactor(sqlx): simplify sqlx fail fast ping

* chore: close connection if not available
2023-02-19 17:18:19 +08:00
Kevin Wan
d113e1352c Update readme-cn.md 2023-02-17 14:48:41 +08:00
fondoger
32e3116ee3 Fix bug in dart api gen: path parameter is not replaced 2023-02-16 15:24:51 +00:00
Kevin Wan
1dd18e2329 Update readme-cn.md 2023-02-16 21:51:17 +08:00
dependabot[bot]
44b2389f9c chore(deps): bump go.uber.org/goleak from 1.2.0 to 1.2.1 (#2883)
Bumps [go.uber.org/goleak](https://github.com/uber-go/goleak) from 1.2.0 to 1.2.1.
- [Release notes](https://github.com/uber-go/goleak/releases)
- [Changelog](https://github.com/uber-go/goleak/blob/master/CHANGELOG.md)
- [Commits](https://github.com/uber-go/goleak/compare/v1.2.0...v1.2.1)

---
updated-dependencies:
- dependency-name: go.uber.org/goleak
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-16 21:18:00 +08:00
Kevin Wan
8bc34c58f4 fix: test failures (#2892)
Co-authored-by: kevin.wan <kevin.wan@yijinin.com>
2023-02-16 19:45:34 +08:00
fondoger
5756627904 Fix Dart API generation bugs; Add ability to generate API for path parameters (#2887)
* Fix bug in dartgen: Import path should match the generated api filename

* Use Route.HandlerName as generated dart API function name

Reasons:
- There is bug when using url path name as function name, because it may have invalid characters such as ":"
- Switching to HandlerName aligns with other languages such as typescript generation

* [DartGen] Add ability to generate api for url path parameters such as /path/:param
2023-02-16 15:19:46 +08:00
Kevin Wan
cddf3875cf refactor: simplify stringx.Replacer, and avoid potential infinite loops (#2877)
* simplify replace

* backup

* refactor: simplify stringx.Replacer

* chore: add comments and const

* chore: add more tests

* chore: rename variable
2023-02-14 10:18:02 +08:00
dependabot[bot]
9be17a2d28 chore(deps): bump go.mongodb.org/mongo-driver from 1.11.1 to 1.11.2
Bumps [go.mongodb.org/mongo-driver](https://github.com/mongodb/mongo-go-driver) from 1.11.1 to 1.11.2.
- [Release notes](https://github.com/mongodb/mongo-go-driver/releases)
- [Commits](https://github.com/mongodb/mongo-go-driver/compare/v1.11.1...v1.11.2)

---
updated-dependencies:
- dependency-name: go.mongodb.org/mongo-driver
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-13 23:39:41 +00:00
Kevin Wan
b8a86e2135 Update readme-cn.md 2023-02-13 22:03:22 +08:00
Kevin Wan
072db116c3 chore: refactor (#2875) 2023-02-12 22:20:36 +08:00
dahaihu
cacd5dc91a fix Replacer suffix match, and add test case (#2867)
* fix: replace shoud replace the longest match

* feat: revert bytes.Buffer to strings.Builder

* fix: loop reset nextStart

* feat: add node longest match test

* feat: add replacer suffix match test case

* feat: multiple match

* fix: partial match ends

* fix: replace look back upon error

* feat: rm unnecessary branch

---------

Co-authored-by: hudahai <hscxrzs@gmail.com>
Co-authored-by: hushichang <hushichang@sensetime.com>
2023-02-12 21:04:35 +08:00
Kevin Wan
3736dacf1e chore: add more tests (#2873) 2023-02-12 20:32:56 +08:00
Kevin Wan
434973c206 fix: test failure (#2874) 2023-02-12 20:08:02 +08:00
Mikael
84f9863b63 only unmashal public variables (#2872)
* only unmashal public variables

* only unmashal public variables

* only unmashal public variables

* only unmashal public variables
2023-02-12 19:53:20 +08:00
Kevin Wan
99a7e6600d feat: use dependabot for goctl (#2869)
* feat: use dependabot for goctl

* chore: add more tests

* chore: remove temp file
2023-02-11 16:32:28 +08:00
chen quan
ea7dab3d26 feat(sqlx): error log print traceId and spanId (#2845) 2023-02-11 16:12:14 +08:00
Kevin Wan
d7d6eccce6 chore: remove clickhouse, added to zero-contrib (#2848) 2023-02-11 15:28:59 +08:00
Kevin Wan
0a5a26385d chore: add more tests (#2866)
* chore: add more tests

* chore: add more tests

* chore: fix test failure
2023-02-11 14:21:39 +08:00
hudahai
62e59837c6 fix: loop reset nextStart 2023-02-10 04:26:50 +00:00
hudahai
981d7dab13 feat: revert bytes.Buffer to strings.Builder 2023-02-10 04:26:50 +00:00
hudahai
d9a732a273 fix: replace shoud replace the longest match 2023-02-10 04:26:50 +00:00
dependabot[bot]
b6f1bce695 chore(deps): bump go.opentelemetry.io/otel/exporters/jaeger (#2858)
Bumps [go.opentelemetry.io/otel/exporters/jaeger](https://github.com/open-telemetry/opentelemetry-go) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.12.0...v1.13.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/jaeger
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-10 10:41:35 +08:00
dependabot[bot]
0988c4148f chore(deps): bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
Bumps [go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp](https://github.com/open-telemetry/opentelemetry-go) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.12.0...v1.13.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-10 01:49:56 +00:00
dependabot[bot]
165133b91b chore(deps): bump go.opentelemetry.io/otel/exporters/zipkin (#2857)
Bumps [go.opentelemetry.io/otel/exporters/zipkin](https://github.com/open-telemetry/opentelemetry-go) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.12.0...v1.13.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/zipkin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-09 22:23:04 +08:00
dependabot[bot]
cd8081c567 chore(deps): bump go.opentelemetry.io/otel/trace from 1.12.0 to 1.13.0 (#2860)
Bumps [go.opentelemetry.io/otel/trace](https://github.com/open-telemetry/opentelemetry-go) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.12.0...v1.13.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/trace
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-09 20:23:04 +08:00
dependabot[bot]
e40a089086 chore(deps): bump google.golang.org/grpc from 1.52.3 to 1.53.0 (#2856)
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.52.3 to 1.53.0.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.52.3...v1.53.0)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-09 13:38:23 +08:00
Kevin Wan
d9780fb2a6 Update readme-cn.md 2023-02-08 12:49:33 +08:00
dependabot[bot]
2c8ae994cf chore(deps): bump golang.org/x/sys from 0.4.0 to 0.5.0 (#2854)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.4.0 to 0.5.0.
- [Release notes](https://github.com/golang/sys/releases)
- [Commits](https://github.com/golang/sys/compare/v0.4.0...v0.5.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-08 12:38:11 +08:00
dependabot[bot]
67a046b554 chore(deps): bump github.com/ClickHouse/clickhouse-go/v2 (#2838)
Bumps [github.com/ClickHouse/clickhouse-go/v2](https://github.com/ClickHouse/clickhouse-go) from 2.5.1 to 2.6.0.
- [Release notes](https://github.com/ClickHouse/clickhouse-go/releases)
- [Changelog](https://github.com/ClickHouse/clickhouse-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ClickHouse/clickhouse-go/compare/v2.5.1...2.6.0)

---
updated-dependencies:
- dependency-name: github.com/ClickHouse/clickhouse-go/v2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-05 14:55:26 +08:00
Kevin Wan
a019a1f59f fix: conf anonymous overlay problem (#2847) 2023-02-05 14:40:57 +08:00
Kevin Wan
aed312f3c0 Update readme-cn.md 2023-01-31 23:12:17 +08:00
Kevin Wan
58138fd56c Chore/rewire (#2836)
* fix: problem on name overlaping in config (#2820)

* chore: fix missing funcs on windows (#2825)

* chore: add more tests (#2812)

* chore: add more tests

* chore: add more tests

* chore: add more tests (#2814)

* chore: add more tests (#2815)

* chore: add more tests

* chore: add more tests

* chore: add more tests

* chore: add more tests

* chore: add more tests

* chore: add more tests

* feat: upgrade go to v1.18 (#2817)

* feat: upgrade go to v1.18

* feat: upgrade go to v1.18

* chore: change interface{} to any (#2818)

* chore: change interface{} to any

* chore: update goctl version to 1.5.0

* chore: update goctl deps

* chore: update goctl interface{} to any (#2819)

* chore: update goctl interface{} to any

* chore: update goctl interface{} to any

* chore(deps): bump google.golang.org/grpc from 1.52.0 to 1.52.3 (#2823)

* support custom maxBytes in API file (#2822)

* feat: mapreduce generic version (#2827)

* feat: mapreduce generic version

* fix: gateway mr type issue

---------

Co-authored-by: kevin.wan <kevin.wan@yijinin.com>

* feat: add MustNewRedis (#2824)

* feat: add MustNewRedis

* feat: add MustNewRedis

* feat: add MustNewRedis

* x

* x

* fix ut

* x

* x

* x

* x

* x

* chore: improve codecov (#2828)

* feat: converge grpc interceptor processing (#2830)

* feat: converge grpc interceptor processing

* x

* x

* chore(deps): bump go.opentelemetry.io/otel/exporters/zipkin (#2831)

* chore(deps): bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp (#2833)

Bumps [go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp](https://github.com/open-telemetry/opentelemetry-go) from 1.11.2 to 1.12.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.11.2...v1.12.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump go.opentelemetry.io/otel/exporters/jaeger (#2832)

Bumps [go.opentelemetry.io/otel/exporters/jaeger](https://github.com/open-telemetry/opentelemetry-go) from 1.11.2 to 1.12.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.11.2...v1.12.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/jaeger
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Xiaoju Jiang <44432198+jiang4869@users.noreply.github.com>
Co-authored-by: kevin.wan <kevin.wan@yijinin.com>
Co-authored-by: MarkJoyMa <64180138+MarkJoyMa@users.noreply.github.com>
2023-01-31 13:58:22 +08:00
dependabot[bot]
f2588b238f chore(deps): bump go.opentelemetry.io/otel/exporters/jaeger (#2832)
Bumps [go.opentelemetry.io/otel/exporters/jaeger](https://github.com/open-telemetry/opentelemetry-go) from 1.11.2 to 1.12.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.11.2...v1.12.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/jaeger
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-31 11:04:39 +08:00
dependabot[bot]
cc5ae722a2 chore(deps): bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp (#2833)
Bumps [go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp](https://github.com/open-telemetry/opentelemetry-go) from 1.11.2 to 1.12.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.11.2...v1.12.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-31 09:21:08 +08:00
dependabot[bot]
1ee61709d9 chore(deps): bump go.opentelemetry.io/otel/exporters/zipkin (#2831) 2023-01-31 09:06:24 +08:00
MarkJoyMa
dd117ce9cf feat: converge grpc interceptor processing (#2830)
* feat: converge grpc interceptor processing

* x

* x
2023-01-30 15:48:50 +08:00
Kevin Wan
3c0dc8435e chore: improve codecov (#2828) 2023-01-29 21:39:54 +08:00
MarkJoyMa
fde05ccb28 feat: add MustNewRedis (#2824)
* feat: add MustNewRedis

* feat: add MustNewRedis

* feat: add MustNewRedis

* x

* x

* fix ut

* x

* x

* x

* x

* x
2023-01-29 18:03:05 +08:00
Kevin Wan
464ed51728 feat: mapreduce generic version (#2827)
* feat: mapreduce generic version

* fix: gateway mr type issue

---------

Co-authored-by: kevin.wan <kevin.wan@yijinin.com>
2023-01-29 18:01:23 +08:00
Xiaoju Jiang
413ee919e6 support custom maxBytes in API file (#2822) 2023-01-29 10:43:00 +08:00
Kevin Wan
35b9568657 chore: fix missing funcs on windows (#2825) 2023-01-28 22:45:36 +08:00
dependabot[bot]
167d76b46d chore(deps): bump google.golang.org/grpc from 1.52.0 to 1.52.3 (#2823) 2023-01-28 16:48:30 +08:00
Kevin Wan
ab9eeff500 fix: problem on name overlaping in config (#2820) 2023-01-24 21:14:48 +08:00
Kevin Wan
eab904af64 chore: update goctl interface{} to any (#2819)
* chore: update goctl interface{} to any

* chore: update goctl interface{} to any
2023-01-24 17:51:03 +08:00
Kevin Wan
ae87114282 chore: change interface{} to any (#2818)
* chore: change interface{} to any

* chore: update goctl version to 1.5.0

* chore: update goctl deps
2023-01-24 16:32:02 +08:00
Kevin Wan
7e0ac77139 feat: upgrade go to v1.18 (#2817)
* feat: upgrade go to v1.18

* feat: upgrade go to v1.18
2023-01-24 15:47:01 +08:00
Kevin Wan
696da4efee chore: add more tests (#2815)
* chore: add more tests

* chore: add more tests

* chore: add more tests

* chore: add more tests

* chore: add more tests

* chore: add more tests
2023-01-24 13:43:13 +08:00
Kevin Wan
ceab564429 chore: add more tests (#2814) 2023-01-24 12:03:05 +08:00
Kevin Wan
4bd8025c5b chore: add more tests (#2812)
* chore: add more tests

* chore: add more tests
2023-01-23 23:27:47 +08:00
476 changed files with 20832 additions and 5092 deletions

View File

@@ -3,4 +3,6 @@ comment:
behavior: once behavior: once
require_changes: true require_changes: true
ignore: ignore:
- "tools" - "tools"
- "**/mock"
- "**/*_mock.go"

2
.github/FUNDING.yml vendored
View File

@@ -10,4 +10,4 @@ liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username otechie: # Replace with a single Otechie username
custom: # https://gitee.com/kevwan/static/raw/master/images/sponsor.jpg custom: # https://gitee.com/kevwan/static/raw/master/images/sponsor.jpg
ethereum: 0x5052b7f6B937B02563996D23feb69b38D06Ca150 | kevwan ethereum: # 0x5052b7f6B937B02563996D23feb69b38D06Ca150 | kevwan

View File

@@ -9,3 +9,7 @@ updates:
directory: "/" # Location of package manifests directory: "/" # Location of package manifests
schedule: schedule:
interval: "daily" interval: "daily"
- package-ecosystem: "gomod" # See documentation for possible values
directory: "/tools/goctl" # Location of package manifests
schedule:
interval: "daily"

View File

@@ -17,7 +17,7 @@ jobs:
- name: Set up Go 1.x - name: Set up Go 1.x
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
go-version: ^1.16 go-version: 1.18
check-latest: true check-latest: true
cache: true cache: true
id: go id: go
@@ -29,8 +29,12 @@ jobs:
- name: Lint - name: Lint
run: | run: |
go vet -stdmethods=false $(go list ./...) go vet -stdmethods=false $(go list ./...)
go install mvdan.cc/gofumpt@latest
test -z "$(gofumpt -l -extra .)" || echo "Please run 'gofumpt -l -w -extra .'" go mod tidy
if ! test -z "$(git status --porcelain)"; then
echo "Please run 'go mod tidy'"
exit 1
fi
- name: Test - name: Test
run: go test -race -coverprofile=coverage.txt -covermode=atomic ./... run: go test -race -coverprofile=coverage.txt -covermode=atomic ./...
@@ -48,8 +52,8 @@ jobs:
- name: Set up Go 1.x - name: Set up Go 1.x
uses: actions/setup-go@v3 uses: actions/setup-go@v3
with: with:
# use 1.16 to guarantee Go 1.16 compatibility # use 1.18 to guarantee Go 1.18 compatibility
go-version: 1.16 go-version: 1.18
check-latest: true check-latest: true
cache: true cache: true

View File

@@ -22,7 +22,7 @@ jobs:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
goos: ${{ matrix.goos }} goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }} goarch: ${{ matrix.goarch }}
goversion: "https://dl.google.com/go/go1.17.5.linux-amd64.tar.gz" goversion: "https://dl.google.com/go/go1.18.10.linux-amd64.tar.gz"
project_path: "tools/goctl" project_path: "tools/goctl"
binary_name: "goctl" binary_name: "goctl"
extra_files: tools/goctl/readme.md tools/goctl/readme-cn.md extra_files: tools/goctl/readme.md tools/goctl/readme-cn.md

2
.gitignore vendored
View File

@@ -11,7 +11,7 @@
!api !api
# ignore # ignore
.idea **/.idea
**/.DS_Store **/.DS_Store
**/logs **/logs

View File

@@ -1,6 +1,7 @@
package bloom package bloom
import ( import (
"context"
"errors" "errors"
"strconv" "strconv"
@@ -8,28 +9,29 @@ import (
"github.com/zeromicro/go-zero/core/stores/redis" "github.com/zeromicro/go-zero/core/stores/redis"
) )
const ( // for detailed error rate table, see http://pages.cs.wisc.edu/~cao/papers/summary-cache/node8.html
// for detailed error rate table, see http://pages.cs.wisc.edu/~cao/papers/summary-cache/node8.html // maps as k in the error rate table
// maps as k in the error rate table const maps = 14
maps = 14
setScript = ` var (
// ErrTooLargeOffset indicates the offset is too large in bitset.
ErrTooLargeOffset = errors.New("too large offset")
setScript = redis.NewScript(`
for _, offset in ipairs(ARGV) do for _, offset in ipairs(ARGV) do
redis.call("setbit", KEYS[1], offset, 1) redis.call("setbit", KEYS[1], offset, 1)
end end
` `)
testScript = ` testScript = redis.NewScript(`
for _, offset in ipairs(ARGV) do for _, offset in ipairs(ARGV) do
if tonumber(redis.call("getbit", KEYS[1], offset)) == 0 then if tonumber(redis.call("getbit", KEYS[1], offset)) == 0 then
return false return false
end end
end end
return true return true
` `)
) )
// ErrTooLargeOffset indicates the offset is too large in bitset.
var ErrTooLargeOffset = errors.New("too large offset")
type ( type (
// A Filter is a bloom filter. // A Filter is a bloom filter.
Filter struct { Filter struct {
@@ -38,8 +40,8 @@ type (
} }
bitSetProvider interface { bitSetProvider interface {
check([]uint) (bool, error) check(ctx context.Context, offsets []uint) (bool, error)
set([]uint) error set(ctx context.Context, offsets []uint) error
} }
) )
@@ -58,14 +60,24 @@ func New(store *redis.Redis, key string, bits uint) *Filter {
// Add adds data into f. // Add adds data into f.
func (f *Filter) Add(data []byte) error { func (f *Filter) Add(data []byte) error {
return f.AddCtx(context.Background(), data)
}
// AddCtx adds data into f with context.
func (f *Filter) AddCtx(ctx context.Context, data []byte) error {
locations := f.getLocations(data) locations := f.getLocations(data)
return f.bitSet.set(locations) return f.bitSet.set(ctx, locations)
} }
// Exists checks if data is in f. // Exists checks if data is in f.
func (f *Filter) Exists(data []byte) (bool, error) { func (f *Filter) Exists(data []byte) (bool, error) {
return f.ExistsCtx(context.Background(), data)
}
// ExistsCtx checks if data is in f with context.
func (f *Filter) ExistsCtx(ctx context.Context, data []byte) (bool, error) {
locations := f.getLocations(data) locations := f.getLocations(data)
isSet, err := f.bitSet.check(locations) isSet, err := f.bitSet.check(ctx, locations)
if err != nil { if err != nil {
return false, err return false, err
} }
@@ -111,13 +123,13 @@ func (r *redisBitSet) buildOffsetArgs(offsets []uint) ([]string, error) {
return args, nil return args, nil
} }
func (r *redisBitSet) check(offsets []uint) (bool, error) { func (r *redisBitSet) check(ctx context.Context, offsets []uint) (bool, error) {
args, err := r.buildOffsetArgs(offsets) args, err := r.buildOffsetArgs(offsets)
if err != nil { if err != nil {
return false, err return false, err
} }
resp, err := r.store.Eval(testScript, []string{r.key}, args) resp, err := r.store.ScriptRunCtx(ctx, testScript, []string{r.key}, args)
if err == redis.Nil { if err == redis.Nil {
return false, nil return false, nil
} else if err != nil { } else if err != nil {
@@ -132,22 +144,24 @@ func (r *redisBitSet) check(offsets []uint) (bool, error) {
return exists == 1, nil return exists == 1, nil
} }
// del only use for testing.
func (r *redisBitSet) del() error { func (r *redisBitSet) del() error {
_, err := r.store.Del(r.key) _, err := r.store.Del(r.key)
return err return err
} }
// expire only use for testing.
func (r *redisBitSet) expire(seconds int) error { func (r *redisBitSet) expire(seconds int) error {
return r.store.Expire(r.key, seconds) return r.store.Expire(r.key, seconds)
} }
func (r *redisBitSet) set(offsets []uint) error { func (r *redisBitSet) set(ctx context.Context, offsets []uint) error {
args, err := r.buildOffsetArgs(offsets) args, err := r.buildOffsetArgs(offsets)
if err != nil { if err != nil {
return err return err
} }
_, err = r.store.Eval(setScript, []string{r.key}, args) _, err = r.store.ScriptRunCtx(ctx, setScript, []string{r.key}, args)
if err == redis.Nil { if err == redis.Nil {
return nil return nil
} }

View File

@@ -1,30 +1,31 @@
package bloom package bloom
import ( import (
"context"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/redis/redistest" "github.com/zeromicro/go-zero/core/stores/redis/redistest"
) )
func TestRedisBitSet_New_Set_Test(t *testing.T) { func TestRedisBitSet_New_Set_Test(t *testing.T) {
store, clean, err := redistest.CreateRedis() store := redistest.CreateRedis(t)
assert.Nil(t, err) ctx := context.Background()
defer clean()
bitSet := newRedisBitSet(store, "test_key", 1024) bitSet := newRedisBitSet(store, "test_key", 1024)
isSetBefore, err := bitSet.check([]uint{0}) isSetBefore, err := bitSet.check(ctx, []uint{0})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if isSetBefore { if isSetBefore {
t.Fatal("Bit should not be set") t.Fatal("Bit should not be set")
} }
err = bitSet.set([]uint{512}) err = bitSet.set(ctx, []uint{512})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
isSetAfter, err := bitSet.check([]uint{512}) isSetAfter, err := bitSet.check(ctx, []uint{512})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -42,9 +43,7 @@ func TestRedisBitSet_New_Set_Test(t *testing.T) {
} }
func TestRedisBitSet_Add(t *testing.T) { func TestRedisBitSet_Add(t *testing.T) {
store, clean, err := redistest.CreateRedis() store := redistest.CreateRedis(t)
assert.Nil(t, err)
defer clean()
filter := New(store, "test_key", 64) filter := New(store, "test_key", 64)
assert.Nil(t, filter.Add([]byte("hello"))) assert.Nil(t, filter.Add([]byte("hello")))
@@ -53,3 +52,51 @@ func TestRedisBitSet_Add(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, ok) assert.True(t, ok)
} }
func TestFilter_Exists(t *testing.T) {
store, clean := redistest.CreateRedisWithClean(t)
rbs := New(store, "test", 64)
_, err := rbs.Exists([]byte{0, 1, 2})
assert.NoError(t, err)
clean()
rbs = New(store, "test", 64)
_, err = rbs.Exists([]byte{0, 1, 2})
assert.Error(t, err)
}
func TestRedisBitSet_check(t *testing.T) {
store, clean := redistest.CreateRedisWithClean(t)
ctx := context.Background()
rbs := newRedisBitSet(store, "test", 0)
assert.Error(t, rbs.set(ctx, []uint{0, 1, 2}))
_, err := rbs.check(ctx, []uint{0, 1, 2})
assert.Error(t, err)
rbs = newRedisBitSet(store, "test", 64)
_, err = rbs.check(ctx, []uint{0, 1, 2})
assert.NoError(t, err)
clean()
rbs = newRedisBitSet(store, "test", 64)
_, err = rbs.check(ctx, []uint{0, 1, 2})
assert.Error(t, err)
}
func TestRedisBitSet_set(t *testing.T) {
logx.Disable()
store, clean := redistest.CreateRedisWithClean(t)
ctx := context.Background()
rbs := newRedisBitSet(store, "test", 0)
assert.Error(t, rbs.set(ctx, []uint{0, 1, 2}))
rbs = newRedisBitSet(store, "test", 64)
assert.NoError(t, rbs.set(ctx, []uint{0, 1, 2}))
clean()
rbs = newRedisBitSet(store, "test", 64)
assert.Error(t, rbs.set(ctx, []uint{0, 1, 2}))
}

View File

@@ -2,6 +2,8 @@ package codec
import ( import (
"bytes" "bytes"
"compress/gzip"
"errors"
"fmt" "fmt"
"testing" "testing"
@@ -21,3 +23,45 @@ func TestGzip(t *testing.T) {
assert.True(t, len(bs) < buf.Len()) assert.True(t, len(bs) < buf.Len())
assert.Equal(t, buf.Bytes(), actual) assert.Equal(t, buf.Bytes(), actual)
} }
func TestGunzip(t *testing.T) {
tests := []struct {
name string
input []byte
expected []byte
expectedErr error
}{
{
name: "valid input",
input: func() []byte {
var buf bytes.Buffer
gz := gzip.NewWriter(&buf)
gz.Write([]byte("hello"))
gz.Close()
return buf.Bytes()
}(),
expected: []byte("hello"),
expectedErr: nil,
},
{
name: "invalid input",
input: []byte("invalid input"),
expected: nil,
expectedErr: gzip.ErrHeader,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
result, err := Gunzip(test.input)
if !bytes.Equal(result, test.expected) {
t.Errorf("unexpected result: %v", result)
}
if !errors.Is(err, test.expectedErr) {
t.Errorf("unexpected error: %v", err)
}
})
}
}

View File

@@ -2,6 +2,7 @@ package codec
import ( import (
"encoding/base64" "encoding/base64"
"os"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -41,6 +42,7 @@ func TestCryption(t *testing.T) {
file, err := fs.TempFilenameWithText(priKey) file, err := fs.TempFilenameWithText(priKey)
assert.Nil(t, err) assert.Nil(t, err)
defer os.Remove(file)
dec, err := NewRsaDecrypter(file) dec, err := NewRsaDecrypter(file)
assert.Nil(t, err) assert.Nil(t, err)
actual, err := dec.Decrypt(ret) actual, err := dec.Decrypt(ret)

View File

@@ -30,7 +30,7 @@ type (
Cache struct { Cache struct {
name string name string
lock sync.Mutex lock sync.Mutex
data map[string]interface{} data map[string]any
expire time.Duration expire time.Duration
timingWheel *TimingWheel timingWheel *TimingWheel
lruCache lru lruCache lru
@@ -43,7 +43,7 @@ type (
// NewCache returns a Cache with given expire. // NewCache returns a Cache with given expire.
func NewCache(expire time.Duration, opts ...CacheOption) (*Cache, error) { func NewCache(expire time.Duration, opts ...CacheOption) (*Cache, error) {
cache := &Cache{ cache := &Cache{
data: make(map[string]interface{}), data: make(map[string]any),
expire: expire, expire: expire,
lruCache: emptyLruCache, lruCache: emptyLruCache,
barrier: syncx.NewSingleFlight(), barrier: syncx.NewSingleFlight(),
@@ -59,7 +59,7 @@ func NewCache(expire time.Duration, opts ...CacheOption) (*Cache, error) {
} }
cache.stats = newCacheStat(cache.name, cache.size) cache.stats = newCacheStat(cache.name, cache.size)
timingWheel, err := NewTimingWheel(time.Second, slots, func(k, v interface{}) { timingWheel, err := NewTimingWheel(time.Second, slots, func(k, v any) {
key, ok := k.(string) key, ok := k.(string)
if !ok { if !ok {
return return
@@ -85,7 +85,7 @@ func (c *Cache) Del(key string) {
} }
// Get returns the item with the given key from c. // Get returns the item with the given key from c.
func (c *Cache) Get(key string) (interface{}, bool) { func (c *Cache) Get(key string) (any, bool) {
value, ok := c.doGet(key) value, ok := c.doGet(key)
if ok { if ok {
c.stats.IncrementHit() c.stats.IncrementHit()
@@ -97,12 +97,12 @@ func (c *Cache) Get(key string) (interface{}, bool) {
} }
// Set sets value into c with key. // Set sets value into c with key.
func (c *Cache) Set(key string, value interface{}) { func (c *Cache) Set(key string, value any) {
c.SetWithExpire(key, value, c.expire) c.SetWithExpire(key, value, c.expire)
} }
// SetWithExpire sets value into c with key and expire with the given value. // SetWithExpire sets value into c with key and expire with the given value.
func (c *Cache) SetWithExpire(key string, value interface{}, expire time.Duration) { func (c *Cache) SetWithExpire(key string, value any, expire time.Duration) {
c.lock.Lock() c.lock.Lock()
_, ok := c.data[key] _, ok := c.data[key]
c.data[key] = value c.data[key] = value
@@ -120,14 +120,14 @@ func (c *Cache) SetWithExpire(key string, value interface{}, expire time.Duratio
// Take returns the item with the given key. // Take returns the item with the given key.
// If the item is in c, return it directly. // If the item is in c, return it directly.
// If not, use fetch method to get the item, set into c and return it. // If not, use fetch method to get the item, set into c and return it.
func (c *Cache) Take(key string, fetch func() (interface{}, error)) (interface{}, error) { func (c *Cache) Take(key string, fetch func() (any, error)) (any, error) {
if val, ok := c.doGet(key); ok { if val, ok := c.doGet(key); ok {
c.stats.IncrementHit() c.stats.IncrementHit()
return val, nil return val, nil
} }
var fresh bool var fresh bool
val, err := c.barrier.Do(key, func() (interface{}, error) { val, err := c.barrier.Do(key, func() (any, error) {
// because O(1) on map search in memory, and fetch is an IO query // because O(1) on map search in memory, and fetch is an IO query
// so we do double check, cache might be taken by another call // so we do double check, cache might be taken by another call
if val, ok := c.doGet(key); ok { if val, ok := c.doGet(key); ok {
@@ -157,7 +157,7 @@ func (c *Cache) Take(key string, fetch func() (interface{}, error)) (interface{}
return val, nil return val, nil
} }
func (c *Cache) doGet(key string) (interface{}, bool) { func (c *Cache) doGet(key string) (any, bool) {
c.lock.Lock() c.lock.Lock()
defer c.lock.Unlock() defer c.lock.Unlock()

View File

@@ -52,7 +52,7 @@ func TestCacheTake(t *testing.T) {
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
wg.Add(1) wg.Add(1)
go func() { go func() {
cache.Take("first", func() (interface{}, error) { cache.Take("first", func() (any, error) {
atomic.AddInt32(&count, 1) atomic.AddInt32(&count, 1)
time.Sleep(time.Millisecond * 100) time.Sleep(time.Millisecond * 100)
return "first element", nil return "first element", nil
@@ -76,7 +76,7 @@ func TestCacheTakeExists(t *testing.T) {
wg.Add(1) wg.Add(1)
go func() { go func() {
cache.Set("first", "first element") cache.Set("first", "first element")
cache.Take("first", func() (interface{}, error) { cache.Take("first", func() (any, error) {
atomic.AddInt32(&count, 1) atomic.AddInt32(&count, 1)
time.Sleep(time.Millisecond * 100) time.Sleep(time.Millisecond * 100)
return "first element", nil return "first element", nil
@@ -99,7 +99,7 @@ func TestCacheTakeError(t *testing.T) {
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
wg.Add(1) wg.Add(1)
go func() { go func() {
_, err := cache.Take("first", func() (interface{}, error) { _, err := cache.Take("first", func() (any, error) {
atomic.AddInt32(&count, 1) atomic.AddInt32(&count, 1)
time.Sleep(time.Millisecond * 100) time.Sleep(time.Millisecond * 100)
return "", errDummy return "", errDummy

View File

@@ -5,7 +5,7 @@ import "sync"
// A Queue is a FIFO queue. // A Queue is a FIFO queue.
type Queue struct { type Queue struct {
lock sync.Mutex lock sync.Mutex
elements []interface{} elements []any
size int size int
head int head int
tail int tail int
@@ -15,7 +15,7 @@ type Queue struct {
// NewQueue returns a Queue object. // NewQueue returns a Queue object.
func NewQueue(size int) *Queue { func NewQueue(size int) *Queue {
return &Queue{ return &Queue{
elements: make([]interface{}, size), elements: make([]any, size),
size: size, size: size,
} }
} }
@@ -30,12 +30,12 @@ func (q *Queue) Empty() bool {
} }
// Put puts element into q at the last position. // Put puts element into q at the last position.
func (q *Queue) Put(element interface{}) { func (q *Queue) Put(element any) {
q.lock.Lock() q.lock.Lock()
defer q.lock.Unlock() defer q.lock.Unlock()
if q.head == q.tail && q.count > 0 { if q.head == q.tail && q.count > 0 {
nodes := make([]interface{}, len(q.elements)+q.size) nodes := make([]any, len(q.elements)+q.size)
copy(nodes, q.elements[q.head:]) copy(nodes, q.elements[q.head:])
copy(nodes[len(q.elements)-q.head:], q.elements[:q.head]) copy(nodes[len(q.elements)-q.head:], q.elements[:q.head])
q.head = 0 q.head = 0
@@ -49,7 +49,7 @@ func (q *Queue) Put(element interface{}) {
} }
// Take takes the first element out of q if not empty. // Take takes the first element out of q if not empty.
func (q *Queue) Take() (interface{}, bool) { func (q *Queue) Take() (any, bool) {
q.lock.Lock() q.lock.Lock()
defer q.lock.Unlock() defer q.lock.Unlock()

View File

@@ -4,7 +4,7 @@ import "sync"
// A Ring can be used as fixed size ring. // A Ring can be used as fixed size ring.
type Ring struct { type Ring struct {
elements []interface{} elements []any
index int index int
lock sync.RWMutex lock sync.RWMutex
} }
@@ -16,12 +16,12 @@ func NewRing(n int) *Ring {
} }
return &Ring{ return &Ring{
elements: make([]interface{}, n), elements: make([]any, n),
} }
} }
// Add adds v into r. // Add adds v into r.
func (r *Ring) Add(v interface{}) { func (r *Ring) Add(v any) {
r.lock.Lock() r.lock.Lock()
defer r.lock.Unlock() defer r.lock.Unlock()
@@ -30,7 +30,7 @@ func (r *Ring) Add(v interface{}) {
} }
// Take takes all items from r. // Take takes all items from r.
func (r *Ring) Take() []interface{} { func (r *Ring) Take() []any {
r.lock.RLock() r.lock.RLock()
defer r.lock.RUnlock() defer r.lock.RUnlock()
@@ -43,7 +43,7 @@ func (r *Ring) Take() []interface{} {
size = r.index size = r.index
} }
elements := make([]interface{}, size) elements := make([]any, size)
for i := 0; i < size; i++ { for i := 0; i < size; i++ {
elements[i] = r.elements[(start+i)%len(r.elements)] elements[i] = r.elements[(start+i)%len(r.elements)]
} }

View File

@@ -19,7 +19,7 @@ func TestRingLess(t *testing.T) {
ring.Add(i) ring.Add(i)
} }
elements := ring.Take() elements := ring.Take()
assert.ElementsMatch(t, []interface{}{0, 1, 2}, elements) assert.ElementsMatch(t, []any{0, 1, 2}, elements)
} }
func TestRingMore(t *testing.T) { func TestRingMore(t *testing.T) {
@@ -28,7 +28,7 @@ func TestRingMore(t *testing.T) {
ring.Add(i) ring.Add(i)
} }
elements := ring.Take() elements := ring.Take()
assert.ElementsMatch(t, []interface{}{6, 7, 8, 9, 10}, elements) assert.ElementsMatch(t, []any{6, 7, 8, 9, 10}, elements)
} }
func TestRingAdd(t *testing.T) { func TestRingAdd(t *testing.T) {

View File

@@ -14,20 +14,20 @@ type SafeMap struct {
lock sync.RWMutex lock sync.RWMutex
deletionOld int deletionOld int
deletionNew int deletionNew int
dirtyOld map[interface{}]interface{} dirtyOld map[any]any
dirtyNew map[interface{}]interface{} dirtyNew map[any]any
} }
// NewSafeMap returns a SafeMap. // NewSafeMap returns a SafeMap.
func NewSafeMap() *SafeMap { func NewSafeMap() *SafeMap {
return &SafeMap{ return &SafeMap{
dirtyOld: make(map[interface{}]interface{}), dirtyOld: make(map[any]any),
dirtyNew: make(map[interface{}]interface{}), dirtyNew: make(map[any]any),
} }
} }
// Del deletes the value with the given key from m. // Del deletes the value with the given key from m.
func (m *SafeMap) Del(key interface{}) { func (m *SafeMap) Del(key any) {
m.lock.Lock() m.lock.Lock()
if _, ok := m.dirtyOld[key]; ok { if _, ok := m.dirtyOld[key]; ok {
delete(m.dirtyOld, key) delete(m.dirtyOld, key)
@@ -42,21 +42,21 @@ func (m *SafeMap) Del(key interface{}) {
} }
m.dirtyOld = m.dirtyNew m.dirtyOld = m.dirtyNew
m.deletionOld = m.deletionNew m.deletionOld = m.deletionNew
m.dirtyNew = make(map[interface{}]interface{}) m.dirtyNew = make(map[any]any)
m.deletionNew = 0 m.deletionNew = 0
} }
if m.deletionNew >= maxDeletion && len(m.dirtyNew) < copyThreshold { if m.deletionNew >= maxDeletion && len(m.dirtyNew) < copyThreshold {
for k, v := range m.dirtyNew { for k, v := range m.dirtyNew {
m.dirtyOld[k] = v m.dirtyOld[k] = v
} }
m.dirtyNew = make(map[interface{}]interface{}) m.dirtyNew = make(map[any]any)
m.deletionNew = 0 m.deletionNew = 0
} }
m.lock.Unlock() m.lock.Unlock()
} }
// Get gets the value with the given key from m. // Get gets the value with the given key from m.
func (m *SafeMap) Get(key interface{}) (interface{}, bool) { func (m *SafeMap) Get(key any) (any, bool) {
m.lock.RLock() m.lock.RLock()
defer m.lock.RUnlock() defer m.lock.RUnlock()
@@ -70,7 +70,7 @@ func (m *SafeMap) Get(key interface{}) (interface{}, bool) {
// Range calls f sequentially for each key and value present in the map. // Range calls f sequentially for each key and value present in the map.
// If f returns false, range stops the iteration. // If f returns false, range stops the iteration.
func (m *SafeMap) Range(f func(key, val interface{}) bool) { func (m *SafeMap) Range(f func(key, val any) bool) {
m.lock.RLock() m.lock.RLock()
defer m.lock.RUnlock() defer m.lock.RUnlock()
@@ -87,7 +87,7 @@ func (m *SafeMap) Range(f func(key, val interface{}) bool) {
} }
// Set sets the value into m with the given key. // Set sets the value into m with the given key.
func (m *SafeMap) Set(key, value interface{}) { func (m *SafeMap) Set(key, value any) {
m.lock.Lock() m.lock.Lock()
if m.deletionOld <= maxDeletion { if m.deletionOld <= maxDeletion {
if _, ok := m.dirtyNew[key]; ok { if _, ok := m.dirtyNew[key]; ok {

View File

@@ -138,7 +138,7 @@ func TestSafeMap_Range(t *testing.T) {
} }
var count int32 var count int32
m.Range(func(k, v interface{}) bool { m.Range(func(k, v any) bool {
atomic.AddInt32(&count, 1) atomic.AddInt32(&count, 1)
newMap.Set(k, v) newMap.Set(k, v)
return true return true

View File

@@ -17,14 +17,14 @@ const (
// Set is not thread-safe, for concurrent use, make sure to use it with synchronization. // Set is not thread-safe, for concurrent use, make sure to use it with synchronization.
type Set struct { type Set struct {
data map[interface{}]lang.PlaceholderType data map[any]lang.PlaceholderType
tp int tp int
} }
// NewSet returns a managed Set, can only put the values with the same type. // NewSet returns a managed Set, can only put the values with the same type.
func NewSet() *Set { func NewSet() *Set {
return &Set{ return &Set{
data: make(map[interface{}]lang.PlaceholderType), data: make(map[any]lang.PlaceholderType),
tp: untyped, tp: untyped,
} }
} }
@@ -32,13 +32,13 @@ func NewSet() *Set {
// NewUnmanagedSet returns an unmanaged Set, which can put values with different types. // NewUnmanagedSet returns an unmanaged Set, which can put values with different types.
func NewUnmanagedSet() *Set { func NewUnmanagedSet() *Set {
return &Set{ return &Set{
data: make(map[interface{}]lang.PlaceholderType), data: make(map[any]lang.PlaceholderType),
tp: unmanaged, tp: unmanaged,
} }
} }
// Add adds i into s. // Add adds i into s.
func (s *Set) Add(i ...interface{}) { func (s *Set) Add(i ...any) {
for _, each := range i { for _, each := range i {
s.add(each) s.add(each)
} }
@@ -80,7 +80,7 @@ func (s *Set) AddStr(ss ...string) {
} }
// Contains checks if i is in s. // Contains checks if i is in s.
func (s *Set) Contains(i interface{}) bool { func (s *Set) Contains(i any) bool {
if len(s.data) == 0 { if len(s.data) == 0 {
return false return false
} }
@@ -91,8 +91,8 @@ func (s *Set) Contains(i interface{}) bool {
} }
// Keys returns the keys in s. // Keys returns the keys in s.
func (s *Set) Keys() []interface{} { func (s *Set) Keys() []any {
var keys []interface{} var keys []any
for key := range s.data { for key := range s.data {
keys = append(keys, key) keys = append(keys, key)
@@ -167,7 +167,7 @@ func (s *Set) KeysStr() []string {
} }
// Remove removes i from s. // Remove removes i from s.
func (s *Set) Remove(i interface{}) { func (s *Set) Remove(i any) {
s.validate(i) s.validate(i)
delete(s.data, i) delete(s.data, i)
} }
@@ -177,7 +177,7 @@ func (s *Set) Count() int {
return len(s.data) return len(s.data)
} }
func (s *Set) add(i interface{}) { func (s *Set) add(i any) {
switch s.tp { switch s.tp {
case unmanaged: case unmanaged:
// do nothing // do nothing
@@ -189,7 +189,7 @@ func (s *Set) add(i interface{}) {
s.data[i] = lang.Placeholder s.data[i] = lang.Placeholder
} }
func (s *Set) setType(i interface{}) { func (s *Set) setType(i any) {
// s.tp can only be untyped here // s.tp can only be untyped here
switch i.(type) { switch i.(type) {
case int: case int:
@@ -205,7 +205,7 @@ func (s *Set) setType(i interface{}) {
} }
} }
func (s *Set) validate(i interface{}) { func (s *Set) validate(i any) {
if s.tp == unmanaged { if s.tp == unmanaged {
return return
} }
@@ -213,23 +213,23 @@ func (s *Set) validate(i interface{}) {
switch i.(type) { switch i.(type) {
case int: case int:
if s.tp != intType { if s.tp != intType {
logx.Errorf("Error: element is int, but set contains elements with type %d", s.tp) logx.Errorf("element is int, but set contains elements with type %d", s.tp)
} }
case int64: case int64:
if s.tp != int64Type { if s.tp != int64Type {
logx.Errorf("Error: element is int64, but set contains elements with type %d", s.tp) logx.Errorf("element is int64, but set contains elements with type %d", s.tp)
} }
case uint: case uint:
if s.tp != uintType { if s.tp != uintType {
logx.Errorf("Error: element is uint, but set contains elements with type %d", s.tp) logx.Errorf("element is uint, but set contains elements with type %d", s.tp)
} }
case uint64: case uint64:
if s.tp != uint64Type { if s.tp != uint64Type {
logx.Errorf("Error: element is uint64, but set contains elements with type %d", s.tp) logx.Errorf("element is uint64, but set contains elements with type %d", s.tp)
} }
case string: case string:
if s.tp != stringType { if s.tp != stringType {
logx.Errorf("Error: element is string, but set contains elements with type %d", s.tp) logx.Errorf("element is string, but set contains elements with type %d", s.tp)
} }
} }
} }

View File

@@ -13,7 +13,7 @@ func init() {
} }
func BenchmarkRawSet(b *testing.B) { func BenchmarkRawSet(b *testing.B) {
m := make(map[interface{}]struct{}) m := make(map[any]struct{})
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
m[i] = struct{}{} m[i] = struct{}{}
_ = m[i] _ = m[i]
@@ -39,7 +39,7 @@ func BenchmarkSet(b *testing.B) {
func TestAdd(t *testing.T) { func TestAdd(t *testing.T) {
// given // given
set := NewUnmanagedSet() set := NewUnmanagedSet()
values := []interface{}{1, 2, 3} values := []any{1, 2, 3}
// when // when
set.Add(values...) set.Add(values...)
@@ -135,7 +135,7 @@ func TestContainsUnmanagedWithoutElements(t *testing.T) {
func TestRemove(t *testing.T) { func TestRemove(t *testing.T) {
// given // given
set := NewSet() set := NewSet()
set.Add([]interface{}{1, 2, 3}...) set.Add([]any{1, 2, 3}...)
// when // when
set.Remove(2) set.Remove(2)
@@ -147,7 +147,7 @@ func TestRemove(t *testing.T) {
func TestCount(t *testing.T) { func TestCount(t *testing.T) {
// given // given
set := NewSet() set := NewSet()
set.Add([]interface{}{1, 2, 3}...) set.Add([]any{1, 2, 3}...)
// then // then
assert.Equal(t, set.Count(), 3) assert.Equal(t, set.Count(), 3)
@@ -198,5 +198,5 @@ func TestSetType(t *testing.T) {
set.add(1) set.add(1)
set.add("2") set.add("2")
vals := set.Keys() vals := set.Keys()
assert.ElementsMatch(t, []interface{}{1, "2"}, vals) assert.ElementsMatch(t, []any{1, "2"}, vals)
} }

View File

@@ -20,7 +20,7 @@ var (
type ( type (
// Execute defines the method to execute the task. // Execute defines the method to execute the task.
Execute func(key, value interface{}) Execute func(key, value any)
// A TimingWheel is a timing wheel object to schedule tasks. // A TimingWheel is a timing wheel object to schedule tasks.
TimingWheel struct { TimingWheel struct {
@@ -33,14 +33,14 @@ type (
execute Execute execute Execute
setChannel chan timingEntry setChannel chan timingEntry
moveChannel chan baseEntry moveChannel chan baseEntry
removeChannel chan interface{} removeChannel chan any
drainChannel chan func(key, value interface{}) drainChannel chan func(key, value any)
stopChannel chan lang.PlaceholderType stopChannel chan lang.PlaceholderType
} }
timingEntry struct { timingEntry struct {
baseEntry baseEntry
value interface{} value any
circle int circle int
diff int diff int
removed bool removed bool
@@ -48,7 +48,7 @@ type (
baseEntry struct { baseEntry struct {
delay time.Duration delay time.Duration
key interface{} key any
} }
positionEntry struct { positionEntry struct {
@@ -57,8 +57,8 @@ type (
} }
timingTask struct { timingTask struct {
key interface{} key any
value interface{} value any
} }
) )
@@ -85,8 +85,8 @@ func NewTimingWheelWithTicker(interval time.Duration, numSlots int, execute Exec
numSlots: numSlots, numSlots: numSlots,
setChannel: make(chan timingEntry), setChannel: make(chan timingEntry),
moveChannel: make(chan baseEntry), moveChannel: make(chan baseEntry),
removeChannel: make(chan interface{}), removeChannel: make(chan any),
drainChannel: make(chan func(key, value interface{})), drainChannel: make(chan func(key, value any)),
stopChannel: make(chan lang.PlaceholderType), stopChannel: make(chan lang.PlaceholderType),
} }
@@ -97,7 +97,7 @@ func NewTimingWheelWithTicker(interval time.Duration, numSlots int, execute Exec
} }
// Drain drains all items and executes them. // Drain drains all items and executes them.
func (tw *TimingWheel) Drain(fn func(key, value interface{})) error { func (tw *TimingWheel) Drain(fn func(key, value any)) error {
select { select {
case tw.drainChannel <- fn: case tw.drainChannel <- fn:
return nil return nil
@@ -107,7 +107,7 @@ func (tw *TimingWheel) Drain(fn func(key, value interface{})) error {
} }
// MoveTimer moves the task with the given key to the given delay. // MoveTimer moves the task with the given key to the given delay.
func (tw *TimingWheel) MoveTimer(key interface{}, delay time.Duration) error { func (tw *TimingWheel) MoveTimer(key any, delay time.Duration) error {
if delay <= 0 || key == nil { if delay <= 0 || key == nil {
return ErrArgument return ErrArgument
} }
@@ -124,7 +124,7 @@ func (tw *TimingWheel) MoveTimer(key interface{}, delay time.Duration) error {
} }
// RemoveTimer removes the task with the given key. // RemoveTimer removes the task with the given key.
func (tw *TimingWheel) RemoveTimer(key interface{}) error { func (tw *TimingWheel) RemoveTimer(key any) error {
if key == nil { if key == nil {
return ErrArgument return ErrArgument
} }
@@ -138,7 +138,7 @@ func (tw *TimingWheel) RemoveTimer(key interface{}) error {
} }
// SetTimer sets the task value with the given key to the delay. // SetTimer sets the task value with the given key to the delay.
func (tw *TimingWheel) SetTimer(key, value interface{}, delay time.Duration) error { func (tw *TimingWheel) SetTimer(key, value any, delay time.Duration) error {
if delay <= 0 || key == nil { if delay <= 0 || key == nil {
return ErrArgument return ErrArgument
} }
@@ -162,7 +162,7 @@ func (tw *TimingWheel) Stop() {
close(tw.stopChannel) close(tw.stopChannel)
} }
func (tw *TimingWheel) drainAll(fn func(key, value interface{})) { func (tw *TimingWheel) drainAll(fn func(key, value any)) {
runner := threading.NewTaskRunner(drainWorkers) runner := threading.NewTaskRunner(drainWorkers)
for _, slot := range tw.slots { for _, slot := range tw.slots {
for e := slot.Front(); e != nil; { for e := slot.Front(); e != nil; {
@@ -232,7 +232,7 @@ func (tw *TimingWheel) onTick() {
tw.scanAndRunTasks(l) tw.scanAndRunTasks(l)
} }
func (tw *TimingWheel) removeTask(key interface{}) { func (tw *TimingWheel) removeTask(key any) {
val, ok := tw.timers.Get(key) val, ok := tw.timers.Get(key)
if !ok { if !ok {
return return

View File

@@ -20,13 +20,13 @@ const (
) )
func TestNewTimingWheel(t *testing.T) { func TestNewTimingWheel(t *testing.T) {
_, err := NewTimingWheel(0, 10, func(key, value interface{}) {}) _, err := NewTimingWheel(0, 10, func(key, value any) {})
assert.NotNil(t, err) assert.NotNil(t, err)
} }
func TestTimingWheel_Drain(t *testing.T) { func TestTimingWheel_Drain(t *testing.T) {
ticker := timex.NewFakeTicker() ticker := timex.NewFakeTicker()
tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v interface{}) { tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v any) {
}, ticker) }, ticker)
tw.SetTimer("first", 3, testStep*4) tw.SetTimer("first", 3, testStep*4)
tw.SetTimer("second", 5, testStep*7) tw.SetTimer("second", 5, testStep*7)
@@ -36,7 +36,7 @@ func TestTimingWheel_Drain(t *testing.T) {
var lock sync.Mutex var lock sync.Mutex
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(3) wg.Add(3)
tw.Drain(func(key, value interface{}) { tw.Drain(func(key, value any) {
lock.Lock() lock.Lock()
defer lock.Unlock() defer lock.Unlock()
keys = append(keys, key.(string)) keys = append(keys, key.(string))
@@ -50,19 +50,19 @@ func TestTimingWheel_Drain(t *testing.T) {
assert.EqualValues(t, []string{"first", "second", "third"}, keys) assert.EqualValues(t, []string{"first", "second", "third"}, keys)
assert.EqualValues(t, []int{3, 5, 7}, vals) assert.EqualValues(t, []int{3, 5, 7}, vals)
var count int var count int
tw.Drain(func(key, value interface{}) { tw.Drain(func(key, value any) {
count++ count++
}) })
time.Sleep(time.Millisecond * 100) time.Sleep(time.Millisecond * 100)
assert.Equal(t, 0, count) assert.Equal(t, 0, count)
tw.Stop() tw.Stop()
assert.Equal(t, ErrClosed, tw.Drain(func(key, value interface{}) {})) assert.Equal(t, ErrClosed, tw.Drain(func(key, value any) {}))
} }
func TestTimingWheel_SetTimerSoon(t *testing.T) { func TestTimingWheel_SetTimerSoon(t *testing.T) {
run := syncx.NewAtomicBool() run := syncx.NewAtomicBool()
ticker := timex.NewFakeTicker() ticker := timex.NewFakeTicker()
tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v interface{}) { tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v any) {
assert.True(t, run.CompareAndSwap(false, true)) assert.True(t, run.CompareAndSwap(false, true))
assert.Equal(t, "any", k) assert.Equal(t, "any", k)
assert.Equal(t, 3, v.(int)) assert.Equal(t, 3, v.(int))
@@ -78,7 +78,7 @@ func TestTimingWheel_SetTimerSoon(t *testing.T) {
func TestTimingWheel_SetTimerTwice(t *testing.T) { func TestTimingWheel_SetTimerTwice(t *testing.T) {
run := syncx.NewAtomicBool() run := syncx.NewAtomicBool()
ticker := timex.NewFakeTicker() ticker := timex.NewFakeTicker()
tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v interface{}) { tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v any) {
assert.True(t, run.CompareAndSwap(false, true)) assert.True(t, run.CompareAndSwap(false, true))
assert.Equal(t, "any", k) assert.Equal(t, "any", k)
assert.Equal(t, 5, v.(int)) assert.Equal(t, 5, v.(int))
@@ -96,7 +96,7 @@ func TestTimingWheel_SetTimerTwice(t *testing.T) {
func TestTimingWheel_SetTimerWrongDelay(t *testing.T) { func TestTimingWheel_SetTimerWrongDelay(t *testing.T) {
ticker := timex.NewFakeTicker() ticker := timex.NewFakeTicker()
tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v interface{}) {}, ticker) tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v any) {}, ticker)
defer tw.Stop() defer tw.Stop()
assert.NotPanics(t, func() { assert.NotPanics(t, func() {
tw.SetTimer("any", 3, -testStep) tw.SetTimer("any", 3, -testStep)
@@ -105,7 +105,7 @@ func TestTimingWheel_SetTimerWrongDelay(t *testing.T) {
func TestTimingWheel_SetTimerAfterClose(t *testing.T) { func TestTimingWheel_SetTimerAfterClose(t *testing.T) {
ticker := timex.NewFakeTicker() ticker := timex.NewFakeTicker()
tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v interface{}) {}, ticker) tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v any) {}, ticker)
tw.Stop() tw.Stop()
assert.Equal(t, ErrClosed, tw.SetTimer("any", 3, testStep)) assert.Equal(t, ErrClosed, tw.SetTimer("any", 3, testStep))
} }
@@ -113,7 +113,7 @@ func TestTimingWheel_SetTimerAfterClose(t *testing.T) {
func TestTimingWheel_MoveTimer(t *testing.T) { func TestTimingWheel_MoveTimer(t *testing.T) {
run := syncx.NewAtomicBool() run := syncx.NewAtomicBool()
ticker := timex.NewFakeTicker() ticker := timex.NewFakeTicker()
tw, _ := NewTimingWheelWithTicker(testStep, 3, func(k, v interface{}) { tw, _ := NewTimingWheelWithTicker(testStep, 3, func(k, v any) {
assert.True(t, run.CompareAndSwap(false, true)) assert.True(t, run.CompareAndSwap(false, true))
assert.Equal(t, "any", k) assert.Equal(t, "any", k)
assert.Equal(t, 3, v.(int)) assert.Equal(t, 3, v.(int))
@@ -139,7 +139,7 @@ func TestTimingWheel_MoveTimer(t *testing.T) {
func TestTimingWheel_MoveTimerSoon(t *testing.T) { func TestTimingWheel_MoveTimerSoon(t *testing.T) {
run := syncx.NewAtomicBool() run := syncx.NewAtomicBool()
ticker := timex.NewFakeTicker() ticker := timex.NewFakeTicker()
tw, _ := NewTimingWheelWithTicker(testStep, 3, func(k, v interface{}) { tw, _ := NewTimingWheelWithTicker(testStep, 3, func(k, v any) {
assert.True(t, run.CompareAndSwap(false, true)) assert.True(t, run.CompareAndSwap(false, true))
assert.Equal(t, "any", k) assert.Equal(t, "any", k)
assert.Equal(t, 3, v.(int)) assert.Equal(t, 3, v.(int))
@@ -155,7 +155,7 @@ func TestTimingWheel_MoveTimerSoon(t *testing.T) {
func TestTimingWheel_MoveTimerEarlier(t *testing.T) { func TestTimingWheel_MoveTimerEarlier(t *testing.T) {
run := syncx.NewAtomicBool() run := syncx.NewAtomicBool()
ticker := timex.NewFakeTicker() ticker := timex.NewFakeTicker()
tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v interface{}) { tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v any) {
assert.True(t, run.CompareAndSwap(false, true)) assert.True(t, run.CompareAndSwap(false, true))
assert.Equal(t, "any", k) assert.Equal(t, "any", k)
assert.Equal(t, 3, v.(int)) assert.Equal(t, 3, v.(int))
@@ -173,7 +173,7 @@ func TestTimingWheel_MoveTimerEarlier(t *testing.T) {
func TestTimingWheel_RemoveTimer(t *testing.T) { func TestTimingWheel_RemoveTimer(t *testing.T) {
ticker := timex.NewFakeTicker() ticker := timex.NewFakeTicker()
tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v interface{}) {}, ticker) tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v any) {}, ticker)
tw.SetTimer("any", 3, testStep) tw.SetTimer("any", 3, testStep)
assert.NotPanics(t, func() { assert.NotPanics(t, func() {
tw.RemoveTimer("any") tw.RemoveTimer("any")
@@ -236,7 +236,7 @@ func TestTimingWheel_SetTimer(t *testing.T) {
} }
var actual int32 var actual int32
done := make(chan lang.PlaceholderType) done := make(chan lang.PlaceholderType)
tw, err := NewTimingWheelWithTicker(testStep, test.slots, func(key, value interface{}) { tw, err := NewTimingWheelWithTicker(testStep, test.slots, func(key, value any) {
assert.Equal(t, 1, key.(int)) assert.Equal(t, 1, key.(int))
assert.Equal(t, 2, value.(int)) assert.Equal(t, 2, value.(int))
actual = atomic.LoadInt32(&count) actual = atomic.LoadInt32(&count)
@@ -317,7 +317,7 @@ func TestTimingWheel_SetAndMoveThenStart(t *testing.T) {
} }
var actual int32 var actual int32
done := make(chan lang.PlaceholderType) done := make(chan lang.PlaceholderType)
tw, err := NewTimingWheelWithTicker(testStep, test.slots, func(key, value interface{}) { tw, err := NewTimingWheelWithTicker(testStep, test.slots, func(key, value any) {
actual = atomic.LoadInt32(&count) actual = atomic.LoadInt32(&count)
close(done) close(done)
}, ticker) }, ticker)
@@ -405,7 +405,7 @@ func TestTimingWheel_SetAndMoveTwice(t *testing.T) {
} }
var actual int32 var actual int32
done := make(chan lang.PlaceholderType) done := make(chan lang.PlaceholderType)
tw, err := NewTimingWheelWithTicker(testStep, test.slots, func(key, value interface{}) { tw, err := NewTimingWheelWithTicker(testStep, test.slots, func(key, value any) {
actual = atomic.LoadInt32(&count) actual = atomic.LoadInt32(&count)
close(done) close(done)
}, ticker) }, ticker)
@@ -486,7 +486,7 @@ func TestTimingWheel_ElapsedAndSet(t *testing.T) {
} }
var actual int32 var actual int32
done := make(chan lang.PlaceholderType) done := make(chan lang.PlaceholderType)
tw, err := NewTimingWheelWithTicker(testStep, test.slots, func(key, value interface{}) { tw, err := NewTimingWheelWithTicker(testStep, test.slots, func(key, value any) {
actual = atomic.LoadInt32(&count) actual = atomic.LoadInt32(&count)
close(done) close(done)
}, ticker) }, ticker)
@@ -577,7 +577,7 @@ func TestTimingWheel_ElapsedAndSetThenMove(t *testing.T) {
} }
var actual int32 var actual int32
done := make(chan lang.PlaceholderType) done := make(chan lang.PlaceholderType)
tw, err := NewTimingWheelWithTicker(testStep, test.slots, func(key, value interface{}) { tw, err := NewTimingWheelWithTicker(testStep, test.slots, func(key, value any) {
actual = atomic.LoadInt32(&count) actual = atomic.LoadInt32(&count)
close(done) close(done)
}, ticker) }, ticker)
@@ -612,7 +612,7 @@ func TestMoveAndRemoveTask(t *testing.T) {
} }
} }
var keys []int var keys []int
tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v interface{}) { tw, _ := NewTimingWheelWithTicker(testStep, 10, func(k, v any) {
assert.Equal(t, "any", k) assert.Equal(t, "any", k)
assert.Equal(t, 3, v.(int)) assert.Equal(t, 3, v.(int))
keys = append(keys, v.(int)) keys = append(keys, v.(int))
@@ -632,7 +632,7 @@ func TestMoveAndRemoveTask(t *testing.T) {
func BenchmarkTimingWheel(b *testing.B) { func BenchmarkTimingWheel(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
tw, _ := NewTimingWheel(time.Second, 100, func(k, v interface{}) {}) tw, _ := NewTimingWheel(time.Second, 100, func(k, v any) {})
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
tw.SetTimer(i, i, time.Second) tw.SetTimer(i, i, time.Second)
tw.SetTimer(b.N+i, b.N+i, time.Second) tw.SetTimer(b.N+i, b.N+i, time.Second)

View File

@@ -13,21 +13,36 @@ import (
"github.com/zeromicro/go-zero/internal/encoding" "github.com/zeromicro/go-zero/internal/encoding"
) )
var loaders = map[string]func([]byte, interface{}) error{ const (
".json": LoadFromJsonBytes, jsonTagKey = "json"
".toml": LoadFromTomlBytes, jsonTagSep = ','
".yaml": LoadFromYamlBytes, )
".yml": LoadFromYamlBytes,
var (
fillDefaultUnmarshaler = mapping.NewUnmarshaler(jsonTagKey, mapping.WithDefault())
loaders = map[string]func([]byte, any) error{
".json": LoadFromJsonBytes,
".toml": LoadFromTomlBytes,
".yaml": LoadFromYamlBytes,
".yml": LoadFromYamlBytes,
}
)
// children and mapField should not be both filled.
// named fields and map cannot be bound to the same field name.
type fieldInfo struct {
children map[string]*fieldInfo
mapField *fieldInfo
} }
type fieldInfo struct { // FillDefault fills the default values for the given v,
name string // and the premise is that the value of v must be guaranteed to be empty.
kind reflect.Kind func FillDefault(v any) error {
children map[string]fieldInfo return fillDefaultUnmarshaler.Unmarshal(map[string]any{}, v)
} }
// Load loads config into v from file, .json, .yaml and .yml are acceptable. // Load loads config into v from file, .json, .yaml and .yml are acceptable.
func Load(file string, v interface{}, opts ...Option) error { func Load(file string, v any, opts ...Option) error {
content, err := os.ReadFile(file) content, err := os.ReadFile(file)
if err != nil { if err != nil {
return err return err
@@ -52,31 +67,35 @@ func Load(file string, v interface{}, opts ...Option) error {
// LoadConfig loads config into v from file, .json, .yaml and .yml are acceptable. // LoadConfig loads config into v from file, .json, .yaml and .yml are acceptable.
// Deprecated: use Load instead. // Deprecated: use Load instead.
func LoadConfig(file string, v interface{}, opts ...Option) error { func LoadConfig(file string, v any, opts ...Option) error {
return Load(file, v, opts...) return Load(file, v, opts...)
} }
// LoadFromJsonBytes loads config into v from content json bytes. // LoadFromJsonBytes loads config into v from content json bytes.
func LoadFromJsonBytes(content []byte, v interface{}) error { func LoadFromJsonBytes(content []byte, v any) error {
var m map[string]interface{} info, err := buildFieldsInfo(reflect.TypeOf(v), "")
if err := jsonx.Unmarshal(content, &m); err != nil { if err != nil {
return err return err
} }
finfo := buildFieldsInfo(reflect.TypeOf(v)) var m map[string]any
lowerCaseKeyMap := toLowerCaseKeyMap(m, finfo) if err = jsonx.Unmarshal(content, &m); err != nil {
return err
}
lowerCaseKeyMap := toLowerCaseKeyMap(m, info)
return mapping.UnmarshalJsonMap(lowerCaseKeyMap, v, mapping.WithCanonicalKeyFunc(toLowerCase)) return mapping.UnmarshalJsonMap(lowerCaseKeyMap, v, mapping.WithCanonicalKeyFunc(toLowerCase))
} }
// LoadConfigFromJsonBytes loads config into v from content json bytes. // LoadConfigFromJsonBytes loads config into v from content json bytes.
// Deprecated: use LoadFromJsonBytes instead. // Deprecated: use LoadFromJsonBytes instead.
func LoadConfigFromJsonBytes(content []byte, v interface{}) error { func LoadConfigFromJsonBytes(content []byte, v any) error {
return LoadFromJsonBytes(content, v) return LoadFromJsonBytes(content, v)
} }
// LoadFromTomlBytes loads config into v from content toml bytes. // LoadFromTomlBytes loads config into v from content toml bytes.
func LoadFromTomlBytes(content []byte, v interface{}) error { func LoadFromTomlBytes(content []byte, v any) error {
b, err := encoding.TomlToJson(content) b, err := encoding.TomlToJson(content)
if err != nil { if err != nil {
return err return err
@@ -86,7 +105,7 @@ func LoadFromTomlBytes(content []byte, v interface{}) error {
} }
// LoadFromYamlBytes loads config into v from content yaml bytes. // LoadFromYamlBytes loads config into v from content yaml bytes.
func LoadFromYamlBytes(content []byte, v interface{}) error { func LoadFromYamlBytes(content []byte, v any) error {
b, err := encoding.YamlToJson(content) b, err := encoding.YamlToJson(content)
if err != nil { if err != nil {
return err return err
@@ -97,85 +116,198 @@ func LoadFromYamlBytes(content []byte, v interface{}) error {
// LoadConfigFromYamlBytes loads config into v from content yaml bytes. // LoadConfigFromYamlBytes loads config into v from content yaml bytes.
// Deprecated: use LoadFromYamlBytes instead. // Deprecated: use LoadFromYamlBytes instead.
func LoadConfigFromYamlBytes(content []byte, v interface{}) error { func LoadConfigFromYamlBytes(content []byte, v any) error {
return LoadFromYamlBytes(content, v) return LoadFromYamlBytes(content, v)
} }
// MustLoad loads config into v from path, exits on error. // MustLoad loads config into v from path, exits on error.
func MustLoad(path string, v interface{}, opts ...Option) { func MustLoad(path string, v any, opts ...Option) {
if err := Load(path, v, opts...); err != nil { if err := Load(path, v, opts...); err != nil {
log.Fatalf("error: config file %s, %s", path, err.Error()) log.Fatalf("error: config file %s, %s", path, err.Error())
} }
} }
func buildFieldsInfo(tp reflect.Type) map[string]fieldInfo { func addOrMergeFields(info *fieldInfo, key string, child *fieldInfo, fullName string) error {
if prev, ok := info.children[key]; ok {
if child.mapField != nil {
return newConflictKeyError(fullName)
}
if err := mergeFields(prev, key, child.children, fullName); err != nil {
return err
}
} else {
info.children[key] = child
}
return nil
}
func buildAnonymousFieldInfo(info *fieldInfo, lowerCaseName string, ft reflect.Type, fullName string) error {
switch ft.Kind() {
case reflect.Struct:
fields, err := buildFieldsInfo(ft, fullName)
if err != nil {
return err
}
for k, v := range fields.children {
if err = addOrMergeFields(info, k, v, fullName); err != nil {
return err
}
}
case reflect.Map:
elemField, err := buildFieldsInfo(mapping.Deref(ft.Elem()), fullName)
if err != nil {
return err
}
if _, ok := info.children[lowerCaseName]; ok {
return newConflictKeyError(fullName)
}
info.children[lowerCaseName] = &fieldInfo{
children: make(map[string]*fieldInfo),
mapField: elemField,
}
default:
if _, ok := info.children[lowerCaseName]; ok {
return newConflictKeyError(fullName)
}
info.children[lowerCaseName] = &fieldInfo{
children: make(map[string]*fieldInfo),
}
}
return nil
}
func buildFieldsInfo(tp reflect.Type, fullName string) (*fieldInfo, error) {
tp = mapping.Deref(tp) tp = mapping.Deref(tp)
switch tp.Kind() { switch tp.Kind() {
case reflect.Struct: case reflect.Struct:
return buildStructFieldsInfo(tp) return buildStructFieldsInfo(tp, fullName)
case reflect.Array, reflect.Slice: case reflect.Array, reflect.Slice:
return buildFieldsInfo(mapping.Deref(tp.Elem())) return buildFieldsInfo(mapping.Deref(tp.Elem()), fullName)
case reflect.Chan, reflect.Func:
return nil, fmt.Errorf("unsupported type: %s", tp.Kind())
default: default:
return nil return &fieldInfo{
children: make(map[string]*fieldInfo),
}, nil
} }
} }
func buildStructFieldsInfo(tp reflect.Type) map[string]fieldInfo { func buildNamedFieldInfo(info *fieldInfo, lowerCaseName string, ft reflect.Type, fullName string) error {
info := make(map[string]fieldInfo) var finfo *fieldInfo
var err error
for i := 0; i < tp.NumField(); i++ { switch ft.Kind() {
field := tp.Field(i) case reflect.Struct:
name := field.Name finfo, err = buildFieldsInfo(ft, fullName)
lowerCaseName := toLowerCase(name) if err != nil {
ft := mapping.Deref(field.Type) return err
}
// flatten anonymous fields case reflect.Array, reflect.Slice:
if field.Anonymous { finfo, err = buildFieldsInfo(ft.Elem(), fullName)
if ft.Kind() == reflect.Struct { if err != nil {
fields := buildFieldsInfo(ft) return err
for k, v := range fields { }
info[k] = v case reflect.Map:
} elemInfo, err := buildFieldsInfo(mapping.Deref(ft.Elem()), fullName)
} else { if err != nil {
info[lowerCaseName] = fieldInfo{ return err
name: name,
kind: ft.Kind(),
}
}
continue
} }
var fields map[string]fieldInfo finfo = &fieldInfo{
switch ft.Kind() { children: make(map[string]*fieldInfo),
case reflect.Struct: mapField: elemInfo,
fields = buildFieldsInfo(ft)
case reflect.Array, reflect.Slice:
fields = buildFieldsInfo(ft.Elem())
case reflect.Map:
fields = buildFieldsInfo(ft.Elem())
} }
default:
info[lowerCaseName] = fieldInfo{ finfo, err = buildFieldsInfo(ft, fullName)
name: name, if err != nil {
kind: ft.Kind(), return err
children: fields,
} }
} }
return info return addOrMergeFields(info, lowerCaseName, finfo, fullName)
}
func buildStructFieldsInfo(tp reflect.Type, fullName string) (*fieldInfo, error) {
info := &fieldInfo{
children: make(map[string]*fieldInfo),
}
for i := 0; i < tp.NumField(); i++ {
field := tp.Field(i)
if !field.IsExported() {
continue
}
name := getTagName(field)
lowerCaseName := toLowerCase(name)
ft := mapping.Deref(field.Type)
// flatten anonymous fields
if field.Anonymous {
if err := buildAnonymousFieldInfo(info, lowerCaseName, ft,
getFullName(fullName, lowerCaseName)); err != nil {
return nil, err
}
} else if err := buildNamedFieldInfo(info, lowerCaseName, ft,
getFullName(fullName, lowerCaseName)); err != nil {
return nil, err
}
}
return info, nil
}
// getTagName get the tag name of the given field, if no tag name, use file.Name.
// field.Name is returned on tags like `json:""` and `json:",optional"`.
func getTagName(field reflect.StructField) string {
if tag, ok := field.Tag.Lookup(jsonTagKey); ok {
if pos := strings.IndexByte(tag, jsonTagSep); pos >= 0 {
tag = tag[:pos]
}
tag = strings.TrimSpace(tag)
if len(tag) > 0 {
return tag
}
}
return field.Name
}
func mergeFields(prev *fieldInfo, key string, children map[string]*fieldInfo, fullName string) error {
if len(prev.children) == 0 || len(children) == 0 {
return newConflictKeyError(fullName)
}
// merge fields
for k, v := range children {
if _, ok := prev.children[k]; ok {
return newConflictKeyError(fullName)
}
prev.children[k] = v
}
return nil
} }
func toLowerCase(s string) string { func toLowerCase(s string) string {
return strings.ToLower(s) return strings.ToLower(s)
} }
func toLowerCaseInterface(v interface{}, info map[string]fieldInfo) interface{} { func toLowerCaseInterface(v any, info *fieldInfo) any {
switch vv := v.(type) { switch vv := v.(type) {
case map[string]interface{}: case map[string]any:
return toLowerCaseKeyMap(vv, info) return toLowerCaseKeyMap(vv, info)
case []interface{}: case []any:
var arr []interface{} var arr []any
for _, vvv := range vv { for _, vvv := range vv {
arr = append(arr, toLowerCaseInterface(vvv, info)) arr = append(arr, toLowerCaseInterface(vvv, info))
} }
@@ -185,19 +317,21 @@ func toLowerCaseInterface(v interface{}, info map[string]fieldInfo) interface{}
} }
} }
func toLowerCaseKeyMap(m map[string]interface{}, info map[string]fieldInfo) map[string]interface{} { func toLowerCaseKeyMap(m map[string]any, info *fieldInfo) map[string]any {
res := make(map[string]interface{}) res := make(map[string]any)
for k, v := range m { for k, v := range m {
ti, ok := info[k] ti, ok := info.children[k]
if ok { if ok {
res[k] = toLowerCaseInterface(v, ti.children) res[k] = toLowerCaseInterface(v, ti)
continue continue
} }
lk := toLowerCase(k) lk := toLowerCase(k)
if ti, ok = info[lk]; ok { if ti, ok = info.children[lk]; ok {
res[lk] = toLowerCaseInterface(v, ti.children) res[lk] = toLowerCaseInterface(v, ti)
} else if info.mapField != nil {
res[k] = toLowerCaseInterface(v, info.mapField)
} else { } else {
res[k] = v res[k] = v
} }
@@ -205,3 +339,23 @@ func toLowerCaseKeyMap(m map[string]interface{}, info map[string]fieldInfo) map[
return res return res
} }
type conflictKeyError struct {
key string
}
func newConflictKeyError(key string) conflictKeyError {
return conflictKeyError{key: key}
}
func (e conflictKeyError) Error() string {
return fmt.Sprintf("conflict key %s, pay attention to anonymous fields", e.key)
}
func getFullName(parent, child string) string {
if len(parent) == 0 {
return child
}
return strings.Join([]string{parent, child}, ".")
}

View File

@@ -2,6 +2,7 @@ package conf
import ( import (
"os" "os"
"reflect"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -9,6 +10,8 @@ import (
"github.com/zeromicro/go-zero/core/hash" "github.com/zeromicro/go-zero/core/hash"
) )
var dupErr conflictKeyError
func TestLoadConfig_notExists(t *testing.T) { func TestLoadConfig_notExists(t *testing.T) {
assert.NotNil(t, Load("not_a_file", nil)) assert.NotNil(t, Load("not_a_file", nil))
} }
@@ -17,7 +20,7 @@ func TestLoadConfig_notRecogFile(t *testing.T) {
filename, err := fs.TempFilenameWithText("hello") filename, err := fs.TempFilenameWithText("hello")
assert.Nil(t, err) assert.Nil(t, err)
defer os.Remove(filename) defer os.Remove(filename)
assert.NotNil(t, Load(filename, nil)) assert.NotNil(t, LoadConfig(filename, nil))
} }
func TestConfigJson(t *testing.T) { func TestConfigJson(t *testing.T) {
@@ -64,7 +67,7 @@ func TestLoadFromJsonBytesArray(t *testing.T) {
} }
} }
assert.NoError(t, LoadFromJsonBytes(input, &val)) assert.NoError(t, LoadConfigFromJsonBytes(input, &val))
var expect []string var expect []string
for _, user := range val.Users { for _, user := range val.Users {
expect = append(expect, user.Name) expect = append(expect, user.Name)
@@ -121,6 +124,24 @@ d = "abcd"
} }
} }
func TestConfigWithLower(t *testing.T) {
text := `a = "foo"
b = 1
`
tmpfile, err := createTempFile(".toml", text)
assert.Nil(t, err)
defer os.Remove(tmpfile)
var val struct {
A string `json:"a"`
b int
}
if assert.NoError(t, Load(tmpfile, &val)) {
assert.Equal(t, "foo", val.A)
assert.Equal(t, 0, val.b)
}
}
func TestConfigJsonCanonical(t *testing.T) { func TestConfigJsonCanonical(t *testing.T) {
text := []byte(`{"a": "foo", "B": "bar"}`) text := []byte(`{"a": "foo", "B": "bar"}`)
@@ -172,7 +193,7 @@ B: bar`)
A string A string
B string B string
} }
assert.NoError(t, LoadFromYamlBytes(text, &val1)) assert.NoError(t, LoadConfigFromYamlBytes(text, &val1))
assert.Equal(t, "foo", val1.A) assert.Equal(t, "foo", val1.A)
assert.Equal(t, "bar", val1.B) assert.Equal(t, "bar", val1.B)
assert.NoError(t, LoadFromYamlBytes(text, &val2)) assert.NoError(t, LoadFromYamlBytes(text, &val2))
@@ -384,6 +405,102 @@ func TestLoadFromYamlBytesLayers(t *testing.T) {
assert.Equal(t, "foo", val.Value) assert.Equal(t, "foo", val.Value)
} }
func TestLoadFromYamlItemOverlay(t *testing.T) {
type (
Redis struct {
Host string
Port int
}
RedisKey struct {
Redis
Key string
}
Server struct {
Redis RedisKey
}
TestConfig struct {
Server
Redis Redis
}
)
input := []byte(`Redis:
Host: localhost
Port: 6379
Key: test
`)
var c TestConfig
assert.ErrorAs(t, LoadFromYamlBytes(input, &c), &dupErr)
}
func TestLoadFromYamlItemOverlayReverse(t *testing.T) {
type (
Redis struct {
Host string
Port int
}
RedisKey struct {
Redis
Key string
}
Server struct {
Redis Redis
}
TestConfig struct {
Redis RedisKey
Server
}
)
input := []byte(`Redis:
Host: localhost
Port: 6379
Key: test
`)
var c TestConfig
assert.ErrorAs(t, LoadFromYamlBytes(input, &c), &dupErr)
}
func TestLoadFromYamlItemOverlayWithMap(t *testing.T) {
type (
Redis struct {
Host string
Port int
}
RedisKey struct {
Redis
Key string
}
Server struct {
Redis RedisKey
}
TestConfig struct {
Server
Redis map[string]interface{}
}
)
input := []byte(`Redis:
Host: localhost
Port: 6379
Key: test
`)
var c TestConfig
assert.ErrorAs(t, LoadFromYamlBytes(input, &c), &dupErr)
}
func TestUnmarshalJsonBytesMap(t *testing.T) { func TestUnmarshalJsonBytesMap(t *testing.T) {
input := []byte(`{"foo":{"/mtproto.RPCTos": "bff.bff","bar":"baz"}}`) input := []byte(`{"foo":{"/mtproto.RPCTos": "bff.bff","bar":"baz"}}`)
@@ -450,18 +567,742 @@ func TestUnmarshalJsonBytesWithAnonymousField(t *testing.T) {
assert.Equal(t, Int(3), c.Int) assert.Equal(t, Int(3), c.Int)
} }
func TestUnmarshalJsonBytesWithMapValueOfStruct(t *testing.T) {
type (
Value struct {
Name string
}
Config struct {
Items map[string]Value
}
)
var inputs = [][]byte{
[]byte(`{"Items": {"Key":{"Name": "foo"}}}`),
[]byte(`{"Items": {"Key":{"Name": "foo"}}}`),
[]byte(`{"items": {"key":{"name": "foo"}}}`),
[]byte(`{"items": {"key":{"name": "foo"}}}`),
}
for _, input := range inputs {
var c Config
if assert.NoError(t, LoadFromJsonBytes(input, &c)) {
assert.Equal(t, 1, len(c.Items))
for _, v := range c.Items {
assert.Equal(t, "foo", v.Name)
}
}
}
}
func TestUnmarshalJsonBytesWithMapTypeValueOfStruct(t *testing.T) {
type (
Value struct {
Name string
}
Map map[string]Value
Config struct {
Map
}
)
var inputs = [][]byte{
[]byte(`{"Map": {"Key":{"Name": "foo"}}}`),
[]byte(`{"Map": {"Key":{"Name": "foo"}}}`),
[]byte(`{"map": {"key":{"name": "foo"}}}`),
[]byte(`{"map": {"key":{"name": "foo"}}}`),
}
for _, input := range inputs {
var c Config
if assert.NoError(t, LoadFromJsonBytes(input, &c)) {
assert.Equal(t, 1, len(c.Map))
for _, v := range c.Map {
assert.Equal(t, "foo", v.Name)
}
}
}
}
func Test_FieldOverwrite(t *testing.T) {
t.Run("normal", func(t *testing.T) {
type Base struct {
Name string
}
type St1 struct {
Base
Name2 string
}
type St2 struct {
Base
Name2 string
}
type St3 struct {
*Base
Name2 string
}
type St4 struct {
*Base
Name2 *string
}
validate := func(val any) {
input := []byte(`{"Name": "hello", "Name2": "world"}`)
assert.NoError(t, LoadFromJsonBytes(input, val))
}
validate(&St1{})
validate(&St2{})
validate(&St3{})
validate(&St4{})
})
t.Run("Inherit Override", func(t *testing.T) {
type Base struct {
Name string
}
type St1 struct {
Base
Name string
}
type St2 struct {
Base
Name int
}
type St3 struct {
*Base
Name int
}
type St4 struct {
*Base
Name *string
}
validate := func(val any) {
input := []byte(`{"Name": "hello"}`)
err := LoadFromJsonBytes(input, val)
assert.ErrorAs(t, err, &dupErr)
assert.Equal(t, newConflictKeyError("name").Error(), err.Error())
}
validate(&St1{})
validate(&St2{})
validate(&St3{})
validate(&St4{})
})
t.Run("Inherit more", func(t *testing.T) {
type Base1 struct {
Name string
}
type St0 struct {
Base1
Name string
}
type St1 struct {
St0
Name string
}
type St2 struct {
St0
Name int
}
type St3 struct {
*St0
Name int
}
type St4 struct {
*St0
Name *int
}
validate := func(val any) {
input := []byte(`{"Name": "hello"}`)
err := LoadFromJsonBytes(input, val)
assert.ErrorAs(t, err, &dupErr)
assert.Error(t, err)
}
validate(&St0{})
validate(&St1{})
validate(&St2{})
validate(&St3{})
validate(&St4{})
})
}
func TestFieldOverwriteComplicated(t *testing.T) {
t.Run("double maps", func(t *testing.T) {
type (
Base1 struct {
Values map[string]string
}
Base2 struct {
Values map[string]string
}
Config struct {
Base1
Base2
}
)
var c Config
input := []byte(`{"Values": {"Key": "Value"}}`)
assert.ErrorAs(t, LoadFromJsonBytes(input, &c), &dupErr)
})
t.Run("merge children", func(t *testing.T) {
type (
Inner1 struct {
Name string
}
Inner2 struct {
Age int
}
Base1 struct {
Inner Inner1
}
Base2 struct {
Inner Inner2
}
Config struct {
Base1
Base2
}
)
var c Config
input := []byte(`{"Inner": {"Name": "foo", "Age": 10}}`)
if assert.NoError(t, LoadFromJsonBytes(input, &c)) {
assert.Equal(t, "foo", c.Base1.Inner.Name)
assert.Equal(t, 10, c.Base2.Inner.Age)
}
})
t.Run("overwritten maps", func(t *testing.T) {
type (
Inner struct {
Map map[string]string
}
Config struct {
Map map[string]string
Inner
}
)
var c Config
input := []byte(`{"Inner": {"Map": {"Key": "Value"}}}`)
assert.ErrorAs(t, LoadFromJsonBytes(input, &c), &dupErr)
})
t.Run("overwritten nested maps", func(t *testing.T) {
type (
Inner struct {
Map map[string]string
}
Middle1 struct {
Map map[string]string
Inner
}
Middle2 struct {
Map map[string]string
Inner
}
Config struct {
Middle1
Middle2
}
)
var c Config
input := []byte(`{"Middle1": {"Inner": {"Map": {"Key": "Value"}}}}`)
assert.ErrorAs(t, LoadFromJsonBytes(input, &c), &dupErr)
})
t.Run("overwritten outer/inner maps", func(t *testing.T) {
type (
Inner struct {
Map map[string]string
}
Middle struct {
Inner
Map map[string]string
}
Config struct {
Middle
}
)
var c Config
input := []byte(`{"Middle": {"Inner": {"Map": {"Key": "Value"}}}}`)
assert.ErrorAs(t, LoadFromJsonBytes(input, &c), &dupErr)
})
t.Run("overwritten anonymous maps", func(t *testing.T) {
type (
Inner struct {
Map map[string]string
}
Middle struct {
Inner
Map map[string]string
}
Elem map[string]Middle
Config struct {
Elem
}
)
var c Config
input := []byte(`{"Elem": {"Key": {"Inner": {"Map": {"Key": "Value"}}}}}`)
assert.ErrorAs(t, LoadFromJsonBytes(input, &c), &dupErr)
})
t.Run("overwritten primitive and map", func(t *testing.T) {
type (
Inner struct {
Value string
}
Elem map[string]Inner
Named struct {
Elem string
}
Config struct {
Named
Elem
}
)
var c Config
input := []byte(`{"Elem": {"Key": {"Value": "Value"}}}`)
assert.ErrorAs(t, LoadFromJsonBytes(input, &c), &dupErr)
})
t.Run("overwritten map and slice", func(t *testing.T) {
type (
Inner struct {
Value string
}
Elem []Inner
Named struct {
Elem string
}
Config struct {
Named
Elem
}
)
var c Config
input := []byte(`{"Elem": {"Key": {"Value": "Value"}}}`)
assert.ErrorAs(t, LoadFromJsonBytes(input, &c), &dupErr)
})
t.Run("overwritten map and string", func(t *testing.T) {
type (
Elem string
Named struct {
Elem string
}
Config struct {
Named
Elem
}
)
var c Config
input := []byte(`{"Elem": {"Key": {"Value": "Value"}}}`)
assert.ErrorAs(t, LoadFromJsonBytes(input, &c), &dupErr)
})
}
func TestLoadNamedFieldOverwritten(t *testing.T) {
t.Run("overwritten named struct", func(t *testing.T) {
type (
Elem string
Named struct {
Elem string
}
Base struct {
Named
Elem
}
Config struct {
Val Base
}
)
var c Config
input := []byte(`{"Val": {"Elem": {"Key": {"Value": "Value"}}}}`)
assert.ErrorAs(t, LoadFromJsonBytes(input, &c), &dupErr)
})
t.Run("overwritten named []struct", func(t *testing.T) {
type (
Elem string
Named struct {
Elem string
}
Base struct {
Named
Elem
}
Config struct {
Vals []Base
}
)
var c Config
input := []byte(`{"Vals": [{"Elem": {"Key": {"Value": "Value"}}}]}`)
assert.ErrorAs(t, LoadFromJsonBytes(input, &c), &dupErr)
})
t.Run("overwritten named map[string]struct", func(t *testing.T) {
type (
Elem string
Named struct {
Elem string
}
Base struct {
Named
Elem
}
Config struct {
Vals map[string]Base
}
)
var c Config
input := []byte(`{"Vals": {"Key": {"Elem": {"Key": {"Value": "Value"}}}}}`)
assert.ErrorAs(t, LoadFromJsonBytes(input, &c), &dupErr)
})
t.Run("overwritten named *struct", func(t *testing.T) {
type (
Elem string
Named struct {
Elem string
}
Base struct {
Named
Elem
}
Config struct {
Vals *Base
}
)
var c Config
input := []byte(`{"Vals": [{"Elem": {"Key": {"Value": "Value"}}}]}`)
assert.ErrorAs(t, LoadFromJsonBytes(input, &c), &dupErr)
})
t.Run("overwritten named struct", func(t *testing.T) {
type (
Named struct {
Elem string
}
Base struct {
Named
Elem Named
}
Config struct {
Val Base
}
)
var c Config
input := []byte(`{"Val": {"Elem": "Value"}}`)
assert.ErrorAs(t, LoadFromJsonBytes(input, &c), &dupErr)
})
t.Run("overwritten named struct", func(t *testing.T) {
type Config struct {
Val chan int
}
var c Config
input := []byte(`{"Val": 1}`)
assert.Error(t, LoadFromJsonBytes(input, &c))
})
}
func TestLoadLowerMemberShouldNotConflict(t *testing.T) {
type (
Redis struct {
db uint
}
Config struct {
db uint
Redis
}
)
var c Config
assert.NoError(t, LoadFromJsonBytes([]byte(`{}`), &c))
assert.Zero(t, c.db)
assert.Zero(t, c.Redis.db)
}
func TestFillDefaultUnmarshal(t *testing.T) {
t.Run("nil", func(t *testing.T) {
type St struct{}
err := FillDefault(St{})
assert.Error(t, err)
})
t.Run("not nil", func(t *testing.T) {
type St struct{}
err := FillDefault(&St{})
assert.NoError(t, err)
})
t.Run("default", func(t *testing.T) {
type St struct {
A string `json:",default=a"`
B string
}
var st St
err := FillDefault(&st)
assert.NoError(t, err)
assert.Equal(t, st.A, "a")
})
t.Run("env", func(t *testing.T) {
type St struct {
A string `json:",default=a"`
B string
C string `json:",env=TEST_C"`
}
t.Setenv("TEST_C", "c")
var st St
err := FillDefault(&st)
assert.NoError(t, err)
assert.Equal(t, st.A, "a")
assert.Equal(t, st.C, "c")
})
t.Run("has value", func(t *testing.T) {
type St struct {
A string `json:",default=a"`
B string
}
var st = St{
A: "b",
}
err := FillDefault(&st)
assert.Error(t, err)
})
}
func TestConfigWithJsonTag(t *testing.T) {
t.Run("map with value", func(t *testing.T) {
var input = []byte(`[Value]
[Value.first]
Email = "foo"
[Value.second]
Email = "bar"`)
type Value struct {
Email string
}
type Config struct {
ValueMap map[string]Value `json:"Value"`
}
var c Config
if assert.NoError(t, LoadFromTomlBytes(input, &c)) {
assert.Len(t, c.ValueMap, 2)
}
})
t.Run("map with ptr value", func(t *testing.T) {
var input = []byte(`[Value]
[Value.first]
Email = "foo"
[Value.second]
Email = "bar"`)
type Value struct {
Email string
}
type Config struct {
ValueMap map[string]*Value `json:"Value"`
}
var c Config
if assert.NoError(t, LoadFromTomlBytes(input, &c)) {
assert.Len(t, c.ValueMap, 2)
}
})
t.Run("map with optional", func(t *testing.T) {
var input = []byte(`[Value]
[Value.first]
Email = "foo"
[Value.second]
Email = "bar"`)
type Value struct {
Email string
}
type Config struct {
Value map[string]Value `json:",optional"`
}
var c Config
if assert.NoError(t, LoadFromTomlBytes(input, &c)) {
assert.Len(t, c.Value, 2)
}
})
t.Run("map with empty tag", func(t *testing.T) {
var input = []byte(`[Value]
[Value.first]
Email = "foo"
[Value.second]
Email = "bar"`)
type Value struct {
Email string
}
type Config struct {
Value map[string]Value `json:" "`
}
var c Config
if assert.NoError(t, LoadFromTomlBytes(input, &c)) {
assert.Len(t, c.Value, 2)
}
})
}
func Test_getFullName(t *testing.T) {
assert.Equal(t, "a.b", getFullName("a", "b"))
assert.Equal(t, "a", getFullName("", "a"))
}
func Test_buildFieldsInfo(t *testing.T) {
type ParentSt struct {
Name string
M map[string]int
}
tests := []struct {
name string
t reflect.Type
ok bool
containsKey string
}{
{
name: "normal",
t: reflect.TypeOf(struct{ A string }{}),
ok: true,
},
{
name: "struct anonymous",
t: reflect.TypeOf(struct {
ParentSt
Name string
}{}),
ok: false,
containsKey: newConflictKeyError("name").Error(),
},
{
name: "struct ptr anonymous",
t: reflect.TypeOf(struct {
*ParentSt
Name string
}{}),
ok: false,
containsKey: newConflictKeyError("name").Error(),
},
{
name: "more struct anonymous",
t: reflect.TypeOf(struct {
Value struct {
ParentSt
Name string
}
}{}),
ok: false,
containsKey: newConflictKeyError("value.name").Error(),
},
{
name: "map anonymous",
t: reflect.TypeOf(struct {
ParentSt
M string
}{}),
ok: false,
containsKey: newConflictKeyError("m").Error(),
},
{
name: "map more anonymous",
t: reflect.TypeOf(struct {
Value struct {
ParentSt
M string
}
}{}),
ok: false,
containsKey: newConflictKeyError("value.m").Error(),
},
{
name: "struct slice anonymous",
t: reflect.TypeOf([]struct {
ParentSt
Name string
}{}),
ok: false,
containsKey: newConflictKeyError("name").Error(),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := buildFieldsInfo(tt.t, "")
if tt.ok {
assert.NoError(t, err)
} else {
assert.Error(t, err)
assert.Equal(t, err.Error(), tt.containsKey)
}
})
}
}
func createTempFile(ext, text string) (string, error) { func createTempFile(ext, text string) (string, error) {
tmpfile, err := os.CreateTemp(os.TempDir(), hash.Md5Hex([]byte(text))+"*"+ext) tmpFile, err := os.CreateTemp(os.TempDir(), hash.Md5Hex([]byte(text))+"*"+ext)
if err != nil { if err != nil {
return "", err return "", err
} }
if err := os.WriteFile(tmpfile.Name(), []byte(text), os.ModeTemporary); err != nil { if err := os.WriteFile(tmpFile.Name(), []byte(text), os.ModeTemporary); err != nil {
return "", err return "", err
} }
filename := tmpfile.Name() filename := tmpFile.Name()
if err = tmpfile.Close(); err != nil { if err = tmpFile.Close(); err != nil {
return "", err return "", err
} }

View File

@@ -12,7 +12,6 @@ import (
// PropertyError represents a configuration error message. // PropertyError represents a configuration error message.
type PropertyError struct { type PropertyError struct {
error
message string message string
} }

View File

@@ -4,6 +4,7 @@
```go ```go
type RestfulConf struct { type RestfulConf struct {
ServiceName string `json:",env=SERVICE_NAME"` // read from env automatically
Host string `json:",default=0.0.0.0"` Host string `json:",default=0.0.0.0"`
Port int Port int
LogMode string `json:",options=[file,console]"` LogMode string `json:",options=[file,console]"`
@@ -21,20 +22,20 @@ type RestfulConf struct {
```yaml ```yaml
# most fields are optional or have default values # most fields are optional or have default values
Port: 8080 port: 8080
LogMode: console logMode: console
# you can use env settings # you can use env settings
MaxBytes: ${MAX_BYTES} maxBytes: ${MAX_BYTES}
``` ```
- toml example - toml example
```toml ```toml
# most fields are optional or have default values # most fields are optional or have default values
Port = 8_080 port = 8_080
LogMode = "console" logMode = "console"
# you can use env settings # you can use env settings
MaxBytes = "${MAX_BYTES}" maxBytes = "${MAX_BYTES}"
``` ```
3. Load the config from a file: 3. Load the config from a file:

View File

@@ -14,13 +14,13 @@ type contextValuer struct {
context.Context context.Context
} }
func (cv contextValuer) Value(key string) (interface{}, bool) { func (cv contextValuer) Value(key string) (any, bool) {
v := cv.Context.Value(key) v := cv.Context.Value(key)
return v, v != nil return v, v != nil
} }
// For unmarshals ctx into v. // For unmarshals ctx into v.
func For(ctx context.Context, v interface{}) error { func For(ctx context.Context, v any) error {
return unmarshaler.UnmarshalValuer(contextValuer{ return unmarshaler.UnmarshalValuer(contextValuer{
Context: ctx, Context: ctx,
}, v) }, v)

View File

@@ -13,6 +13,7 @@ var (
type EtcdConf struct { type EtcdConf struct {
Hosts []string Hosts []string
Key string Key string
ID int64 `json:",optional"`
User string `json:",optional"` User string `json:",optional"`
Pass string `json:",optional"` Pass string `json:",optional"`
CertFile string `json:",optional"` CertFile string `json:",optional"`
@@ -26,6 +27,11 @@ func (c EtcdConf) HasAccount() bool {
return len(c.User) > 0 && len(c.Pass) > 0 return len(c.User) > 0 && len(c.Pass) > 0
} }
// HasID returns if ID provided.
func (c EtcdConf) HasID() bool {
return c.ID > 0
}
// HasTLS returns if TLS CertFile/CertKeyFile/CACertFile are provided. // HasTLS returns if TLS CertFile/CertKeyFile/CACertFile are provided.
func (c EtcdConf) HasTLS() bool { func (c EtcdConf) HasTLS() bool {
return len(c.CertFile) > 0 && len(c.CertKeyFile) > 0 && len(c.CACertFile) > 0 return len(c.CertFile) > 0 && len(c.CertKeyFile) > 0 && len(c.CACertFile) > 0

View File

@@ -80,3 +80,90 @@ func TestEtcdConf_HasAccount(t *testing.T) {
assert.Equal(t, test.hasAccount, test.EtcdConf.HasAccount()) assert.Equal(t, test.hasAccount, test.EtcdConf.HasAccount())
} }
} }
func TestEtcdConf_HasID(t *testing.T) {
tests := []struct {
EtcdConf
hasServerID bool
}{
{
EtcdConf: EtcdConf{
Hosts: []string{"any"},
ID: -1,
},
hasServerID: false,
},
{
EtcdConf: EtcdConf{
Hosts: []string{"any"},
ID: 0,
},
hasServerID: false,
},
{
EtcdConf: EtcdConf{
Hosts: []string{"any"},
ID: 10000,
},
hasServerID: true,
},
}
for _, test := range tests {
assert.Equal(t, test.hasServerID, test.EtcdConf.HasID())
}
}
func TestEtcdConf_HasTLS(t *testing.T) {
tests := []struct {
name string
conf EtcdConf
want bool
}{
{
name: "empty config",
conf: EtcdConf{},
want: false,
},
{
name: "missing CertFile",
conf: EtcdConf{
CertKeyFile: "key",
CACertFile: "ca",
},
want: false,
},
{
name: "missing CertKeyFile",
conf: EtcdConf{
CertFile: "cert",
CACertFile: "ca",
},
want: false,
},
{
name: "missing CACertFile",
conf: EtcdConf{
CertFile: "cert",
CertKeyFile: "key",
},
want: false,
},
{
name: "valid config",
conf: EtcdConf{
CertFile: "cert",
CertKeyFile: "key",
CACertFile: "ca",
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := tt.conf.HasTLS()
assert.Equal(t, tt.want, got)
})
}
}

View File

@@ -1,12 +1,85 @@
package internal package internal
import ( import (
"os"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/stringx" "github.com/zeromicro/go-zero/core/stringx"
) )
const (
certContent = `-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIUEg9GVO2oaPn+YSmiqmFIuAo10WIwDQYJKoZIhvcNAQEM
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAgFw0yMzAzMTExMzIxMjNaGA8yMTIz
MDIxNTEzMjEyM1owRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBALplXlWsIf0O/IgnIplmiZHKGnxyfyufyE2FBRNk
OofRqbKuPH8GNqbkvZm7N29fwTDAQ+mViAggCkDht4hOzoWJMA7KYJt8JnTSWL48
M1lcrpc9DL2gszC/JF/FGvyANbBtLklkZPFBGdHUX14pjrT937wqPtm+SqUHSvRT
B7bmwmm2drRcmhpVm98LSlV7uQ2EgnJgsLjBPITKUejLmVLHfgX0RwQ2xIpX9pS4
FCe1BTacwl2gGp7Mje7y4Mfv3o0ArJW6Tuwbjx59ZXwb1KIP71b7bT04AVS8ZeYO
UMLKKuB5UR9x9Rn6cLXOTWBpcMVyzDgrAFLZjnE9LPUolZMCAwEAAaNRME8wHwYD
VR0jBBgwFoAUeW8w8pmhncbRgTsl48k4/7wnfx8wCQYDVR0TBAIwADALBgNVHQ8E
BAMCBPAwFAYDVR0RBA0wC4IJbG9jYWxob3N0MA0GCSqGSIb3DQEBDAUAA4IBAQAI
y9xaoS88CLPBsX6mxfcTAFVfGNTRW9VN9Ng1cCnUR+YGoXGM/l+qP4f7p8ocdGwK
iYZErVTzXYIn+D27//wpY3klJk3gAnEUBT3QRkStBw7XnpbeZ2oPBK+cmDnCnZPS
BIF1wxPX7vIgaxs5Zsdqwk3qvZ4Djr2wP7LabNWTLSBKgQoUY45Liw6pffLwcGF9
UKlu54bvGze2SufISCR3ib+I+FLvqpvJhXToZWYb/pfI/HccuCL1oot1x8vx6DQy
U+TYxlZsKS5mdNxAX3dqEkEMsgEi+g/tzDPXJImfeCGGBhIOXLm8SRypiuGdEbc9
xkWYxRPegajuEZGvCqVs
-----END CERTIFICATE-----`
keyContent = `-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAumVeVawh/Q78iCcimWaJkcoafHJ/K5/ITYUFE2Q6h9Gpsq48
fwY2puS9mbs3b1/BMMBD6ZWICCAKQOG3iE7OhYkwDspgm3wmdNJYvjwzWVyulz0M
vaCzML8kX8Ua/IA1sG0uSWRk8UEZ0dRfXimOtP3fvCo+2b5KpQdK9FMHtubCabZ2
tFyaGlWb3wtKVXu5DYSCcmCwuME8hMpR6MuZUsd+BfRHBDbEilf2lLgUJ7UFNpzC
XaAansyN7vLgx+/ejQCslbpO7BuPHn1lfBvUog/vVvttPTgBVLxl5g5Qwsoq4HlR
H3H1Gfpwtc5NYGlwxXLMOCsAUtmOcT0s9SiVkwIDAQABAoIBAD5meTJNMgO55Kjg
ESExxpRcCIno+tHr5+6rvYtEXqPheOIsmmwb9Gfi4+Z3WpOaht5/Pz0Ppj6yGzyl
U//6AgGKb+BDuBvVcDpjwPnOxZIBCSHwejdxeQu0scSuA97MPS0XIAvJ5FEv7ijk
5Bht6SyGYURpECltHygoTNuGgGqmO+McCJRLE9L09lTBI6UQ/JQwWJqSr7wx6iPU
M1Ze/srIV+7cyEPu6i0DGjS1gSQKkX68Lqn1w6oE290O+OZvleO0gZ02fLDWCZke
aeD9+EU/Pw+rqm3H6o0szOFIpzhRp41FUdW9sybB3Yp3u7c/574E+04Z/e30LMKs
TCtE1QECgYEA3K7KIpw0NH2HXL5C3RHcLmr204xeBfS70riBQQuVUgYdmxak2ima
80RInskY8hRhSGTg0l+VYIH8cmjcUyqMSOELS5XfRH99r4QPiK8AguXg80T4VumY
W3Pf+zEC2ssgP/gYthV0g0Xj5m2QxktOF9tRw5nkg739ZR4dI9lm/iECgYEA2Dnf
uwEDGqHiQRF6/fh5BG/nGVMvrefkqx6WvTJQ3k/M/9WhxB+lr/8yH46TuS8N2b29
FoTf3Mr9T7pr/PWkOPzoY3P56nYbKU8xSwCim9xMzhBMzj8/N9ukJvXy27/VOz56
eQaKqnvdXNGtPJrIMDGHps2KKWlKLyAlapzjVTMCgYAA/W++tACv85g13EykfT4F
n0k4LbsGP9DP4zABQLIMyiY72eAncmRVjwrcW36XJ2xATOONTgx3gF3HjZzfaqNy
eD/6uNNllUTVEryXGmHgNHPL45VRnn6memCY2eFvZdXhM5W4y2PYaunY0MkDercA
+GTngbs6tBF88KOk04bYwQKBgFl68cRgsdkmnwwQYNaTKfmVGYzYaQXNzkqmWPko
xmCJo6tHzC7ubdG8iRCYHzfmahPuuj6EdGPZuSRyYFgJi5Ftz/nAN+84OxtIQ3zn
YWOgskQgaLh9YfsKsQ7Sf1NDOsnOnD5TX7UXl07fEpLe9vNCvAFiU8e5Y9LGudU5
4bYTAoGBAMdX3a3bXp4cZvXNBJ/QLVyxC6fP1Q4haCR1Od3m+T00Jth2IX2dk/fl
p6xiJT1av5JtYabv1dFKaXOS5s1kLGGuCCSKpkvFZm826aQ2AFm0XGqEQDLeei5b
A52Kpy/YJ+RkG4BTFtAooFq6DmA0cnoP6oPvG2h6XtDJwDTPInJb
-----END RSA PRIVATE KEY-----`
caContent = `-----BEGIN CERTIFICATE-----
MIIDbTCCAlWgAwIBAgIUBJvFoCowKich7MMfseJ+DYzzirowDQYJKoZIhvcNAQEM
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAgFw0yMzAzMTExMzIxMDNaGA8yMTIz
MDIxNTEzMjEwM1owRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAO4to2YMYj0bxgr2FCiweSTSFuPx33zSw2x/s9Wf
OR41bm2DFsyYT5f3sOIKlXZEdLmOKty2e3ho3yC0EyNpVHdykkkHT3aDI17quZax
kYi/URqqtl1Z08A22txolc04hAZisg2BypGi3vql81UW1t3zyloGnJoIAeXR9uca
ljP6Bk3bwsxoVBLi1JtHrO0hHLQaeHmKhAyrys06X0LRdn7Px48yRZlt6FaLSa8X
YiRM0G44bVy/h6BkoQjMYGwVmCVk6zjJ9U7ZPFqdnDMNxAfR+hjDnYodqdLDMTTR
1NPVrnEnNwFx0AMLvgt/ba/45vZCEAmSZnFXFAJJcM7ai9ECAwEAAaNTMFEwHQYD
VR0OBBYEFHlvMPKZoZ3G0YE7JePJOP+8J38fMB8GA1UdIwQYMBaAFHlvMPKZoZ3G
0YE7JePJOP+8J38fMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggEB
AMX8dNulADOo9uQgBMyFb9TVra7iY0zZjzv4GY5XY7scd52n6CnfAPvYBBDnTr/O
BgNp5jaujb4+9u/2qhV3f9n+/3WOb2CmPehBgVSzlXqHeQ9lshmgwZPeem2T+8Tm
Nnc/xQnsUfCFszUDxpkr55+aLVM22j02RWqcZ4q7TAaVYL+kdFVMc8FoqG/0ro6A
BjE/Qn0Nn7ciX1VUjDt8l+k7ummPJTmzdi6i6E4AwO9dzrGNgGJ4aWL8cC6xYcIX
goVIRTFeONXSDno/oPjWHpIPt7L15heMpKBHNuzPkKx2YVqPHE5QZxWfS+Lzgx+Q
E2oTTM0rYKOZ8p6000mhvKI=
-----END CERTIFICATE-----`
)
func TestAccount(t *testing.T) { func TestAccount(t *testing.T) {
endpoints := []string{ endpoints := []string{
"192.168.0.2:2379", "192.168.0.2:2379",
@@ -32,3 +105,34 @@ func TestAccount(t *testing.T) {
assert.Equal(t, username, account.User) assert.Equal(t, username, account.User)
assert.Equal(t, anotherPassword, account.Pass) assert.Equal(t, anotherPassword, account.Pass)
} }
func TestTLSMethods(t *testing.T) {
certFile := createTempFile(t, []byte(certContent))
defer os.Remove(certFile)
keyFile := createTempFile(t, []byte(keyContent))
defer os.Remove(keyFile)
caFile := createTempFile(t, []byte(caContent))
defer os.Remove(caFile)
assert.NoError(t, AddTLS([]string{"foo"}, certFile, keyFile, caFile, false))
cfg, ok := GetTLS([]string{"foo"})
assert.True(t, ok)
assert.NotNil(t, cfg)
assert.Error(t, AddTLS([]string{"bar"}, "bad-file", keyFile, caFile, false))
assert.Error(t, AddTLS([]string{"bar"}, certFile, keyFile, "bad-file", false))
}
func createTempFile(t *testing.T, body []byte) string {
tmpFile, err := os.CreateTemp(os.TempDir(), "go-unit-*.tmp")
if err != nil {
t.Fatal(err)
}
tmpFile.Close()
if err = os.WriteFile(tmpFile.Name(), body, os.ModePerm); err != nil {
t.Fatal(err)
}
return tmpFile.Name()
}

View File

@@ -81,7 +81,7 @@ func (mr *MockEtcdClientMockRecorder) Ctx() *gomock.Call {
// Get mocks base method // Get mocks base method
func (m *MockEtcdClient) Get(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.GetResponse, error) { func (m *MockEtcdClient) Get(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.GetResponse, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
varargs := []interface{}{ctx, key} varargs := []any{ctx, key}
for _, a := range opts { for _, a := range opts {
varargs = append(varargs, a) varargs = append(varargs, a)
} }
@@ -92,9 +92,9 @@ func (m *MockEtcdClient) Get(ctx context.Context, key string, opts ...clientv3.O
} }
// Get indicates an expected call of Get // Get indicates an expected call of Get
func (mr *MockEtcdClientMockRecorder) Get(ctx, key interface{}, opts ...interface{}) *gomock.Call { func (mr *MockEtcdClientMockRecorder) Get(ctx, key any, opts ...any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{ctx, key}, opts...) varargs := append([]any{ctx, key}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockEtcdClient)(nil).Get), varargs...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockEtcdClient)(nil).Get), varargs...)
} }
@@ -108,7 +108,7 @@ func (m *MockEtcdClient) Grant(ctx context.Context, ttl int64) (*clientv3.LeaseG
} }
// Grant indicates an expected call of Grant // Grant indicates an expected call of Grant
func (mr *MockEtcdClientMockRecorder) Grant(ctx, ttl interface{}) *gomock.Call { func (mr *MockEtcdClientMockRecorder) Grant(ctx, ttl any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Grant", reflect.TypeOf((*MockEtcdClient)(nil).Grant), ctx, ttl) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Grant", reflect.TypeOf((*MockEtcdClient)(nil).Grant), ctx, ttl)
} }
@@ -123,7 +123,7 @@ func (m *MockEtcdClient) KeepAlive(ctx context.Context, id clientv3.LeaseID) (<-
} }
// KeepAlive indicates an expected call of KeepAlive // KeepAlive indicates an expected call of KeepAlive
func (mr *MockEtcdClientMockRecorder) KeepAlive(ctx, id interface{}) *gomock.Call { func (mr *MockEtcdClientMockRecorder) KeepAlive(ctx, id any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "KeepAlive", reflect.TypeOf((*MockEtcdClient)(nil).KeepAlive), ctx, id) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "KeepAlive", reflect.TypeOf((*MockEtcdClient)(nil).KeepAlive), ctx, id)
} }
@@ -131,7 +131,7 @@ func (mr *MockEtcdClientMockRecorder) KeepAlive(ctx, id interface{}) *gomock.Cal
// Put mocks base method // Put mocks base method
func (m *MockEtcdClient) Put(ctx context.Context, key, val string, opts ...clientv3.OpOption) (*clientv3.PutResponse, error) { func (m *MockEtcdClient) Put(ctx context.Context, key, val string, opts ...clientv3.OpOption) (*clientv3.PutResponse, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
varargs := []interface{}{ctx, key, val} varargs := []any{ctx, key, val}
for _, a := range opts { for _, a := range opts {
varargs = append(varargs, a) varargs = append(varargs, a)
} }
@@ -142,9 +142,9 @@ func (m *MockEtcdClient) Put(ctx context.Context, key, val string, opts ...clien
} }
// Put indicates an expected call of Put // Put indicates an expected call of Put
func (mr *MockEtcdClientMockRecorder) Put(ctx, key, val interface{}, opts ...interface{}) *gomock.Call { func (mr *MockEtcdClientMockRecorder) Put(ctx, key, val any, opts ...any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{ctx, key, val}, opts...) varargs := append([]any{ctx, key, val}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Put", reflect.TypeOf((*MockEtcdClient)(nil).Put), varargs...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Put", reflect.TypeOf((*MockEtcdClient)(nil).Put), varargs...)
} }
@@ -158,7 +158,7 @@ func (m *MockEtcdClient) Revoke(ctx context.Context, id clientv3.LeaseID) (*clie
} }
// Revoke indicates an expected call of Revoke // Revoke indicates an expected call of Revoke
func (mr *MockEtcdClientMockRecorder) Revoke(ctx, id interface{}) *gomock.Call { func (mr *MockEtcdClientMockRecorder) Revoke(ctx, id any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Revoke", reflect.TypeOf((*MockEtcdClient)(nil).Revoke), ctx, id) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Revoke", reflect.TypeOf((*MockEtcdClient)(nil).Revoke), ctx, id)
} }
@@ -166,7 +166,7 @@ func (mr *MockEtcdClientMockRecorder) Revoke(ctx, id interface{}) *gomock.Call {
// Watch mocks base method // Watch mocks base method
func (m *MockEtcdClient) Watch(ctx context.Context, key string, opts ...clientv3.OpOption) clientv3.WatchChan { func (m *MockEtcdClient) Watch(ctx context.Context, key string, opts ...clientv3.OpOption) clientv3.WatchChan {
m.ctrl.T.Helper() m.ctrl.T.Helper()
varargs := []interface{}{ctx, key} varargs := []any{ctx, key}
for _, a := range opts { for _, a := range opts {
varargs = append(varargs, a) varargs = append(varargs, a)
} }
@@ -176,8 +176,8 @@ func (m *MockEtcdClient) Watch(ctx context.Context, key string, opts ...clientv3
} }
// Watch indicates an expected call of Watch // Watch indicates an expected call of Watch
func (mr *MockEtcdClientMockRecorder) Watch(ctx, key interface{}, opts ...interface{}) *gomock.Call { func (mr *MockEtcdClientMockRecorder) Watch(ctx, key any, opts ...any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
varargs := append([]interface{}{ctx, key}, opts...) varargs := append([]any{ctx, key}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Watch", reflect.TypeOf((*MockEtcdClient)(nil).Watch), varargs...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Watch", reflect.TypeOf((*MockEtcdClient)(nil).Watch), varargs...)
} }

View File

@@ -2,8 +2,10 @@ package internal
import ( import (
"context" "context"
"os"
"sync" "sync"
"testing" "testing"
"time"
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -14,6 +16,7 @@ import (
"go.etcd.io/etcd/api/v3/etcdserverpb" "go.etcd.io/etcd/api/v3/etcdserverpb"
"go.etcd.io/etcd/api/v3/mvccpb" "go.etcd.io/etcd/api/v3/mvccpb"
clientv3 "go.etcd.io/etcd/client/v3" clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/mock/mockserver"
) )
var mockLock sync.Mutex var mockLock sync.Mutex
@@ -167,7 +170,7 @@ func TestCluster_Watch(t *testing.T) {
assert.Equal(t, "world", kv.Val) assert.Equal(t, "world", kv.Val)
wg.Done() wg.Done()
}).MaxTimes(1) }).MaxTimes(1)
listener.EXPECT().OnDelete(gomock.Any()).Do(func(_ interface{}) { listener.EXPECT().OnDelete(gomock.Any()).Do(func(_ any) {
wg.Done() wg.Done()
}).MaxTimes(1) }).MaxTimes(1)
go c.watch(cli, "any", 0) go c.watch(cli, "any", 0)
@@ -242,3 +245,58 @@ func TestValueOnlyContext(t *testing.T) {
ctx.Done() ctx.Done()
assert.Nil(t, ctx.Err()) assert.Nil(t, ctx.Err())
} }
func TestDialClient(t *testing.T) {
svr, err := mockserver.StartMockServers(1)
assert.NoError(t, err)
svr.StartAt(0)
certFile := createTempFile(t, []byte(certContent))
defer os.Remove(certFile)
keyFile := createTempFile(t, []byte(keyContent))
defer os.Remove(keyFile)
caFile := createTempFile(t, []byte(caContent))
defer os.Remove(caFile)
endpoints := []string{svr.Servers[0].Address}
AddAccount(endpoints, "foo", "bar")
assert.NoError(t, AddTLS(endpoints, certFile, keyFile, caFile, false))
old := DialTimeout
DialTimeout = time.Millisecond
defer func() {
DialTimeout = old
}()
_, err = DialClient(endpoints)
assert.Error(t, err)
}
func TestRegistry_Monitor(t *testing.T) {
svr, err := mockserver.StartMockServers(1)
assert.NoError(t, err)
svr.StartAt(0)
endpoints := []string{svr.Servers[0].Address}
GetRegistry().lock.Lock()
GetRegistry().clusters = map[string]*cluster{
getClusterKey(endpoints): {
listeners: map[string][]UpdateListener{},
values: map[string]map[string]string{
"foo": {
"bar": "baz",
},
},
},
}
GetRegistry().lock.Unlock()
assert.Error(t, GetRegistry().Monitor(endpoints, "foo", new(mockListener)))
}
type mockListener struct {
}
func (m *mockListener) OnAdd(_ KV) {
}
func (m *mockListener) OnDelete(_ KV) {
}

View File

@@ -58,7 +58,7 @@ func (m *MocketcdConn) WaitForStateChange(ctx context.Context, sourceState conne
} }
// WaitForStateChange indicates an expected call of WaitForStateChange // WaitForStateChange indicates an expected call of WaitForStateChange
func (mr *MocketcdConnMockRecorder) WaitForStateChange(ctx, sourceState interface{}) *gomock.Call { func (mr *MocketcdConnMockRecorder) WaitForStateChange(ctx, sourceState any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForStateChange", reflect.TypeOf((*MocketcdConn)(nil).WaitForStateChange), ctx, sourceState) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForStateChange", reflect.TypeOf((*MocketcdConn)(nil).WaitForStateChange), ctx, sourceState)
} }

View File

@@ -40,7 +40,7 @@ func (m *MockUpdateListener) OnAdd(kv KV) {
} }
// OnAdd indicates an expected call of OnAdd // OnAdd indicates an expected call of OnAdd
func (mr *MockUpdateListenerMockRecorder) OnAdd(kv interface{}) *gomock.Call { func (mr *MockUpdateListenerMockRecorder) OnAdd(kv any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnAdd", reflect.TypeOf((*MockUpdateListener)(nil).OnAdd), kv) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnAdd", reflect.TypeOf((*MockUpdateListener)(nil).OnAdd), kv)
} }
@@ -52,7 +52,7 @@ func (m *MockUpdateListener) OnDelete(kv KV) {
} }
// OnDelete indicates an expected call of OnDelete // OnDelete indicates an expected call of OnDelete
func (mr *MockUpdateListenerMockRecorder) OnDelete(kv interface{}) *gomock.Call { func (mr *MockUpdateListenerMockRecorder) OnDelete(kv any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnDelete", reflect.TypeOf((*MockUpdateListener)(nil).OnDelete), kv) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnDelete", reflect.TypeOf((*MockUpdateListener)(nil).OnDelete), kv)
} }

View File

@@ -1,7 +1,10 @@
package discov package discov
import ( import (
"context"
"errors" "errors"
"net"
"os"
"sync" "sync"
"testing" "testing"
"time" "time"
@@ -13,6 +16,83 @@ import (
"github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stringx" "github.com/zeromicro/go-zero/core/stringx"
clientv3 "go.etcd.io/etcd/client/v3" clientv3 "go.etcd.io/etcd/client/v3"
"golang.org/x/net/http2"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/resolver"
"google.golang.org/grpc/resolver/manual"
)
const (
certContent = `-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIUEg9GVO2oaPn+YSmiqmFIuAo10WIwDQYJKoZIhvcNAQEM
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAgFw0yMzAzMTExMzIxMjNaGA8yMTIz
MDIxNTEzMjEyM1owRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBALplXlWsIf0O/IgnIplmiZHKGnxyfyufyE2FBRNk
OofRqbKuPH8GNqbkvZm7N29fwTDAQ+mViAggCkDht4hOzoWJMA7KYJt8JnTSWL48
M1lcrpc9DL2gszC/JF/FGvyANbBtLklkZPFBGdHUX14pjrT937wqPtm+SqUHSvRT
B7bmwmm2drRcmhpVm98LSlV7uQ2EgnJgsLjBPITKUejLmVLHfgX0RwQ2xIpX9pS4
FCe1BTacwl2gGp7Mje7y4Mfv3o0ArJW6Tuwbjx59ZXwb1KIP71b7bT04AVS8ZeYO
UMLKKuB5UR9x9Rn6cLXOTWBpcMVyzDgrAFLZjnE9LPUolZMCAwEAAaNRME8wHwYD
VR0jBBgwFoAUeW8w8pmhncbRgTsl48k4/7wnfx8wCQYDVR0TBAIwADALBgNVHQ8E
BAMCBPAwFAYDVR0RBA0wC4IJbG9jYWxob3N0MA0GCSqGSIb3DQEBDAUAA4IBAQAI
y9xaoS88CLPBsX6mxfcTAFVfGNTRW9VN9Ng1cCnUR+YGoXGM/l+qP4f7p8ocdGwK
iYZErVTzXYIn+D27//wpY3klJk3gAnEUBT3QRkStBw7XnpbeZ2oPBK+cmDnCnZPS
BIF1wxPX7vIgaxs5Zsdqwk3qvZ4Djr2wP7LabNWTLSBKgQoUY45Liw6pffLwcGF9
UKlu54bvGze2SufISCR3ib+I+FLvqpvJhXToZWYb/pfI/HccuCL1oot1x8vx6DQy
U+TYxlZsKS5mdNxAX3dqEkEMsgEi+g/tzDPXJImfeCGGBhIOXLm8SRypiuGdEbc9
xkWYxRPegajuEZGvCqVs
-----END CERTIFICATE-----`
keyContent = `-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAumVeVawh/Q78iCcimWaJkcoafHJ/K5/ITYUFE2Q6h9Gpsq48
fwY2puS9mbs3b1/BMMBD6ZWICCAKQOG3iE7OhYkwDspgm3wmdNJYvjwzWVyulz0M
vaCzML8kX8Ua/IA1sG0uSWRk8UEZ0dRfXimOtP3fvCo+2b5KpQdK9FMHtubCabZ2
tFyaGlWb3wtKVXu5DYSCcmCwuME8hMpR6MuZUsd+BfRHBDbEilf2lLgUJ7UFNpzC
XaAansyN7vLgx+/ejQCslbpO7BuPHn1lfBvUog/vVvttPTgBVLxl5g5Qwsoq4HlR
H3H1Gfpwtc5NYGlwxXLMOCsAUtmOcT0s9SiVkwIDAQABAoIBAD5meTJNMgO55Kjg
ESExxpRcCIno+tHr5+6rvYtEXqPheOIsmmwb9Gfi4+Z3WpOaht5/Pz0Ppj6yGzyl
U//6AgGKb+BDuBvVcDpjwPnOxZIBCSHwejdxeQu0scSuA97MPS0XIAvJ5FEv7ijk
5Bht6SyGYURpECltHygoTNuGgGqmO+McCJRLE9L09lTBI6UQ/JQwWJqSr7wx6iPU
M1Ze/srIV+7cyEPu6i0DGjS1gSQKkX68Lqn1w6oE290O+OZvleO0gZ02fLDWCZke
aeD9+EU/Pw+rqm3H6o0szOFIpzhRp41FUdW9sybB3Yp3u7c/574E+04Z/e30LMKs
TCtE1QECgYEA3K7KIpw0NH2HXL5C3RHcLmr204xeBfS70riBQQuVUgYdmxak2ima
80RInskY8hRhSGTg0l+VYIH8cmjcUyqMSOELS5XfRH99r4QPiK8AguXg80T4VumY
W3Pf+zEC2ssgP/gYthV0g0Xj5m2QxktOF9tRw5nkg739ZR4dI9lm/iECgYEA2Dnf
uwEDGqHiQRF6/fh5BG/nGVMvrefkqx6WvTJQ3k/M/9WhxB+lr/8yH46TuS8N2b29
FoTf3Mr9T7pr/PWkOPzoY3P56nYbKU8xSwCim9xMzhBMzj8/N9ukJvXy27/VOz56
eQaKqnvdXNGtPJrIMDGHps2KKWlKLyAlapzjVTMCgYAA/W++tACv85g13EykfT4F
n0k4LbsGP9DP4zABQLIMyiY72eAncmRVjwrcW36XJ2xATOONTgx3gF3HjZzfaqNy
eD/6uNNllUTVEryXGmHgNHPL45VRnn6memCY2eFvZdXhM5W4y2PYaunY0MkDercA
+GTngbs6tBF88KOk04bYwQKBgFl68cRgsdkmnwwQYNaTKfmVGYzYaQXNzkqmWPko
xmCJo6tHzC7ubdG8iRCYHzfmahPuuj6EdGPZuSRyYFgJi5Ftz/nAN+84OxtIQ3zn
YWOgskQgaLh9YfsKsQ7Sf1NDOsnOnD5TX7UXl07fEpLe9vNCvAFiU8e5Y9LGudU5
4bYTAoGBAMdX3a3bXp4cZvXNBJ/QLVyxC6fP1Q4haCR1Od3m+T00Jth2IX2dk/fl
p6xiJT1av5JtYabv1dFKaXOS5s1kLGGuCCSKpkvFZm826aQ2AFm0XGqEQDLeei5b
A52Kpy/YJ+RkG4BTFtAooFq6DmA0cnoP6oPvG2h6XtDJwDTPInJb
-----END RSA PRIVATE KEY-----`
caContent = `-----BEGIN CERTIFICATE-----
MIIDbTCCAlWgAwIBAgIUBJvFoCowKich7MMfseJ+DYzzirowDQYJKoZIhvcNAQEM
BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAgFw0yMzAzMTExMzIxMDNaGA8yMTIz
MDIxNTEzMjEwM1owRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAO4to2YMYj0bxgr2FCiweSTSFuPx33zSw2x/s9Wf
OR41bm2DFsyYT5f3sOIKlXZEdLmOKty2e3ho3yC0EyNpVHdykkkHT3aDI17quZax
kYi/URqqtl1Z08A22txolc04hAZisg2BypGi3vql81UW1t3zyloGnJoIAeXR9uca
ljP6Bk3bwsxoVBLi1JtHrO0hHLQaeHmKhAyrys06X0LRdn7Px48yRZlt6FaLSa8X
YiRM0G44bVy/h6BkoQjMYGwVmCVk6zjJ9U7ZPFqdnDMNxAfR+hjDnYodqdLDMTTR
1NPVrnEnNwFx0AMLvgt/ba/45vZCEAmSZnFXFAJJcM7ai9ECAwEAAaNTMFEwHQYD
VR0OBBYEFHlvMPKZoZ3G0YE7JePJOP+8J38fMB8GA1UdIwQYMBaAFHlvMPKZoZ3G
0YE7JePJOP+8J38fMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggEB
AMX8dNulADOo9uQgBMyFb9TVra7iY0zZjzv4GY5XY7scd52n6CnfAPvYBBDnTr/O
BgNp5jaujb4+9u/2qhV3f9n+/3WOb2CmPehBgVSzlXqHeQ9lshmgwZPeem2T+8Tm
Nnc/xQnsUfCFszUDxpkr55+aLVM22j02RWqcZ4q7TAaVYL+kdFVMc8FoqG/0ro6A
BjE/Qn0Nn7ciX1VUjDt8l+k7ummPJTmzdi6i6E4AwO9dzrGNgGJ4aWL8cC6xYcIX
goVIRTFeONXSDno/oPjWHpIPt7L15heMpKBHNuzPkKx2YVqPHE5QZxWfS+Lzgx+Q
E2oTTM0rYKOZ8p6000mhvKI=
-----END CERTIFICATE-----`
) )
func init() { func init() {
@@ -37,7 +117,7 @@ func TestPublisher_register(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
} }
func TestPublisher_registerWithId(t *testing.T) { func TestPublisher_registerWithOptions(t *testing.T) {
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
defer ctrl.Finish() defer ctrl.Finish()
const id = 2 const id = 2
@@ -49,7 +129,15 @@ func TestPublisher_registerWithId(t *testing.T) {
ID: 1, ID: 1,
}, nil) }, nil)
cli.EXPECT().Put(gomock.Any(), makeEtcdKey("thekey", id), "thevalue", gomock.Any()) cli.EXPECT().Put(gomock.Any(), makeEtcdKey("thekey", id), "thevalue", gomock.Any())
pub := NewPublisher(nil, "thekey", "thevalue", WithId(id))
certFile := createTempFile(t, []byte(certContent))
defer os.Remove(certFile)
keyFile := createTempFile(t, []byte(keyContent))
defer os.Remove(keyFile)
caFile := createTempFile(t, []byte(caContent))
defer os.Remove(caFile)
pub := NewPublisher(nil, "thekey", "thevalue", WithId(id),
WithPubEtcdTLS(certFile, keyFile, caFile, true))
_, err := pub.register(cli) _, err := pub.register(cli)
assert.Nil(t, err) assert.Nil(t, err)
} }
@@ -125,7 +213,7 @@ func TestPublisher_keepAliveAsyncQuit(t *testing.T) {
cli.EXPECT().KeepAlive(gomock.Any(), id) cli.EXPECT().KeepAlive(gomock.Any(), id)
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(1) wg.Add(1)
cli.EXPECT().Revoke(gomock.Any(), id).Do(func(_, _ interface{}) { cli.EXPECT().Revoke(gomock.Any(), id).Do(func(_, _ any) {
wg.Done() wg.Done()
}) })
pub := NewPublisher(nil, "thekey", "thevalue") pub := NewPublisher(nil, "thekey", "thevalue")
@@ -147,7 +235,7 @@ func TestPublisher_keepAliveAsyncPause(t *testing.T) {
pub := NewPublisher(nil, "thekey", "thevalue") pub := NewPublisher(nil, "thekey", "thevalue")
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(1) wg.Add(1)
cli.EXPECT().Revoke(gomock.Any(), id).Do(func(_, _ interface{}) { cli.EXPECT().Revoke(gomock.Any(), id).Do(func(_, _ any) {
pub.Stop() pub.Stop()
wg.Done() wg.Done()
}) })
@@ -169,3 +257,92 @@ func TestPublisher_Resume(t *testing.T) {
}() }()
<-publisher.resumeChan <-publisher.resumeChan
} }
func TestPublisher_keepAliveAsync(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
const id clientv3.LeaseID = 1
conn := createMockConn(t)
defer conn.Close()
cli := internal.NewMockEtcdClient(ctrl)
cli.EXPECT().ActiveConnection().Return(conn).AnyTimes()
cli.EXPECT().Close()
defer cli.Close()
cli.ActiveConnection()
restore := setMockClient(cli)
defer restore()
cli.EXPECT().Ctx().AnyTimes()
cli.EXPECT().KeepAlive(gomock.Any(), id)
cli.EXPECT().Grant(gomock.Any(), timeToLive).Return(&clientv3.LeaseGrantResponse{
ID: 1,
}, nil)
cli.EXPECT().Put(gomock.Any(), makeEtcdKey("thekey", int64(id)), "thevalue", gomock.Any())
var wg sync.WaitGroup
wg.Add(1)
cli.EXPECT().Revoke(gomock.Any(), id).Do(func(_, _ any) {
wg.Done()
})
pub := NewPublisher([]string{"the-endpoint"}, "thekey", "thevalue")
pub.lease = id
assert.Nil(t, pub.KeepAlive())
pub.Stop()
wg.Wait()
}
func createMockConn(t *testing.T) *grpc.ClientConn {
lis, err := net.Listen("tcp", "localhost:0")
if err != nil {
t.Fatalf("Error while listening. Err: %v", err)
}
defer lis.Close()
lisAddr := resolver.Address{Addr: lis.Addr().String()}
lisDone := make(chan struct{})
dialDone := make(chan struct{})
// 1st listener accepts the connection and then does nothing
go func() {
defer close(lisDone)
conn, err := lis.Accept()
if err != nil {
t.Errorf("Error while accepting. Err: %v", err)
return
}
framer := http2.NewFramer(conn, conn)
if err := framer.WriteSettings(http2.Setting{}); err != nil {
t.Errorf("Error while writing settings. Err: %v", err)
return
}
<-dialDone // Close conn only after dial returns.
}()
r := manual.NewBuilderWithScheme("whatever")
r.InitialState(resolver.State{Addresses: []resolver.Address{lisAddr}})
client, err := grpc.DialContext(context.Background(), r.Scheme()+":///test.server",
grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(r))
close(dialDone)
if err != nil {
t.Fatalf("Dial failed. Err: %v", err)
}
timeout := time.After(1 * time.Second)
select {
case <-timeout:
t.Fatal("timed out waiting for server to finish")
case <-lisDone:
}
return client
}
func createTempFile(t *testing.T, body []byte) string {
tmpFile, err := os.CreateTemp(os.TempDir(), "go-unit-*.tmp")
if err != nil {
t.Fatal(err)
}
tmpFile.Close()
if err = os.WriteFile(tmpFile.Name(), body, os.ModePerm); err != nil {
t.Fatal(err)
}
return tmpFile.Name()
}

View File

@@ -12,7 +12,7 @@ func Wrap(err error, message string) error {
} }
// Wrapf returns an error that wraps err with given format and args. // Wrapf returns an error that wraps err with given format and args.
func Wrapf(err error, format string, args ...interface{}) error { func Wrapf(err error, format string, args ...any) error {
if err == nil { if err == nil {
return nil return nil
} }

View File

@@ -42,7 +42,7 @@ func NewBulkExecutor(execute Execute, opts ...BulkOption) *BulkExecutor {
} }
// Add adds task into be. // Add adds task into be.
func (be *BulkExecutor) Add(task interface{}) error { func (be *BulkExecutor) Add(task any) error {
be.executor.Add(task) be.executor.Add(task)
return nil return nil
} }
@@ -79,22 +79,22 @@ func newBulkOptions() bulkOptions {
} }
type bulkContainer struct { type bulkContainer struct {
tasks []interface{} tasks []any
execute Execute execute Execute
maxTasks int maxTasks int
} }
func (bc *bulkContainer) AddTask(task interface{}) bool { func (bc *bulkContainer) AddTask(task any) bool {
bc.tasks = append(bc.tasks, task) bc.tasks = append(bc.tasks, task)
return len(bc.tasks) >= bc.maxTasks return len(bc.tasks) >= bc.maxTasks
} }
func (bc *bulkContainer) Execute(tasks interface{}) { func (bc *bulkContainer) Execute(tasks any) {
vals := tasks.([]interface{}) vals := tasks.([]any)
bc.execute(vals) bc.execute(vals)
} }
func (bc *bulkContainer) RemoveAll() interface{} { func (bc *bulkContainer) RemoveAll() any {
tasks := bc.tasks tasks := bc.tasks
bc.tasks = nil bc.tasks = nil
return tasks return tasks

View File

@@ -12,7 +12,7 @@ func TestBulkExecutor(t *testing.T) {
var values []int var values []int
var lock sync.Mutex var lock sync.Mutex
executor := NewBulkExecutor(func(items []interface{}) { executor := NewBulkExecutor(func(items []any) {
lock.Lock() lock.Lock()
values = append(values, len(items)) values = append(values, len(items))
lock.Unlock() lock.Unlock()
@@ -40,7 +40,7 @@ func TestBulkExecutorFlushInterval(t *testing.T) {
var wait sync.WaitGroup var wait sync.WaitGroup
wait.Add(1) wait.Add(1)
executor := NewBulkExecutor(func(items []interface{}) { executor := NewBulkExecutor(func(items []any) {
assert.Equal(t, size, len(items)) assert.Equal(t, size, len(items))
wait.Done() wait.Done()
}, WithBulkTasks(caches), WithBulkInterval(time.Millisecond*100)) }, WithBulkTasks(caches), WithBulkInterval(time.Millisecond*100))
@@ -53,7 +53,7 @@ func TestBulkExecutorFlushInterval(t *testing.T) {
} }
func TestBulkExecutorEmpty(t *testing.T) { func TestBulkExecutorEmpty(t *testing.T) {
NewBulkExecutor(func(items []interface{}) { NewBulkExecutor(func(items []any) {
assert.Fail(t, "should not called") assert.Fail(t, "should not called")
}, WithBulkTasks(10), WithBulkInterval(time.Millisecond)) }, WithBulkTasks(10), WithBulkInterval(time.Millisecond))
time.Sleep(time.Millisecond * 100) time.Sleep(time.Millisecond * 100)
@@ -67,7 +67,7 @@ func TestBulkExecutorFlush(t *testing.T) {
var wait sync.WaitGroup var wait sync.WaitGroup
wait.Add(1) wait.Add(1)
be := NewBulkExecutor(func(items []interface{}) { be := NewBulkExecutor(func(items []any) {
assert.Equal(t, tasks, len(items)) assert.Equal(t, tasks, len(items))
wait.Done() wait.Done()
}, WithBulkTasks(caches), WithBulkInterval(time.Minute)) }, WithBulkTasks(caches), WithBulkInterval(time.Minute))
@@ -81,8 +81,8 @@ func TestBulkExecutorFlush(t *testing.T) {
func TestBuldExecutorFlushSlowTasks(t *testing.T) { func TestBuldExecutorFlushSlowTasks(t *testing.T) {
const total = 1500 const total = 1500
lock := new(sync.Mutex) lock := new(sync.Mutex)
result := make([]interface{}, 0, 10000) result := make([]any, 0, 10000)
exec := NewBulkExecutor(func(tasks []interface{}) { exec := NewBulkExecutor(func(tasks []any) {
time.Sleep(time.Millisecond * 100) time.Sleep(time.Millisecond * 100)
lock.Lock() lock.Lock()
defer lock.Unlock() defer lock.Unlock()
@@ -100,7 +100,7 @@ func TestBuldExecutorFlushSlowTasks(t *testing.T) {
func BenchmarkBulkExecutor(b *testing.B) { func BenchmarkBulkExecutor(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
be := NewBulkExecutor(func(tasks []interface{}) { be := NewBulkExecutor(func(tasks []any) {
time.Sleep(time.Millisecond * time.Duration(len(tasks))) time.Sleep(time.Millisecond * time.Duration(len(tasks)))
}) })
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {

View File

@@ -42,7 +42,7 @@ func NewChunkExecutor(execute Execute, opts ...ChunkOption) *ChunkExecutor {
} }
// Add adds task with given chunk size into ce. // Add adds task with given chunk size into ce.
func (ce *ChunkExecutor) Add(task interface{}, size int) error { func (ce *ChunkExecutor) Add(task any, size int) error {
ce.executor.Add(chunk{ ce.executor.Add(chunk{
val: task, val: task,
size: size, size: size,
@@ -82,25 +82,25 @@ func newChunkOptions() chunkOptions {
} }
type chunkContainer struct { type chunkContainer struct {
tasks []interface{} tasks []any
execute Execute execute Execute
size int size int
maxChunkSize int maxChunkSize int
} }
func (bc *chunkContainer) AddTask(task interface{}) bool { func (bc *chunkContainer) AddTask(task any) bool {
ck := task.(chunk) ck := task.(chunk)
bc.tasks = append(bc.tasks, ck.val) bc.tasks = append(bc.tasks, ck.val)
bc.size += ck.size bc.size += ck.size
return bc.size >= bc.maxChunkSize return bc.size >= bc.maxChunkSize
} }
func (bc *chunkContainer) Execute(tasks interface{}) { func (bc *chunkContainer) Execute(tasks any) {
vals := tasks.([]interface{}) vals := tasks.([]any)
bc.execute(vals) bc.execute(vals)
} }
func (bc *chunkContainer) RemoveAll() interface{} { func (bc *chunkContainer) RemoveAll() any {
tasks := bc.tasks tasks := bc.tasks
bc.tasks = nil bc.tasks = nil
bc.size = 0 bc.size = 0
@@ -108,6 +108,6 @@ func (bc *chunkContainer) RemoveAll() interface{} {
} }
type chunk struct { type chunk struct {
val interface{} val any
size int size int
} }

View File

@@ -12,7 +12,7 @@ func TestChunkExecutor(t *testing.T) {
var values []int var values []int
var lock sync.Mutex var lock sync.Mutex
executor := NewChunkExecutor(func(items []interface{}) { executor := NewChunkExecutor(func(items []any) {
lock.Lock() lock.Lock()
values = append(values, len(items)) values = append(values, len(items))
lock.Unlock() lock.Unlock()
@@ -40,7 +40,7 @@ func TestChunkExecutorFlushInterval(t *testing.T) {
var wait sync.WaitGroup var wait sync.WaitGroup
wait.Add(1) wait.Add(1)
executor := NewChunkExecutor(func(items []interface{}) { executor := NewChunkExecutor(func(items []any) {
assert.Equal(t, size, len(items)) assert.Equal(t, size, len(items))
wait.Done() wait.Done()
}, WithChunkBytes(caches), WithFlushInterval(time.Millisecond*100)) }, WithChunkBytes(caches), WithFlushInterval(time.Millisecond*100))
@@ -53,10 +53,11 @@ func TestChunkExecutorFlushInterval(t *testing.T) {
} }
func TestChunkExecutorEmpty(t *testing.T) { func TestChunkExecutorEmpty(t *testing.T) {
NewChunkExecutor(func(items []interface{}) { executor := NewChunkExecutor(func(items []any) {
assert.Fail(t, "should not called") assert.Fail(t, "should not called")
}, WithChunkBytes(10), WithFlushInterval(time.Millisecond)) }, WithChunkBytes(10), WithFlushInterval(time.Millisecond))
time.Sleep(time.Millisecond * 100) time.Sleep(time.Millisecond * 100)
executor.Wait()
} }
func TestChunkExecutorFlush(t *testing.T) { func TestChunkExecutorFlush(t *testing.T) {
@@ -67,7 +68,7 @@ func TestChunkExecutorFlush(t *testing.T) {
var wait sync.WaitGroup var wait sync.WaitGroup
wait.Add(1) wait.Add(1)
be := NewChunkExecutor(func(items []interface{}) { be := NewChunkExecutor(func(items []any) {
assert.Equal(t, tasks, len(items)) assert.Equal(t, tasks, len(items))
wait.Done() wait.Done()
}, WithChunkBytes(caches), WithFlushInterval(time.Minute)) }, WithChunkBytes(caches), WithFlushInterval(time.Minute))
@@ -81,7 +82,7 @@ func TestChunkExecutorFlush(t *testing.T) {
func BenchmarkChunkExecutor(b *testing.B) { func BenchmarkChunkExecutor(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
be := NewChunkExecutor(func(tasks []interface{}) { be := NewChunkExecutor(func(tasks []any) {
time.Sleep(time.Millisecond * time.Duration(len(tasks))) time.Sleep(time.Millisecond * time.Duration(len(tasks)))
}) })
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {

View File

@@ -21,16 +21,16 @@ type (
TaskContainer interface { TaskContainer interface {
// AddTask adds the task into the container. // AddTask adds the task into the container.
// Returns true if the container needs to be flushed after the addition. // Returns true if the container needs to be flushed after the addition.
AddTask(task interface{}) bool AddTask(task any) bool
// Execute handles the collected tasks by the container when flushing. // Execute handles the collected tasks by the container when flushing.
Execute(tasks interface{}) Execute(tasks any)
// RemoveAll removes the contained tasks, and return them. // RemoveAll removes the contained tasks, and return them.
RemoveAll() interface{} RemoveAll() any
} }
// A PeriodicalExecutor is an executor that periodically execute tasks. // A PeriodicalExecutor is an executor that periodically execute tasks.
PeriodicalExecutor struct { PeriodicalExecutor struct {
commander chan interface{} commander chan any
interval time.Duration interval time.Duration
container TaskContainer container TaskContainer
waitGroup sync.WaitGroup waitGroup sync.WaitGroup
@@ -48,7 +48,7 @@ type (
func NewPeriodicalExecutor(interval time.Duration, container TaskContainer) *PeriodicalExecutor { func NewPeriodicalExecutor(interval time.Duration, container TaskContainer) *PeriodicalExecutor {
executor := &PeriodicalExecutor{ executor := &PeriodicalExecutor{
// buffer 1 to let the caller go quickly // buffer 1 to let the caller go quickly
commander: make(chan interface{}, 1), commander: make(chan any, 1),
interval: interval, interval: interval,
container: container, container: container,
confirmChan: make(chan lang.PlaceholderType), confirmChan: make(chan lang.PlaceholderType),
@@ -64,7 +64,7 @@ func NewPeriodicalExecutor(interval time.Duration, container TaskContainer) *Per
} }
// Add adds tasks into pe. // Add adds tasks into pe.
func (pe *PeriodicalExecutor) Add(task interface{}) { func (pe *PeriodicalExecutor) Add(task any) {
if vals, ok := pe.addAndCheck(task); ok { if vals, ok := pe.addAndCheck(task); ok {
pe.commander <- vals pe.commander <- vals
<-pe.confirmChan <-pe.confirmChan
@@ -74,14 +74,14 @@ func (pe *PeriodicalExecutor) Add(task interface{}) {
// Flush forces pe to execute tasks. // Flush forces pe to execute tasks.
func (pe *PeriodicalExecutor) Flush() bool { func (pe *PeriodicalExecutor) Flush() bool {
pe.enterExecution() pe.enterExecution()
return pe.executeTasks(func() interface{} { return pe.executeTasks(func() any {
pe.lock.Lock() pe.lock.Lock()
defer pe.lock.Unlock() defer pe.lock.Unlock()
return pe.container.RemoveAll() return pe.container.RemoveAll()
}()) }())
} }
// Sync lets caller to run fn thread-safe with pe, especially for the underlying container. // Sync lets caller run fn thread-safe with pe, especially for the underlying container.
func (pe *PeriodicalExecutor) Sync(fn func()) { func (pe *PeriodicalExecutor) Sync(fn func()) {
pe.lock.Lock() pe.lock.Lock()
defer pe.lock.Unlock() defer pe.lock.Unlock()
@@ -96,7 +96,7 @@ func (pe *PeriodicalExecutor) Wait() {
}) })
} }
func (pe *PeriodicalExecutor) addAndCheck(task interface{}) (interface{}, bool) { func (pe *PeriodicalExecutor) addAndCheck(task any) (any, bool) {
pe.lock.Lock() pe.lock.Lock()
defer func() { defer func() {
if !pe.guarded { if !pe.guarded {
@@ -116,7 +116,7 @@ func (pe *PeriodicalExecutor) addAndCheck(task interface{}) (interface{}, bool)
} }
func (pe *PeriodicalExecutor) backgroundFlush() { func (pe *PeriodicalExecutor) backgroundFlush() {
threading.GoSafe(func() { go func() {
// flush before quit goroutine to avoid missing tasks // flush before quit goroutine to avoid missing tasks
defer pe.Flush() defer pe.Flush()
@@ -144,7 +144,7 @@ func (pe *PeriodicalExecutor) backgroundFlush() {
} }
} }
} }
}) }()
} }
func (pe *PeriodicalExecutor) doneExecution() { func (pe *PeriodicalExecutor) doneExecution() {
@@ -157,18 +157,20 @@ func (pe *PeriodicalExecutor) enterExecution() {
}) })
} }
func (pe *PeriodicalExecutor) executeTasks(tasks interface{}) bool { func (pe *PeriodicalExecutor) executeTasks(tasks any) bool {
defer pe.doneExecution() defer pe.doneExecution()
ok := pe.hasTasks(tasks) ok := pe.hasTasks(tasks)
if ok { if ok {
pe.container.Execute(tasks) threading.RunSafe(func() {
pe.container.Execute(tasks)
})
} }
return ok return ok
} }
func (pe *PeriodicalExecutor) hasTasks(tasks interface{}) bool { func (pe *PeriodicalExecutor) hasTasks(tasks any) bool {
if tasks == nil { if tasks == nil {
return false return false
} }

View File

@@ -8,6 +8,7 @@ import (
"time" "time"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/proc"
"github.com/zeromicro/go-zero/core/timex" "github.com/zeromicro/go-zero/core/timex"
) )
@@ -16,22 +17,22 @@ const threshold = 10
type container struct { type container struct {
interval time.Duration interval time.Duration
tasks []int tasks []int
execute func(tasks interface{}) execute func(tasks any)
} }
func newContainer(interval time.Duration, execute func(tasks interface{})) *container { func newContainer(interval time.Duration, execute func(tasks any)) *container {
return &container{ return &container{
interval: interval, interval: interval,
execute: execute, execute: execute,
} }
} }
func (c *container) AddTask(task interface{}) bool { func (c *container) AddTask(task any) bool {
c.tasks = append(c.tasks, task.(int)) c.tasks = append(c.tasks, task.(int))
return len(c.tasks) > threshold return len(c.tasks) > threshold
} }
func (c *container) Execute(tasks interface{}) { func (c *container) Execute(tasks any) {
if c.execute != nil { if c.execute != nil {
c.execute(tasks) c.execute(tasks)
} else { } else {
@@ -39,7 +40,7 @@ func (c *container) Execute(tasks interface{}) {
} }
} }
func (c *container) RemoveAll() interface{} { func (c *container) RemoveAll() any {
tasks := c.tasks tasks := c.tasks
c.tasks = nil c.tasks = nil
return tasks return tasks
@@ -67,6 +68,7 @@ func TestPeriodicalExecutor_QuitGoroutine(t *testing.T) {
ticker.Tick() ticker.Tick()
ticker.Wait(time.Millisecond * idleRound) ticker.Wait(time.Millisecond * idleRound)
assert.Equal(t, routines, runtime.NumGoroutine()) assert.Equal(t, routines, runtime.NumGoroutine())
proc.Shutdown()
} }
func TestPeriodicalExecutor_Bulk(t *testing.T) { func TestPeriodicalExecutor_Bulk(t *testing.T) {
@@ -74,7 +76,7 @@ func TestPeriodicalExecutor_Bulk(t *testing.T) {
var vals []int var vals []int
// avoid data race // avoid data race
var lock sync.Mutex var lock sync.Mutex
exec := NewPeriodicalExecutor(time.Millisecond, newContainer(time.Millisecond, func(tasks interface{}) { exec := NewPeriodicalExecutor(time.Millisecond, newContainer(time.Millisecond, func(tasks any) {
t := tasks.([]int) t := tasks.([]int)
for _, each := range t { for _, each := range t {
lock.Lock() lock.Lock()
@@ -106,9 +108,67 @@ func TestPeriodicalExecutor_Bulk(t *testing.T) {
lock.Unlock() lock.Unlock()
} }
func TestPeriodicalExecutor_Panic(t *testing.T) {
// avoid data race
var lock sync.Mutex
ticker := timex.NewFakeTicker()
var (
executedTasks []int
expected []int
)
executor := NewPeriodicalExecutor(time.Millisecond, newContainer(time.Millisecond, func(tasks any) {
tt := tasks.([]int)
lock.Lock()
executedTasks = append(executedTasks, tt...)
lock.Unlock()
if tt[0] == 0 {
panic("test")
}
}))
executor.newTicker = func(duration time.Duration) timex.Ticker {
return ticker
}
for i := 0; i < 30; i++ {
executor.Add(i)
expected = append(expected, i)
}
ticker.Tick()
ticker.Tick()
time.Sleep(time.Millisecond)
lock.Lock()
assert.Equal(t, expected, executedTasks)
lock.Unlock()
}
func TestPeriodicalExecutor_FlushPanic(t *testing.T) {
var (
executedTasks []int
expected []int
lock sync.Mutex
)
executor := NewPeriodicalExecutor(time.Millisecond, newContainer(time.Millisecond, func(tasks any) {
tt := tasks.([]int)
lock.Lock()
executedTasks = append(executedTasks, tt...)
lock.Unlock()
if tt[0] == 0 {
panic("flush panic")
}
}))
for i := 0; i < 8; i++ {
executor.Add(i)
expected = append(expected, i)
}
executor.Flush()
lock.Lock()
assert.Equal(t, expected, executedTasks)
lock.Unlock()
}
func TestPeriodicalExecutor_Wait(t *testing.T) { func TestPeriodicalExecutor_Wait(t *testing.T) {
var lock sync.Mutex var lock sync.Mutex
executer := NewBulkExecutor(func(tasks []interface{}) { executer := NewBulkExecutor(func(tasks []any) {
lock.Lock() lock.Lock()
defer lock.Unlock() defer lock.Unlock()
time.Sleep(10 * time.Millisecond) time.Sleep(10 * time.Millisecond)
@@ -124,7 +184,7 @@ func TestPeriodicalExecutor_WaitFast(t *testing.T) {
const total = 3 const total = 3
var cnt int var cnt int
var lock sync.Mutex var lock sync.Mutex
executer := NewBulkExecutor(func(tasks []interface{}) { executer := NewBulkExecutor(func(tasks []any) {
defer func() { defer func() {
cnt++ cnt++
}() }()
@@ -141,7 +201,7 @@ func TestPeriodicalExecutor_WaitFast(t *testing.T) {
} }
func TestPeriodicalExecutor_Deadlock(t *testing.T) { func TestPeriodicalExecutor_Deadlock(t *testing.T) {
executor := NewBulkExecutor(func(tasks []interface{}) { executor := NewBulkExecutor(func(tasks []any) {
}, WithBulkTasks(1), WithBulkInterval(time.Millisecond)) }, WithBulkTasks(1), WithBulkInterval(time.Millisecond))
for i := 0; i < 1e5; i++ { for i := 0; i < 1e5; i++ {
executor.Add(1) executor.Add(1)
@@ -149,13 +209,7 @@ func TestPeriodicalExecutor_Deadlock(t *testing.T) {
} }
func TestPeriodicalExecutor_hasTasks(t *testing.T) { func TestPeriodicalExecutor_hasTasks(t *testing.T) {
ticker := timex.NewFakeTicker()
defer ticker.Stop()
exec := NewPeriodicalExecutor(time.Millisecond, newContainer(time.Millisecond, nil)) exec := NewPeriodicalExecutor(time.Millisecond, newContainer(time.Millisecond, nil))
exec.newTicker = func(d time.Duration) timex.Ticker {
return ticker
}
assert.False(t, exec.hasTasks(nil)) assert.False(t, exec.hasTasks(nil))
assert.True(t, exec.hasTasks(1)) assert.True(t, exec.hasTasks(1))
} }

View File

@@ -5,4 +5,4 @@ import "time"
const defaultFlushInterval = time.Second const defaultFlushInterval = time.Second
// Execute defines the method to execute tasks. // Execute defines the method to execute tasks.
type Execute func(tasks []interface{}) type Execute func(tasks []any)

View File

@@ -1,5 +1,4 @@
//go:build windows //go:build windows
// +build windows
package fs package fs

View File

@@ -1,5 +1,4 @@
//go:build linux || darwin //go:build linux || darwin
// +build linux darwin
package fs package fs

View File

@@ -21,31 +21,31 @@ type (
} }
// FilterFunc defines the method to filter a Stream. // FilterFunc defines the method to filter a Stream.
FilterFunc func(item interface{}) bool FilterFunc func(item any) bool
// ForAllFunc defines the method to handle all elements in a Stream. // ForAllFunc defines the method to handle all elements in a Stream.
ForAllFunc func(pipe <-chan interface{}) ForAllFunc func(pipe <-chan any)
// ForEachFunc defines the method to handle each element in a Stream. // ForEachFunc defines the method to handle each element in a Stream.
ForEachFunc func(item interface{}) ForEachFunc func(item any)
// GenerateFunc defines the method to send elements into a Stream. // GenerateFunc defines the method to send elements into a Stream.
GenerateFunc func(source chan<- interface{}) GenerateFunc func(source chan<- any)
// KeyFunc defines the method to generate keys for the elements in a Stream. // KeyFunc defines the method to generate keys for the elements in a Stream.
KeyFunc func(item interface{}) interface{} KeyFunc func(item any) any
// LessFunc defines the method to compare the elements in a Stream. // LessFunc defines the method to compare the elements in a Stream.
LessFunc func(a, b interface{}) bool LessFunc func(a, b any) bool
// MapFunc defines the method to map each element to another object in a Stream. // MapFunc defines the method to map each element to another object in a Stream.
MapFunc func(item interface{}) interface{} MapFunc func(item any) any
// Option defines the method to customize a Stream. // Option defines the method to customize a Stream.
Option func(opts *rxOptions) Option func(opts *rxOptions)
// ParallelFunc defines the method to handle elements parallelly. // ParallelFunc defines the method to handle elements parallelly.
ParallelFunc func(item interface{}) ParallelFunc func(item any)
// ReduceFunc defines the method to reduce all the elements in a Stream. // ReduceFunc defines the method to reduce all the elements in a Stream.
ReduceFunc func(pipe <-chan interface{}) (interface{}, error) ReduceFunc func(pipe <-chan any) (any, error)
// WalkFunc defines the method to walk through all the elements in a Stream. // WalkFunc defines the method to walk through all the elements in a Stream.
WalkFunc func(item interface{}, pipe chan<- interface{}) WalkFunc func(item any, pipe chan<- any)
// A Stream is a stream that can be used to do stream processing. // A Stream is a stream that can be used to do stream processing.
Stream struct { Stream struct {
source <-chan interface{} source <-chan any
} }
) )
@@ -56,7 +56,7 @@ func Concat(s Stream, others ...Stream) Stream {
// From constructs a Stream from the given GenerateFunc. // From constructs a Stream from the given GenerateFunc.
func From(generate GenerateFunc) Stream { func From(generate GenerateFunc) Stream {
source := make(chan interface{}) source := make(chan any)
threading.GoSafe(func() { threading.GoSafe(func() {
defer close(source) defer close(source)
@@ -67,8 +67,8 @@ func From(generate GenerateFunc) Stream {
} }
// Just converts the given arbitrary items to a Stream. // Just converts the given arbitrary items to a Stream.
func Just(items ...interface{}) Stream { func Just(items ...any) Stream {
source := make(chan interface{}, len(items)) source := make(chan any, len(items))
for _, item := range items { for _, item := range items {
source <- item source <- item
} }
@@ -78,7 +78,7 @@ func Just(items ...interface{}) Stream {
} }
// Range converts the given channel to a Stream. // Range converts the given channel to a Stream.
func Range(source <-chan interface{}) Stream { func Range(source <-chan any) Stream {
return Stream{ return Stream{
source: source, source: source,
} }
@@ -87,7 +87,7 @@ func Range(source <-chan interface{}) Stream {
// AllMach returns whether all elements of this stream match the provided predicate. // AllMach returns whether all elements of this stream match the provided predicate.
// May not evaluate the predicate on all elements if not necessary for determining the result. // May not evaluate the predicate on all elements if not necessary for determining the result.
// If the stream is empty then true is returned and the predicate is not evaluated. // If the stream is empty then true is returned and the predicate is not evaluated.
func (s Stream) AllMach(predicate func(item interface{}) bool) bool { func (s Stream) AllMach(predicate func(item any) bool) bool {
for item := range s.source { for item := range s.source {
if !predicate(item) { if !predicate(item) {
// make sure the former goroutine not block, and current func returns fast. // make sure the former goroutine not block, and current func returns fast.
@@ -102,7 +102,7 @@ func (s Stream) AllMach(predicate func(item interface{}) bool) bool {
// AnyMach returns whether any elements of this stream match the provided predicate. // AnyMach returns whether any elements of this stream match the provided predicate.
// May not evaluate the predicate on all elements if not necessary for determining the result. // May not evaluate the predicate on all elements if not necessary for determining the result.
// If the stream is empty then false is returned and the predicate is not evaluated. // If the stream is empty then false is returned and the predicate is not evaluated.
func (s Stream) AnyMach(predicate func(item interface{}) bool) bool { func (s Stream) AnyMach(predicate func(item any) bool) bool {
for item := range s.source { for item := range s.source {
if predicate(item) { if predicate(item) {
// make sure the former goroutine not block, and current func returns fast. // make sure the former goroutine not block, and current func returns fast.
@@ -121,7 +121,7 @@ func (s Stream) Buffer(n int) Stream {
n = 0 n = 0
} }
source := make(chan interface{}, n) source := make(chan any, n)
go func() { go func() {
for item := range s.source { for item := range s.source {
source <- item source <- item
@@ -134,7 +134,7 @@ func (s Stream) Buffer(n int) Stream {
// Concat returns a Stream that concatenated other streams // Concat returns a Stream that concatenated other streams
func (s Stream) Concat(others ...Stream) Stream { func (s Stream) Concat(others ...Stream) Stream {
source := make(chan interface{}) source := make(chan any)
go func() { go func() {
group := threading.NewRoutineGroup() group := threading.NewRoutineGroup()
@@ -170,12 +170,12 @@ func (s Stream) Count() (count int) {
// Distinct removes the duplicated items base on the given KeyFunc. // Distinct removes the duplicated items base on the given KeyFunc.
func (s Stream) Distinct(fn KeyFunc) Stream { func (s Stream) Distinct(fn KeyFunc) Stream {
source := make(chan interface{}) source := make(chan any)
threading.GoSafe(func() { threading.GoSafe(func() {
defer close(source) defer close(source)
keys := make(map[interface{}]lang.PlaceholderType) keys := make(map[any]lang.PlaceholderType)
for item := range s.source { for item := range s.source {
key := fn(item) key := fn(item)
if _, ok := keys[key]; !ok { if _, ok := keys[key]; !ok {
@@ -195,7 +195,7 @@ func (s Stream) Done() {
// Filter filters the items by the given FilterFunc. // Filter filters the items by the given FilterFunc.
func (s Stream) Filter(fn FilterFunc, opts ...Option) Stream { func (s Stream) Filter(fn FilterFunc, opts ...Option) Stream {
return s.Walk(func(item interface{}, pipe chan<- interface{}) { return s.Walk(func(item any, pipe chan<- any) {
if fn(item) { if fn(item) {
pipe <- item pipe <- item
} }
@@ -203,7 +203,7 @@ func (s Stream) Filter(fn FilterFunc, opts ...Option) Stream {
} }
// First returns the first item, nil if no items. // First returns the first item, nil if no items.
func (s Stream) First() interface{} { func (s Stream) First() any {
for item := range s.source { for item := range s.source {
// make sure the former goroutine not block, and current func returns fast. // make sure the former goroutine not block, and current func returns fast.
go drain(s.source) go drain(s.source)
@@ -229,13 +229,13 @@ func (s Stream) ForEach(fn ForEachFunc) {
// Group groups the elements into different groups based on their keys. // Group groups the elements into different groups based on their keys.
func (s Stream) Group(fn KeyFunc) Stream { func (s Stream) Group(fn KeyFunc) Stream {
groups := make(map[interface{}][]interface{}) groups := make(map[any][]any)
for item := range s.source { for item := range s.source {
key := fn(item) key := fn(item)
groups[key] = append(groups[key], item) groups[key] = append(groups[key], item)
} }
source := make(chan interface{}) source := make(chan any)
go func() { go func() {
for _, group := range groups { for _, group := range groups {
source <- group source <- group
@@ -252,7 +252,7 @@ func (s Stream) Head(n int64) Stream {
panic("n must be greater than 0") panic("n must be greater than 0")
} }
source := make(chan interface{}) source := make(chan any)
go func() { go func() {
for item := range s.source { for item := range s.source {
@@ -279,7 +279,7 @@ func (s Stream) Head(n int64) Stream {
} }
// Last returns the last item, or nil if no items. // Last returns the last item, or nil if no items.
func (s Stream) Last() (item interface{}) { func (s Stream) Last() (item any) {
for item = range s.source { for item = range s.source {
} }
return return
@@ -287,29 +287,53 @@ func (s Stream) Last() (item interface{}) {
// Map converts each item to another corresponding item, which means it's a 1:1 model. // Map converts each item to another corresponding item, which means it's a 1:1 model.
func (s Stream) Map(fn MapFunc, opts ...Option) Stream { func (s Stream) Map(fn MapFunc, opts ...Option) Stream {
return s.Walk(func(item interface{}, pipe chan<- interface{}) { return s.Walk(func(item any, pipe chan<- any) {
pipe <- fn(item) pipe <- fn(item)
}, opts...) }, opts...)
} }
// Max returns the maximum item from the underlying source.
func (s Stream) Max(less LessFunc) any {
var max any
for item := range s.source {
if max == nil || less(max, item) {
max = item
}
}
return max
}
// Merge merges all the items into a slice and generates a new stream. // Merge merges all the items into a slice and generates a new stream.
func (s Stream) Merge() Stream { func (s Stream) Merge() Stream {
var items []interface{} var items []any
for item := range s.source { for item := range s.source {
items = append(items, item) items = append(items, item)
} }
source := make(chan interface{}, 1) source := make(chan any, 1)
source <- items source <- items
close(source) close(source)
return Range(source) return Range(source)
} }
// Min returns the minimum item from the underlying source.
func (s Stream) Min(less LessFunc) any {
var min any
for item := range s.source {
if min == nil || less(item, min) {
min = item
}
}
return min
}
// NoneMatch returns whether all elements of this stream don't match the provided predicate. // NoneMatch returns whether all elements of this stream don't match the provided predicate.
// May not evaluate the predicate on all elements if not necessary for determining the result. // May not evaluate the predicate on all elements if not necessary for determining the result.
// If the stream is empty then true is returned and the predicate is not evaluated. // If the stream is empty then true is returned and the predicate is not evaluated.
func (s Stream) NoneMatch(predicate func(item interface{}) bool) bool { func (s Stream) NoneMatch(predicate func(item any) bool) bool {
for item := range s.source { for item := range s.source {
if predicate(item) { if predicate(item) {
// make sure the former goroutine not block, and current func returns fast. // make sure the former goroutine not block, and current func returns fast.
@@ -323,19 +347,19 @@ func (s Stream) NoneMatch(predicate func(item interface{}) bool) bool {
// Parallel applies the given ParallelFunc to each item concurrently with given number of workers. // Parallel applies the given ParallelFunc to each item concurrently with given number of workers.
func (s Stream) Parallel(fn ParallelFunc, opts ...Option) { func (s Stream) Parallel(fn ParallelFunc, opts ...Option) {
s.Walk(func(item interface{}, pipe chan<- interface{}) { s.Walk(func(item any, pipe chan<- any) {
fn(item) fn(item)
}, opts...).Done() }, opts...).Done()
} }
// Reduce is an utility method to let the caller deal with the underlying channel. // Reduce is an utility method to let the caller deal with the underlying channel.
func (s Stream) Reduce(fn ReduceFunc) (interface{}, error) { func (s Stream) Reduce(fn ReduceFunc) (any, error) {
return fn(s.source) return fn(s.source)
} }
// Reverse reverses the elements in the stream. // Reverse reverses the elements in the stream.
func (s Stream) Reverse() Stream { func (s Stream) Reverse() Stream {
var items []interface{} var items []any
for item := range s.source { for item := range s.source {
items = append(items, item) items = append(items, item)
} }
@@ -357,7 +381,7 @@ func (s Stream) Skip(n int64) Stream {
return s return s
} }
source := make(chan interface{}) source := make(chan any)
go func() { go func() {
for item := range s.source { for item := range s.source {
@@ -376,7 +400,7 @@ func (s Stream) Skip(n int64) Stream {
// Sort sorts the items from the underlying source. // Sort sorts the items from the underlying source.
func (s Stream) Sort(less LessFunc) Stream { func (s Stream) Sort(less LessFunc) Stream {
var items []interface{} var items []any
for item := range s.source { for item := range s.source {
items = append(items, item) items = append(items, item)
} }
@@ -394,9 +418,9 @@ func (s Stream) Split(n int) Stream {
panic("n should be greater than 0") panic("n should be greater than 0")
} }
source := make(chan interface{}) source := make(chan any)
go func() { go func() {
var chunk []interface{} var chunk []any
for item := range s.source { for item := range s.source {
chunk = append(chunk, item) chunk = append(chunk, item)
if len(chunk) == n { if len(chunk) == n {
@@ -419,7 +443,7 @@ func (s Stream) Tail(n int64) Stream {
panic("n should be greater than 0") panic("n should be greater than 0")
} }
source := make(chan interface{}) source := make(chan any)
go func() { go func() {
ring := collection.NewRing(int(n)) ring := collection.NewRing(int(n))
@@ -446,7 +470,7 @@ func (s Stream) Walk(fn WalkFunc, opts ...Option) Stream {
} }
func (s Stream) walkLimited(fn WalkFunc, option *rxOptions) Stream { func (s Stream) walkLimited(fn WalkFunc, option *rxOptions) Stream {
pipe := make(chan interface{}, option.workers) pipe := make(chan any, option.workers)
go func() { go func() {
var wg sync.WaitGroup var wg sync.WaitGroup
@@ -477,7 +501,7 @@ func (s Stream) walkLimited(fn WalkFunc, option *rxOptions) Stream {
} }
func (s Stream) walkUnlimited(fn WalkFunc, option *rxOptions) Stream { func (s Stream) walkUnlimited(fn WalkFunc, option *rxOptions) Stream {
pipe := make(chan interface{}, option.workers) pipe := make(chan any, option.workers)
go func() { go func() {
var wg sync.WaitGroup var wg sync.WaitGroup
@@ -529,7 +553,7 @@ func buildOptions(opts ...Option) *rxOptions {
} }
// drain drains the given channel. // drain drains the given channel.
func drain(channel <-chan interface{}) { func drain(channel <-chan any) {
for range channel { for range channel {
} }
} }

View File

@@ -23,7 +23,7 @@ func TestBuffer(t *testing.T) {
var count int32 var count int32
var wait sync.WaitGroup var wait sync.WaitGroup
wait.Add(1) wait.Add(1)
From(func(source chan<- interface{}) { From(func(source chan<- any) {
ticker := time.NewTicker(10 * time.Millisecond) ticker := time.NewTicker(10 * time.Millisecond)
defer ticker.Stop() defer ticker.Stop()
@@ -36,7 +36,7 @@ func TestBuffer(t *testing.T) {
return return
} }
} }
}).Buffer(N).ForAll(func(pipe <-chan interface{}) { }).Buffer(N).ForAll(func(pipe <-chan any) {
wait.Wait() wait.Wait()
// why N+1, because take one more to wait for sending into the channel // why N+1, because take one more to wait for sending into the channel
assert.Equal(t, int32(N+1), atomic.LoadInt32(&count)) assert.Equal(t, int32(N+1), atomic.LoadInt32(&count))
@@ -47,7 +47,7 @@ func TestBuffer(t *testing.T) {
func TestBufferNegative(t *testing.T) { func TestBufferNegative(t *testing.T) {
runCheckedTest(t, func(t *testing.T) { runCheckedTest(t, func(t *testing.T) {
var result int var result int
Just(1, 2, 3, 4).Buffer(-1).Reduce(func(pipe <-chan interface{}) (interface{}, error) { Just(1, 2, 3, 4).Buffer(-1).Reduce(func(pipe <-chan any) (any, error) {
for item := range pipe { for item := range pipe {
result += item.(int) result += item.(int)
} }
@@ -61,22 +61,22 @@ func TestCount(t *testing.T) {
runCheckedTest(t, func(t *testing.T) { runCheckedTest(t, func(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
elements []interface{} elements []any
}{ }{
{ {
name: "no elements with nil", name: "no elements with nil",
}, },
{ {
name: "no elements", name: "no elements",
elements: []interface{}{}, elements: []any{},
}, },
{ {
name: "1 element", name: "1 element",
elements: []interface{}{1}, elements: []any{1},
}, },
{ {
name: "multiple elements", name: "multiple elements",
elements: []interface{}{1, 2, 3}, elements: []any{1, 2, 3},
}, },
} }
@@ -92,7 +92,7 @@ func TestCount(t *testing.T) {
func TestDone(t *testing.T) { func TestDone(t *testing.T) {
runCheckedTest(t, func(t *testing.T) { runCheckedTest(t, func(t *testing.T) {
var count int32 var count int32
Just(1, 2, 3).Walk(func(item interface{}, pipe chan<- interface{}) { Just(1, 2, 3).Walk(func(item any, pipe chan<- any) {
time.Sleep(time.Millisecond * 100) time.Sleep(time.Millisecond * 100)
atomic.AddInt32(&count, int32(item.(int))) atomic.AddInt32(&count, int32(item.(int)))
}).Done() }).Done()
@@ -103,7 +103,7 @@ func TestDone(t *testing.T) {
func TestJust(t *testing.T) { func TestJust(t *testing.T) {
runCheckedTest(t, func(t *testing.T) { runCheckedTest(t, func(t *testing.T) {
var result int var result int
Just(1, 2, 3, 4).Reduce(func(pipe <-chan interface{}) (interface{}, error) { Just(1, 2, 3, 4).Reduce(func(pipe <-chan any) (any, error) {
for item := range pipe { for item := range pipe {
result += item.(int) result += item.(int)
} }
@@ -116,9 +116,9 @@ func TestJust(t *testing.T) {
func TestDistinct(t *testing.T) { func TestDistinct(t *testing.T) {
runCheckedTest(t, func(t *testing.T) { runCheckedTest(t, func(t *testing.T) {
var result int var result int
Just(4, 1, 3, 2, 3, 4).Distinct(func(item interface{}) interface{} { Just(4, 1, 3, 2, 3, 4).Distinct(func(item any) any {
return item return item
}).Reduce(func(pipe <-chan interface{}) (interface{}, error) { }).Reduce(func(pipe <-chan any) (any, error) {
for item := range pipe { for item := range pipe {
result += item.(int) result += item.(int)
} }
@@ -131,9 +131,9 @@ func TestDistinct(t *testing.T) {
func TestFilter(t *testing.T) { func TestFilter(t *testing.T) {
runCheckedTest(t, func(t *testing.T) { runCheckedTest(t, func(t *testing.T) {
var result int var result int
Just(1, 2, 3, 4).Filter(func(item interface{}) bool { Just(1, 2, 3, 4).Filter(func(item any) bool {
return item.(int)%2 == 0 return item.(int)%2 == 0
}).Reduce(func(pipe <-chan interface{}) (interface{}, error) { }).Reduce(func(pipe <-chan any) (any, error) {
for item := range pipe { for item := range pipe {
result += item.(int) result += item.(int)
} }
@@ -154,9 +154,9 @@ func TestFirst(t *testing.T) {
func TestForAll(t *testing.T) { func TestForAll(t *testing.T) {
runCheckedTest(t, func(t *testing.T) { runCheckedTest(t, func(t *testing.T) {
var result int var result int
Just(1, 2, 3, 4).Filter(func(item interface{}) bool { Just(1, 2, 3, 4).Filter(func(item any) bool {
return item.(int)%2 == 0 return item.(int)%2 == 0
}).ForAll(func(pipe <-chan interface{}) { }).ForAll(func(pipe <-chan any) {
for item := range pipe { for item := range pipe {
result += item.(int) result += item.(int)
} }
@@ -168,11 +168,11 @@ func TestForAll(t *testing.T) {
func TestGroup(t *testing.T) { func TestGroup(t *testing.T) {
runCheckedTest(t, func(t *testing.T) { runCheckedTest(t, func(t *testing.T) {
var groups [][]int var groups [][]int
Just(10, 11, 20, 21).Group(func(item interface{}) interface{} { Just(10, 11, 20, 21).Group(func(item any) any {
v := item.(int) v := item.(int)
return v / 10 return v / 10
}).ForEach(func(item interface{}) { }).ForEach(func(item any) {
v := item.([]interface{}) v := item.([]any)
var group []int var group []int
for _, each := range v { for _, each := range v {
group = append(group, each.(int)) group = append(group, each.(int))
@@ -191,7 +191,7 @@ func TestGroup(t *testing.T) {
func TestHead(t *testing.T) { func TestHead(t *testing.T) {
runCheckedTest(t, func(t *testing.T) { runCheckedTest(t, func(t *testing.T) {
var result int var result int
Just(1, 2, 3, 4).Head(2).Reduce(func(pipe <-chan interface{}) (interface{}, error) { Just(1, 2, 3, 4).Head(2).Reduce(func(pipe <-chan any) (any, error) {
for item := range pipe { for item := range pipe {
result += item.(int) result += item.(int)
} }
@@ -204,7 +204,7 @@ func TestHead(t *testing.T) {
func TestHeadZero(t *testing.T) { func TestHeadZero(t *testing.T) {
runCheckedTest(t, func(t *testing.T) { runCheckedTest(t, func(t *testing.T) {
assert.Panics(t, func() { assert.Panics(t, func() {
Just(1, 2, 3, 4).Head(0).Reduce(func(pipe <-chan interface{}) (interface{}, error) { Just(1, 2, 3, 4).Head(0).Reduce(func(pipe <-chan any) (any, error) {
return nil, nil return nil, nil
}) })
}) })
@@ -214,7 +214,7 @@ func TestHeadZero(t *testing.T) {
func TestHeadMore(t *testing.T) { func TestHeadMore(t *testing.T) {
runCheckedTest(t, func(t *testing.T) { runCheckedTest(t, func(t *testing.T) {
var result int var result int
Just(1, 2, 3, 4).Head(6).Reduce(func(pipe <-chan interface{}) (interface{}, error) { Just(1, 2, 3, 4).Head(6).Reduce(func(pipe <-chan any) (any, error) {
for item := range pipe { for item := range pipe {
result += item.(int) result += item.(int)
} }
@@ -245,14 +245,14 @@ func TestMap(t *testing.T) {
expect int expect int
}{ }{
{ {
mapper: func(item interface{}) interface{} { mapper: func(item any) any {
v := item.(int) v := item.(int)
return v * v return v * v
}, },
expect: 30, expect: 30,
}, },
{ {
mapper: func(item interface{}) interface{} { mapper: func(item any) any {
v := item.(int) v := item.(int)
if v%2 == 0 { if v%2 == 0 {
return 0 return 0
@@ -262,7 +262,7 @@ func TestMap(t *testing.T) {
expect: 10, expect: 10,
}, },
{ {
mapper: func(item interface{}) interface{} { mapper: func(item any) any {
v := item.(int) v := item.(int)
if v%2 == 0 { if v%2 == 0 {
panic(v) panic(v)
@@ -283,12 +283,12 @@ func TestMap(t *testing.T) {
} else { } else {
workers = runtime.NumCPU() workers = runtime.NumCPU()
} }
From(func(source chan<- interface{}) { From(func(source chan<- any) {
for i := 1; i < 5; i++ { for i := 1; i < 5; i++ {
source <- i source <- i
} }
}).Map(test.mapper, WithWorkers(workers)).Reduce( }).Map(test.mapper, WithWorkers(workers)).Reduce(
func(pipe <-chan interface{}) (interface{}, error) { func(pipe <-chan any) (any, error) {
for item := range pipe { for item := range pipe {
result += item.(int) result += item.(int)
} }
@@ -303,8 +303,8 @@ func TestMap(t *testing.T) {
func TestMerge(t *testing.T) { func TestMerge(t *testing.T) {
runCheckedTest(t, func(t *testing.T) { runCheckedTest(t, func(t *testing.T) {
Just(1, 2, 3, 4).Merge().ForEach(func(item interface{}) { Just(1, 2, 3, 4).Merge().ForEach(func(item any) {
assert.ElementsMatch(t, []interface{}{1, 2, 3, 4}, item.([]interface{})) assert.ElementsMatch(t, []any{1, 2, 3, 4}, item.([]any))
}) })
}) })
} }
@@ -312,7 +312,7 @@ func TestMerge(t *testing.T) {
func TestParallelJust(t *testing.T) { func TestParallelJust(t *testing.T) {
runCheckedTest(t, func(t *testing.T) { runCheckedTest(t, func(t *testing.T) {
var count int32 var count int32
Just(1, 2, 3).Parallel(func(item interface{}) { Just(1, 2, 3).Parallel(func(item any) {
time.Sleep(time.Millisecond * 100) time.Sleep(time.Millisecond * 100)
atomic.AddInt32(&count, int32(item.(int))) atomic.AddInt32(&count, int32(item.(int)))
}, UnlimitedWorkers()) }, UnlimitedWorkers())
@@ -322,8 +322,8 @@ func TestParallelJust(t *testing.T) {
func TestReverse(t *testing.T) { func TestReverse(t *testing.T) {
runCheckedTest(t, func(t *testing.T) { runCheckedTest(t, func(t *testing.T) {
Just(1, 2, 3, 4).Reverse().Merge().ForEach(func(item interface{}) { Just(1, 2, 3, 4).Reverse().Merge().ForEach(func(item any) {
assert.ElementsMatch(t, []interface{}{4, 3, 2, 1}, item.([]interface{})) assert.ElementsMatch(t, []any{4, 3, 2, 1}, item.([]any))
}) })
}) })
} }
@@ -331,9 +331,9 @@ func TestReverse(t *testing.T) {
func TestSort(t *testing.T) { func TestSort(t *testing.T) {
runCheckedTest(t, func(t *testing.T) { runCheckedTest(t, func(t *testing.T) {
var prev int var prev int
Just(5, 3, 7, 1, 9, 6, 4, 8, 2).Sort(func(a, b interface{}) bool { Just(5, 3, 7, 1, 9, 6, 4, 8, 2).Sort(func(a, b any) bool {
return a.(int) < b.(int) return a.(int) < b.(int)
}).ForEach(func(item interface{}) { }).ForEach(func(item any) {
next := item.(int) next := item.(int)
assert.True(t, prev < next) assert.True(t, prev < next)
prev = next prev = next
@@ -346,12 +346,12 @@ func TestSplit(t *testing.T) {
assert.Panics(t, func() { assert.Panics(t, func() {
Just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).Split(0).Done() Just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).Split(0).Done()
}) })
var chunks [][]interface{} var chunks [][]any
Just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).Split(4).ForEach(func(item interface{}) { Just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).Split(4).ForEach(func(item any) {
chunk := item.([]interface{}) chunk := item.([]any)
chunks = append(chunks, chunk) chunks = append(chunks, chunk)
}) })
assert.EqualValues(t, [][]interface{}{ assert.EqualValues(t, [][]any{
{1, 2, 3, 4}, {1, 2, 3, 4},
{5, 6, 7, 8}, {5, 6, 7, 8},
{9, 10}, {9, 10},
@@ -362,7 +362,7 @@ func TestSplit(t *testing.T) {
func TestTail(t *testing.T) { func TestTail(t *testing.T) {
runCheckedTest(t, func(t *testing.T) { runCheckedTest(t, func(t *testing.T) {
var result int var result int
Just(1, 2, 3, 4).Tail(2).Reduce(func(pipe <-chan interface{}) (interface{}, error) { Just(1, 2, 3, 4).Tail(2).Reduce(func(pipe <-chan any) (any, error) {
for item := range pipe { for item := range pipe {
result += item.(int) result += item.(int)
} }
@@ -375,7 +375,7 @@ func TestTail(t *testing.T) {
func TestTailZero(t *testing.T) { func TestTailZero(t *testing.T) {
runCheckedTest(t, func(t *testing.T) { runCheckedTest(t, func(t *testing.T) {
assert.Panics(t, func() { assert.Panics(t, func() {
Just(1, 2, 3, 4).Tail(0).Reduce(func(pipe <-chan interface{}) (interface{}, error) { Just(1, 2, 3, 4).Tail(0).Reduce(func(pipe <-chan any) (any, error) {
return nil, nil return nil, nil
}) })
}) })
@@ -385,11 +385,11 @@ func TestTailZero(t *testing.T) {
func TestWalk(t *testing.T) { func TestWalk(t *testing.T) {
runCheckedTest(t, func(t *testing.T) { runCheckedTest(t, func(t *testing.T) {
var result int var result int
Just(1, 2, 3, 4, 5).Walk(func(item interface{}, pipe chan<- interface{}) { Just(1, 2, 3, 4, 5).Walk(func(item any, pipe chan<- any) {
if item.(int)%2 != 0 { if item.(int)%2 != 0 {
pipe <- item pipe <- item
} }
}, UnlimitedWorkers()).ForEach(func(item interface{}) { }, UnlimitedWorkers()).ForEach(func(item any) {
result += item.(int) result += item.(int)
}) })
assert.Equal(t, 9, result) assert.Equal(t, 9, result)
@@ -398,16 +398,16 @@ func TestWalk(t *testing.T) {
func TestStream_AnyMach(t *testing.T) { func TestStream_AnyMach(t *testing.T) {
runCheckedTest(t, func(t *testing.T) { runCheckedTest(t, func(t *testing.T) {
assetEqual(t, false, Just(1, 2, 3).AnyMach(func(item interface{}) bool { assetEqual(t, false, Just(1, 2, 3).AnyMach(func(item any) bool {
return item.(int) == 4 return item.(int) == 4
})) }))
assetEqual(t, false, Just(1, 2, 3).AnyMach(func(item interface{}) bool { assetEqual(t, false, Just(1, 2, 3).AnyMach(func(item any) bool {
return item.(int) == 0 return item.(int) == 0
})) }))
assetEqual(t, true, Just(1, 2, 3).AnyMach(func(item interface{}) bool { assetEqual(t, true, Just(1, 2, 3).AnyMach(func(item any) bool {
return item.(int) == 2 return item.(int) == 2
})) }))
assetEqual(t, true, Just(1, 2, 3).AnyMach(func(item interface{}) bool { assetEqual(t, true, Just(1, 2, 3).AnyMach(func(item any) bool {
return item.(int) == 2 return item.(int) == 2
})) }))
}) })
@@ -416,17 +416,17 @@ func TestStream_AnyMach(t *testing.T) {
func TestStream_AllMach(t *testing.T) { func TestStream_AllMach(t *testing.T) {
runCheckedTest(t, func(t *testing.T) { runCheckedTest(t, func(t *testing.T) {
assetEqual( assetEqual(
t, true, Just(1, 2, 3).AllMach(func(item interface{}) bool { t, true, Just(1, 2, 3).AllMach(func(item any) bool {
return true return true
}), }),
) )
assetEqual( assetEqual(
t, false, Just(1, 2, 3).AllMach(func(item interface{}) bool { t, false, Just(1, 2, 3).AllMach(func(item any) bool {
return false return false
}), }),
) )
assetEqual( assetEqual(
t, false, Just(1, 2, 3).AllMach(func(item interface{}) bool { t, false, Just(1, 2, 3).AllMach(func(item any) bool {
return item.(int) == 1 return item.(int) == 1
}), }),
) )
@@ -436,17 +436,17 @@ func TestStream_AllMach(t *testing.T) {
func TestStream_NoneMatch(t *testing.T) { func TestStream_NoneMatch(t *testing.T) {
runCheckedTest(t, func(t *testing.T) { runCheckedTest(t, func(t *testing.T) {
assetEqual( assetEqual(
t, true, Just(1, 2, 3).NoneMatch(func(item interface{}) bool { t, true, Just(1, 2, 3).NoneMatch(func(item any) bool {
return false return false
}), }),
) )
assetEqual( assetEqual(
t, false, Just(1, 2, 3).NoneMatch(func(item interface{}) bool { t, false, Just(1, 2, 3).NoneMatch(func(item any) bool {
return true return true
}), }),
) )
assetEqual( assetEqual(
t, true, Just(1, 2, 3).NoneMatch(func(item interface{}) bool { t, true, Just(1, 2, 3).NoneMatch(func(item any) bool {
return item.(int) == 4 return item.(int) == 4
}), }),
) )
@@ -455,19 +455,19 @@ func TestStream_NoneMatch(t *testing.T) {
func TestConcat(t *testing.T) { func TestConcat(t *testing.T) {
runCheckedTest(t, func(t *testing.T) { runCheckedTest(t, func(t *testing.T) {
a1 := []interface{}{1, 2, 3} a1 := []any{1, 2, 3}
a2 := []interface{}{4, 5, 6} a2 := []any{4, 5, 6}
s1 := Just(a1...) s1 := Just(a1...)
s2 := Just(a2...) s2 := Just(a2...)
stream := Concat(s1, s2) stream := Concat(s1, s2)
var items []interface{} var items []any
for item := range stream.source { for item := range stream.source {
items = append(items, item) items = append(items, item)
} }
sort.Slice(items, func(i, j int) bool { sort.Slice(items, func(i, j int) bool {
return items[i].(int) < items[j].(int) return items[i].(int) < items[j].(int)
}) })
ints := make([]interface{}, 0) ints := make([]any, 0)
ints = append(ints, a1...) ints = append(ints, a1...)
ints = append(ints, a2...) ints = append(ints, a2...)
assetEqual(t, ints, items) assetEqual(t, ints, items)
@@ -479,7 +479,7 @@ func TestStream_Skip(t *testing.T) {
assetEqual(t, 3, Just(1, 2, 3, 4).Skip(1).Count()) assetEqual(t, 3, Just(1, 2, 3, 4).Skip(1).Count())
assetEqual(t, 1, Just(1, 2, 3, 4).Skip(3).Count()) assetEqual(t, 1, Just(1, 2, 3, 4).Skip(3).Count())
assetEqual(t, 4, Just(1, 2, 3, 4).Skip(0).Count()) assetEqual(t, 4, Just(1, 2, 3, 4).Skip(0).Count())
equal(t, Just(1, 2, 3, 4).Skip(3), []interface{}{4}) equal(t, Just(1, 2, 3, 4).Skip(3), []any{4})
assert.Panics(t, func() { assert.Panics(t, func() {
Just(1, 2, 3, 4).Skip(-1) Just(1, 2, 3, 4).Skip(-1)
}) })
@@ -489,27 +489,104 @@ func TestStream_Skip(t *testing.T) {
func TestStream_Concat(t *testing.T) { func TestStream_Concat(t *testing.T) {
runCheckedTest(t, func(t *testing.T) { runCheckedTest(t, func(t *testing.T) {
stream := Just(1).Concat(Just(2), Just(3)) stream := Just(1).Concat(Just(2), Just(3))
var items []interface{} var items []any
for item := range stream.source { for item := range stream.source {
items = append(items, item) items = append(items, item)
} }
sort.Slice(items, func(i, j int) bool { sort.Slice(items, func(i, j int) bool {
return items[i].(int) < items[j].(int) return items[i].(int) < items[j].(int)
}) })
assetEqual(t, []interface{}{1, 2, 3}, items) assetEqual(t, []any{1, 2, 3}, items)
just := Just(1) just := Just(1)
equal(t, just.Concat(just), []interface{}{1}) equal(t, just.Concat(just), []any{1})
})
}
func TestStream_Max(t *testing.T) {
runCheckedTest(t, func(t *testing.T) {
tests := []struct {
name string
elements []any
max any
}{
{
name: "no elements with nil",
},
{
name: "no elements",
elements: []any{},
max: nil,
},
{
name: "1 element",
elements: []any{1},
max: 1,
},
{
name: "multiple elements",
elements: []any{1, 2, 9, 5, 8},
max: 9,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
val := Just(test.elements...).Max(func(a, b any) bool {
return a.(int) < b.(int)
})
assetEqual(t, test.max, val)
})
}
})
}
func TestStream_Min(t *testing.T) {
runCheckedTest(t, func(t *testing.T) {
tests := []struct {
name string
elements []any
min any
}{
{
name: "no elements with nil",
min: nil,
},
{
name: "no elements",
elements: []any{},
min: nil,
},
{
name: "1 element",
elements: []any{1},
min: 1,
},
{
name: "multiple elements",
elements: []any{-1, 1, 2, 9, 5, 8},
min: -1,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
val := Just(test.elements...).Min(func(a, b any) bool {
return a.(int) < b.(int)
})
assetEqual(t, test.min, val)
})
}
}) })
} }
func BenchmarkParallelMapReduce(b *testing.B) { func BenchmarkParallelMapReduce(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
mapper := func(v interface{}) interface{} { mapper := func(v any) any {
return v.(int64) * v.(int64) return v.(int64) * v.(int64)
} }
reducer := func(input <-chan interface{}) (interface{}, error) { reducer := func(input <-chan any) (any, error) {
var result int64 var result int64
for v := range input { for v := range input {
result += v.(int64) result += v.(int64)
@@ -517,7 +594,7 @@ func BenchmarkParallelMapReduce(b *testing.B) {
return result, nil return result, nil
} }
b.ResetTimer() b.ResetTimer()
From(func(input chan<- interface{}) { From(func(input chan<- any) {
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
input <- int64(rand.Int()) input <- int64(rand.Int())
@@ -529,10 +606,10 @@ func BenchmarkParallelMapReduce(b *testing.B) {
func BenchmarkMapReduce(b *testing.B) { func BenchmarkMapReduce(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
mapper := func(v interface{}) interface{} { mapper := func(v any) any {
return v.(int64) * v.(int64) return v.(int64) * v.(int64)
} }
reducer := func(input <-chan interface{}) (interface{}, error) { reducer := func(input <-chan any) (any, error) {
var result int64 var result int64
for v := range input { for v := range input {
result += v.(int64) result += v.(int64)
@@ -540,21 +617,21 @@ func BenchmarkMapReduce(b *testing.B) {
return result, nil return result, nil
} }
b.ResetTimer() b.ResetTimer()
From(func(input chan<- interface{}) { From(func(input chan<- any) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
input <- int64(rand.Int()) input <- int64(rand.Int())
} }
}).Map(mapper).Reduce(reducer) }).Map(mapper).Reduce(reducer)
} }
func assetEqual(t *testing.T, except, data interface{}) { func assetEqual(t *testing.T, except, data any) {
if !reflect.DeepEqual(except, data) { if !reflect.DeepEqual(except, data) {
t.Errorf(" %v, want %v", data, except) t.Errorf(" %v, want %v", data, except)
} }
} }
func equal(t *testing.T, stream Stream, data []interface{}) { func equal(t *testing.T, stream Stream, data []any) {
items := make([]interface{}, 0) items := make([]any, 0)
for item := range stream.source { for item := range stream.source {
items = append(items, item) items = append(items, item)
} }

View File

@@ -29,7 +29,7 @@ func DoWithTimeout(fn func() error, timeout time.Duration, opts ...DoOption) err
// create channel with buffer size 1 to avoid goroutine leak // create channel with buffer size 1 to avoid goroutine leak
done := make(chan error, 1) done := make(chan error, 1)
panicChan := make(chan interface{}, 1) panicChan := make(chan any, 1)
go func() { go func() {
defer func() { defer func() {
if p := recover(); p != nil { if p := recover(); p != nil {

View File

@@ -26,7 +26,7 @@ type (
hashFunc Func hashFunc Func
replicas int replicas int
keys []uint64 keys []uint64
ring map[uint64][]interface{} ring map[uint64][]any
nodes map[string]lang.PlaceholderType nodes map[string]lang.PlaceholderType
lock sync.RWMutex lock sync.RWMutex
} }
@@ -50,21 +50,21 @@ func NewCustomConsistentHash(replicas int, fn Func) *ConsistentHash {
return &ConsistentHash{ return &ConsistentHash{
hashFunc: fn, hashFunc: fn,
replicas: replicas, replicas: replicas,
ring: make(map[uint64][]interface{}), ring: make(map[uint64][]any),
nodes: make(map[string]lang.PlaceholderType), nodes: make(map[string]lang.PlaceholderType),
} }
} }
// Add adds the node with the number of h.replicas, // Add adds the node with the number of h.replicas,
// the later call will overwrite the replicas of the former calls. // the later call will overwrite the replicas of the former calls.
func (h *ConsistentHash) Add(node interface{}) { func (h *ConsistentHash) Add(node any) {
h.AddWithReplicas(node, h.replicas) h.AddWithReplicas(node, h.replicas)
} }
// AddWithReplicas adds the node with the number of replicas, // AddWithReplicas adds the node with the number of replicas,
// replicas will be truncated to h.replicas if it's larger than h.replicas, // replicas will be truncated to h.replicas if it's larger than h.replicas,
// the later call will overwrite the replicas of the former calls. // the later call will overwrite the replicas of the former calls.
func (h *ConsistentHash) AddWithReplicas(node interface{}, replicas int) { func (h *ConsistentHash) AddWithReplicas(node any, replicas int) {
h.Remove(node) h.Remove(node)
if replicas > h.replicas { if replicas > h.replicas {
@@ -89,7 +89,7 @@ func (h *ConsistentHash) AddWithReplicas(node interface{}, replicas int) {
// AddWithWeight adds the node with weight, the weight can be 1 to 100, indicates the percent, // AddWithWeight adds the node with weight, the weight can be 1 to 100, indicates the percent,
// the later call will overwrite the replicas of the former calls. // the later call will overwrite the replicas of the former calls.
func (h *ConsistentHash) AddWithWeight(node interface{}, weight int) { func (h *ConsistentHash) AddWithWeight(node any, weight int) {
// don't need to make sure weight not larger than TopWeight, // don't need to make sure weight not larger than TopWeight,
// because AddWithReplicas makes sure replicas cannot be larger than h.replicas // because AddWithReplicas makes sure replicas cannot be larger than h.replicas
replicas := h.replicas * weight / TopWeight replicas := h.replicas * weight / TopWeight
@@ -97,7 +97,7 @@ func (h *ConsistentHash) AddWithWeight(node interface{}, weight int) {
} }
// Get returns the corresponding node from h base on the given v. // Get returns the corresponding node from h base on the given v.
func (h *ConsistentHash) Get(v interface{}) (interface{}, bool) { func (h *ConsistentHash) Get(v any) (any, bool) {
h.lock.RLock() h.lock.RLock()
defer h.lock.RUnlock() defer h.lock.RUnlock()
@@ -124,7 +124,7 @@ func (h *ConsistentHash) Get(v interface{}) (interface{}, bool) {
} }
// Remove removes the given node from h. // Remove removes the given node from h.
func (h *ConsistentHash) Remove(node interface{}) { func (h *ConsistentHash) Remove(node any) {
nodeRepr := repr(node) nodeRepr := repr(node)
h.lock.Lock() h.lock.Lock()
@@ -177,10 +177,10 @@ func (h *ConsistentHash) removeNode(nodeRepr string) {
delete(h.nodes, nodeRepr) delete(h.nodes, nodeRepr)
} }
func innerRepr(node interface{}) string { func innerRepr(node any) string {
return fmt.Sprintf("%d:%v", prime, node) return fmt.Sprintf("%d:%v", prime, node)
} }
func repr(node interface{}) string { func repr(node any) string {
return lang.Repr(node) return lang.Repr(node)
} }

View File

@@ -42,7 +42,7 @@ func TestConsistentHash(t *testing.T) {
keys[key.(string)]++ keys[key.(string)]++
} }
mi := make(map[interface{}]int, len(keys)) mi := make(map[any]int, len(keys))
for k, v := range keys { for k, v := range keys {
mi[k] = v mi[k] = v
} }

View File

@@ -16,7 +16,7 @@ func NewBufferPool(capability int) *BufferPool {
return &BufferPool{ return &BufferPool{
capability: capability, capability: capability,
pool: &sync.Pool{ pool: &sync.Pool{
New: func() interface{} { New: func() any {
return new(bytes.Buffer) return new(bytes.Buffer)
}, },
}, },

View File

@@ -9,12 +9,12 @@ import (
) )
// Marshal marshals v into json bytes. // Marshal marshals v into json bytes.
func Marshal(v interface{}) ([]byte, error) { func Marshal(v any) ([]byte, error) {
return json.Marshal(v) return json.Marshal(v)
} }
// MarshalToString marshals v into a string. // MarshalToString marshals v into a string.
func MarshalToString(v interface{}) (string, error) { func MarshalToString(v any) (string, error) {
data, err := Marshal(v) data, err := Marshal(v)
if err != nil { if err != nil {
return "", err return "", err
@@ -24,7 +24,7 @@ func MarshalToString(v interface{}) (string, error) {
} }
// Unmarshal unmarshals data bytes into v. // Unmarshal unmarshals data bytes into v.
func Unmarshal(data []byte, v interface{}) error { func Unmarshal(data []byte, v any) error {
decoder := json.NewDecoder(bytes.NewReader(data)) decoder := json.NewDecoder(bytes.NewReader(data))
if err := unmarshalUseNumber(decoder, v); err != nil { if err := unmarshalUseNumber(decoder, v); err != nil {
return formatError(string(data), err) return formatError(string(data), err)
@@ -34,7 +34,7 @@ func Unmarshal(data []byte, v interface{}) error {
} }
// UnmarshalFromString unmarshals v from str. // UnmarshalFromString unmarshals v from str.
func UnmarshalFromString(str string, v interface{}) error { func UnmarshalFromString(str string, v any) error {
decoder := json.NewDecoder(strings.NewReader(str)) decoder := json.NewDecoder(strings.NewReader(str))
if err := unmarshalUseNumber(decoder, v); err != nil { if err := unmarshalUseNumber(decoder, v); err != nil {
return formatError(str, err) return formatError(str, err)
@@ -44,7 +44,7 @@ func UnmarshalFromString(str string, v interface{}) error {
} }
// UnmarshalFromReader unmarshals v from reader. // UnmarshalFromReader unmarshals v from reader.
func UnmarshalFromReader(reader io.Reader, v interface{}) error { func UnmarshalFromReader(reader io.Reader, v any) error {
var buf strings.Builder var buf strings.Builder
teeReader := io.TeeReader(reader, &buf) teeReader := io.TeeReader(reader, &buf)
decoder := json.NewDecoder(teeReader) decoder := json.NewDecoder(teeReader)
@@ -55,7 +55,7 @@ func UnmarshalFromReader(reader io.Reader, v interface{}) error {
return nil return nil
} }
func unmarshalUseNumber(decoder *json.Decoder, v interface{}) error { func unmarshalUseNumber(decoder *json.Decoder, v any) error {
decoder.UseNumber() decoder.UseNumber()
return decoder.Decode(v) return decoder.Decode(v)
} }

View File

@@ -11,13 +11,13 @@ var Placeholder PlaceholderType
type ( type (
// AnyType can be used to hold any type. // AnyType can be used to hold any type.
AnyType = interface{} AnyType = any
// PlaceholderType represents a placeholder type. // PlaceholderType represents a placeholder type.
PlaceholderType = struct{} PlaceholderType = struct{}
) )
// Repr returns the string representation of v. // Repr returns the string representation of v.
func Repr(v interface{}) string { func Repr(v any) string {
if v == nil { if v == nil {
return "" return ""
} }

View File

@@ -23,7 +23,7 @@ func TestRepr(t *testing.T) {
u64 uint64 = 8 u64 uint64 = 8
) )
tests := []struct { tests := []struct {
v interface{} v any
expect string expect string
}{ }{
{ {

View File

@@ -9,21 +9,6 @@ import (
"github.com/zeromicro/go-zero/core/stores/redis" "github.com/zeromicro/go-zero/core/stores/redis"
) )
// to be compatible with aliyun redis, we cannot use `local key = KEYS[1]` to reuse the key
const periodScript = `local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local current = redis.call("INCRBY", KEYS[1], 1)
if current == 1 then
redis.call("expire", KEYS[1], window)
end
if current < limit then
return 1
elseif current == limit then
return 2
else
return 0
end`
const ( const (
// Unknown means not initialized state. // Unknown means not initialized state.
Unknown = iota Unknown = iota
@@ -39,8 +24,25 @@ const (
internalHitQuota = 2 internalHitQuota = 2
) )
// ErrUnknownCode is an error that represents unknown status code. var (
var ErrUnknownCode = errors.New("unknown status code") // ErrUnknownCode is an error that represents unknown status code.
ErrUnknownCode = errors.New("unknown status code")
// to be compatible with aliyun redis, we cannot use `local key = KEYS[1]` to reuse the key
periodScript = redis.NewScript(`local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local current = redis.call("INCRBY", KEYS[1], 1)
if current == 1 then
redis.call("expire", KEYS[1], window)
end
if current < limit then
return 1
elseif current == limit then
return 2
else
return 0
end`)
)
type ( type (
// PeriodOption defines the method to customize a PeriodLimit. // PeriodOption defines the method to customize a PeriodLimit.
@@ -80,7 +82,7 @@ func (h *PeriodLimit) Take(key string) (int, error) {
// TakeCtx requests a permit with context, it returns the permit state. // TakeCtx requests a permit with context, it returns the permit state.
func (h *PeriodLimit) TakeCtx(ctx context.Context, key string) (int, error) { func (h *PeriodLimit) TakeCtx(ctx context.Context, key string) (int, error) {
resp, err := h.limitStore.EvalCtx(ctx, periodScript, []string{h.keyPrefix + key}, []string{ resp, err := h.limitStore.ScriptRunCtx(ctx, periodScript, []string{h.keyPrefix + key}, []string{
strconv.Itoa(h.quota), strconv.Itoa(h.quota),
strconv.Itoa(h.calcExpireSeconds()), strconv.Itoa(h.calcExpireSeconds()),
}) })

View File

@@ -33,9 +33,7 @@ func TestPeriodLimit_RedisUnavailable(t *testing.T) {
} }
func testPeriodLimit(t *testing.T, opts ...PeriodOption) { func testPeriodLimit(t *testing.T, opts ...PeriodOption) {
store, clean, err := redistest.CreateRedis() store := redistest.CreateRedis(t)
assert.Nil(t, err)
defer clean()
const ( const (
seconds = 1 seconds = 1

View File

@@ -15,10 +15,15 @@ import (
) )
const ( const (
// to be compatible with aliyun redis, we cannot use `local key = KEYS[1]` to reuse the key tokenFormat = "{%s}.tokens"
// KEYS[1] as tokens_key timestampFormat = "{%s}.ts"
// KEYS[2] as timestamp_key pingInterval = time.Millisecond * 100
script = `local rate = tonumber(ARGV[1]) )
// to be compatible with aliyun redis, we cannot use `local key = KEYS[1]` to reuse the key
// KEYS[1] as tokens_key
// KEYS[2] as timestamp_key
var script = redis.NewScript(`local rate = tonumber(ARGV[1])
local capacity = tonumber(ARGV[2]) local capacity = tonumber(ARGV[2])
local now = tonumber(ARGV[3]) local now = tonumber(ARGV[3])
local requested = tonumber(ARGV[4]) local requested = tonumber(ARGV[4])
@@ -45,11 +50,7 @@ end
redis.call("setex", KEYS[1], ttl, new_tokens) redis.call("setex", KEYS[1], ttl, new_tokens)
redis.call("setex", KEYS[2], ttl, now) redis.call("setex", KEYS[2], ttl, now)
return allowed` return allowed`)
tokenFormat = "{%s}.tokens"
timestampFormat = "{%s}.ts"
pingInterval = time.Millisecond * 100
)
// A TokenLimiter controls how frequently events are allowed to happen with in one second. // A TokenLimiter controls how frequently events are allowed to happen with in one second.
type TokenLimiter struct { type TokenLimiter struct {
@@ -110,7 +111,7 @@ func (lim *TokenLimiter) reserveN(ctx context.Context, now time.Time, n int) boo
return lim.rescueLimiter.AllowN(now, n) return lim.rescueLimiter.AllowN(now, n)
} }
resp, err := lim.store.EvalCtx(ctx, resp, err := lim.store.ScriptRunCtx(ctx,
script, script,
[]string{ []string{
lim.tokenKey, lim.tokenKey,

View File

@@ -70,9 +70,7 @@ func TestTokenLimit_Rescue(t *testing.T) {
} }
func TestTokenLimit_Take(t *testing.T) { func TestTokenLimit_Take(t *testing.T) {
store, clean, err := redistest.CreateRedis() store := redistest.CreateRedis(t)
assert.Nil(t, err)
defer clean()
const ( const (
total = 100 total = 100
@@ -92,9 +90,7 @@ func TestTokenLimit_Take(t *testing.T) {
} }
func TestTokenLimit_TakeBurst(t *testing.T) { func TestTokenLimit_TakeBurst(t *testing.T) {
store, clean, err := redistest.CreateRedis() store := redistest.CreateRedis(t)
assert.Nil(t, err)
defer clean()
const ( const (
total = 100 total = 100

View File

@@ -27,19 +27,39 @@ func Close() error {
return logx.Close() return logx.Close()
} }
// Debug writes v into access log.
func Debug(ctx context.Context, v ...interface{}) {
getLogger(ctx).Debug(v...)
}
// Debugf writes v with format into access log.
func Debugf(ctx context.Context, format string, v ...interface{}) {
getLogger(ctx).Debugf(format, v...)
}
// Debugv writes v into access log with json content.
func Debugv(ctx context.Context, v interface{}) {
getLogger(ctx).Debugv(v)
}
// Debugw writes msg along with fields into access log.
func Debugw(ctx context.Context, msg string, fields ...LogField) {
getLogger(ctx).Debugw(msg, fields...)
}
// Error writes v into error log. // Error writes v into error log.
func Error(ctx context.Context, v ...interface{}) { func Error(ctx context.Context, v ...any) {
getLogger(ctx).Error(v...) getLogger(ctx).Error(v...)
} }
// Errorf writes v with format into error log. // Errorf writes v with format into error log.
func Errorf(ctx context.Context, format string, v ...interface{}) { func Errorf(ctx context.Context, format string, v ...any) {
getLogger(ctx).Errorf(fmt.Errorf(format, v...).Error()) getLogger(ctx).Errorf(fmt.Errorf(format, v...).Error())
} }
// Errorv writes v into error log with json content. // Errorv writes v into error log with json content.
// No call stack attached, because not elegant to pack the messages. // No call stack attached, because not elegant to pack the messages.
func Errorv(ctx context.Context, v interface{}) { func Errorv(ctx context.Context, v any) {
getLogger(ctx).Errorv(v) getLogger(ctx).Errorv(v)
} }
@@ -49,22 +69,22 @@ func Errorw(ctx context.Context, msg string, fields ...LogField) {
} }
// Field returns a LogField for the given key and value. // Field returns a LogField for the given key and value.
func Field(key string, value interface{}) LogField { func Field(key string, value any) LogField {
return logx.Field(key, value) return logx.Field(key, value)
} }
// Info writes v into access log. // Info writes v into access log.
func Info(ctx context.Context, v ...interface{}) { func Info(ctx context.Context, v ...any) {
getLogger(ctx).Info(v...) getLogger(ctx).Info(v...)
} }
// Infof writes v with format into access log. // Infof writes v with format into access log.
func Infof(ctx context.Context, format string, v ...interface{}) { func Infof(ctx context.Context, format string, v ...any) {
getLogger(ctx).Infof(format, v...) getLogger(ctx).Infof(format, v...)
} }
// Infov writes v into access log with json content. // Infov writes v into access log with json content.
func Infov(ctx context.Context, v interface{}) { func Infov(ctx context.Context, v any) {
getLogger(ctx).Infov(v) getLogger(ctx).Infov(v)
} }
@@ -97,17 +117,17 @@ func SetUp(c LogConf) error {
} }
// Slow writes v into slow log. // Slow writes v into slow log.
func Slow(ctx context.Context, v ...interface{}) { func Slow(ctx context.Context, v ...any) {
getLogger(ctx).Slow(v...) getLogger(ctx).Slow(v...)
} }
// Slowf writes v with format into slow log. // Slowf writes v with format into slow log.
func Slowf(ctx context.Context, format string, v ...interface{}) { func Slowf(ctx context.Context, format string, v ...any) {
getLogger(ctx).Slowf(format, v...) getLogger(ctx).Slowf(format, v...)
} }
// Slowv writes v into slow log with json content. // Slowv writes v into slow log with json content.
func Slowv(ctx context.Context, v interface{}) { func Slowv(ctx context.Context, v any) {
getLogger(ctx).Slowv(v) getLogger(ctx).Slowv(v)
} }

View File

@@ -26,7 +26,7 @@ func TestAddGlobalFields(t *testing.T) {
AddGlobalFields(Field("a", "1"), Field("b", "2")) AddGlobalFields(Field("a", "1"), Field("b", "2"))
AddGlobalFields(Field("c", "3")) AddGlobalFields(Field("c", "3"))
Info(context.Background(), "world") Info(context.Background(), "world")
var m map[string]interface{} var m map[string]any
assert.NoError(t, json.Unmarshal(buf.Bytes(), &m)) assert.NoError(t, json.Unmarshal(buf.Bytes(), &m))
assert.Equal(t, "1", m["a"]) assert.Equal(t, "1", m["a"])
assert.Equal(t, "2", m["b"]) assert.Equal(t, "2", m["b"])
@@ -140,6 +140,54 @@ func TestInfow(t *testing.T) {
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1))) assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
} }
func TestDebug(t *testing.T) {
var buf strings.Builder
writer := logx.NewWriter(&buf)
old := logx.Reset()
logx.SetWriter(writer)
defer logx.SetWriter(old)
file, line := getFileLine()
Debug(context.Background(), "foo")
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
}
func TestDebugf(t *testing.T) {
var buf strings.Builder
writer := logx.NewWriter(&buf)
old := logx.Reset()
logx.SetWriter(writer)
defer logx.SetWriter(old)
file, line := getFileLine()
Debugf(context.Background(), "foo %s", "bar")
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
}
func TestDebugv(t *testing.T) {
var buf strings.Builder
writer := logx.NewWriter(&buf)
old := logx.Reset()
logx.SetWriter(writer)
defer logx.SetWriter(old)
file, line := getFileLine()
Debugv(context.Background(), "foo")
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
}
func TestDebugw(t *testing.T) {
var buf strings.Builder
writer := logx.NewWriter(&buf)
old := logx.Reset()
logx.SetWriter(writer)
defer logx.SetWriter(old)
file, line := getFileLine()
Debugw(context.Background(), "foo", Field("a", "b"))
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
}
func TestMust(t *testing.T) { func TestMust(t *testing.T) {
assert.NotPanics(t, func() { assert.NotPanics(t, func() {
Must(nil) Must(nil)

View File

@@ -2,17 +2,34 @@ package logx
// A LogConf is a logging config. // A LogConf is a logging config.
type LogConf struct { type LogConf struct {
ServiceName string `json:",optional"` // ServiceName represents the service name.
Mode string `json:",default=console,options=[console,file,volume]"` ServiceName string `json:",optional"`
Encoding string `json:",default=json,options=[json,plain]"` // Mode represents the logging mode, default is `console`.
TimeFormat string `json:",optional"` // console: log to console.
Path string `json:",default=logs"` // file: log to file.
Level string `json:",default=info,options=[debug,info,error,severe]"` // volume: used in k8s, prepend the hostname to the log file name.
MaxContentLength uint32 `json:",optional"` Mode string `json:",default=console,options=[console,file,volume]"`
Compress bool `json:",optional"` // Encoding represents the encoding type, default is `json`.
Stat bool `json:",default=true"` // json: json encoding.
KeepDays int `json:",optional"` // plain: plain text encoding, typically used in development.
StackCooldownMillis int `json:",default=100"` Encoding string `json:",default=json,options=[json,plain]"`
// TimeFormat represents the time format, default is `2006-01-02T15:04:05.000Z07:00`.
TimeFormat string `json:",optional"`
// Path represents the log file path, default is `logs`.
Path string `json:",default=logs"`
// Level represents the log level, default is `info`.
Level string `json:",default=info,options=[debug,info,error,severe]"`
// MaxContentLength represents the max content bytes, default is no limit.
MaxContentLength uint32 `json:",optional"`
// Compress represents whether to compress the log file, default is `false`.
Compress bool `json:",optional"`
// Stat represents whether to log statistics, default is `true`.
Stat bool `json:",default=true"`
// KeepDays represents how many days the log files will be kept. Default to keep all files.
// Only take effect when Mode is `file` or `volume`, both work when Rotation is `daily` or `size`.
KeepDays int `json:",optional"`
// StackCooldownMillis represents the cooldown time for stack logging, default is 100ms.
StackCooldownMillis int `json:",default=100"`
// MaxBackups represents how many backup log files will be kept. 0 means all files will be kept forever. // MaxBackups represents how many backup log files will be kept. 0 means all files will be kept forever.
// Only take effect when RotationRuleType is `size`. // Only take effect when RotationRuleType is `size`.
// Even thougth `MaxBackups` sets 0, log files will still be removed // Even thougth `MaxBackups` sets 0, log files will still be removed
@@ -21,7 +38,7 @@ type LogConf struct {
// MaxSize represents how much space the writing log file takes up. 0 means no limit. The unit is `MB`. // MaxSize represents how much space the writing log file takes up. 0 means no limit. The unit is `MB`.
// Only take effect when RotationRuleType is `size` // Only take effect when RotationRuleType is `size`
MaxSize int `json:",default=0"` MaxSize int `json:",default=0"`
// RotationRuleType represents the type of log rotation rule. Default is `daily`. // Rotation represents the type of log rotation rule. Default is `daily`.
// daily: daily rotation. // daily: daily rotation.
// size: size limited rotation. // size: size limited rotation.
Rotation string `json:",default=daily,options=[daily,size]"` Rotation string `json:",default=daily,options=[daily,size]"`

View File

@@ -25,7 +25,7 @@ func TestAddGlobalFields(t *testing.T) {
AddGlobalFields(Field("a", "1"), Field("b", "2")) AddGlobalFields(Field("a", "1"), Field("b", "2"))
AddGlobalFields(Field("c", "3")) AddGlobalFields(Field("c", "3"))
Info("world") Info("world")
var m map[string]interface{} var m map[string]any
assert.NoError(t, json.Unmarshal(buf.Bytes(), &m)) assert.NoError(t, json.Unmarshal(buf.Bytes(), &m))
assert.Equal(t, "1", m["a"]) assert.Equal(t, "1", m["a"])
assert.Equal(t, "2", m["b"]) assert.Equal(t, "2", m["b"])

View File

@@ -13,14 +13,14 @@ func NewLessLogger(milliseconds int) *LessLogger {
} }
// Error logs v into error log or discard it if more than once in the given duration. // Error logs v into error log or discard it if more than once in the given duration.
func (logger *LessLogger) Error(v ...interface{}) { func (logger *LessLogger) Error(v ...any) {
logger.logOrDiscard(func() { logger.logOrDiscard(func() {
Error(v...) Error(v...)
}) })
} }
// Errorf logs v with format into error log or discard it if more than once in the given duration. // Errorf logs v with format into error log or discard it if more than once in the given duration.
func (logger *LessLogger) Errorf(format string, v ...interface{}) { func (logger *LessLogger) Errorf(format string, v ...any) {
logger.logOrDiscard(func() { logger.logOrDiscard(func() {
Errorf(format, v...) Errorf(format, v...)
}) })

View File

@@ -8,35 +8,35 @@ import (
// A Logger represents a logger. // A Logger represents a logger.
type Logger interface { type Logger interface {
// Debug logs a message at info level. // Debug logs a message at info level.
Debug(...interface{}) Debug(...any)
// Debugf logs a message at info level. // Debugf logs a message at info level.
Debugf(string, ...interface{}) Debugf(string, ...any)
// Debugv logs a message at info level. // Debugv logs a message at info level.
Debugv(interface{}) Debugv(any)
// Debugw logs a message at info level. // Debugw logs a message at info level.
Debugw(string, ...LogField) Debugw(string, ...LogField)
// Error logs a message at error level. // Error logs a message at error level.
Error(...interface{}) Error(...any)
// Errorf logs a message at error level. // Errorf logs a message at error level.
Errorf(string, ...interface{}) Errorf(string, ...any)
// Errorv logs a message at error level. // Errorv logs a message at error level.
Errorv(interface{}) Errorv(any)
// Errorw logs a message at error level. // Errorw logs a message at error level.
Errorw(string, ...LogField) Errorw(string, ...LogField)
// Info logs a message at info level. // Info logs a message at info level.
Info(...interface{}) Info(...any)
// Infof logs a message at info level. // Infof logs a message at info level.
Infof(string, ...interface{}) Infof(string, ...any)
// Infov logs a message at info level. // Infov logs a message at info level.
Infov(interface{}) Infov(any)
// Infow logs a message at info level. // Infow logs a message at info level.
Infow(string, ...LogField) Infow(string, ...LogField)
// Slow logs a message at slow level. // Slow logs a message at slow level.
Slow(...interface{}) Slow(...any)
// Slowf logs a message at slow level. // Slowf logs a message at slow level.
Slowf(string, ...interface{}) Slowf(string, ...any)
// Slowv logs a message at slow level. // Slowv logs a message at slow level.
Slowv(interface{}) Slowv(any)
// Sloww logs a message at slow level. // Sloww logs a message at slow level.
Sloww(string, ...LogField) Sloww(string, ...LogField)
// WithCallerSkip returns a new logger with the given caller skip. // WithCallerSkip returns a new logger with the given caller skip.

View File

@@ -34,13 +34,13 @@ type (
// LogField is a key-value pair that will be added to the log entry. // LogField is a key-value pair that will be added to the log entry.
LogField struct { LogField struct {
Key string Key string
Value interface{} Value any
} }
// LogOption defines the method to customize the logging. // LogOption defines the method to customize the logging.
LogOption func(options *logOptions) LogOption func(options *logOptions)
logEntry map[string]interface{} logEntry map[string]any
logOptions struct { logOptions struct {
gzipEnabled bool gzipEnabled bool
@@ -67,17 +67,17 @@ func Close() error {
} }
// Debug writes v into access log. // Debug writes v into access log.
func Debug(v ...interface{}) { func Debug(v ...any) {
writeDebug(fmt.Sprint(v...)) writeDebug(fmt.Sprint(v...))
} }
// Debugf writes v with format into access log. // Debugf writes v with format into access log.
func Debugf(format string, v ...interface{}) { func Debugf(format string, v ...any) {
writeDebug(fmt.Sprintf(format, v...)) writeDebug(fmt.Sprintf(format, v...))
} }
// Debugv writes v into access log with json content. // Debugv writes v into access log with json content.
func Debugv(v interface{}) { func Debugv(v any) {
writeDebug(v) writeDebug(v)
} }
@@ -98,30 +98,30 @@ func DisableStat() {
} }
// Error writes v into error log. // Error writes v into error log.
func Error(v ...interface{}) { func Error(v ...any) {
writeError(fmt.Sprint(v...)) writeError(fmt.Sprint(v...))
} }
// Errorf writes v with format into error log. // Errorf writes v with format into error log.
func Errorf(format string, v ...interface{}) { func Errorf(format string, v ...any) {
writeError(fmt.Errorf(format, v...).Error()) writeError(fmt.Errorf(format, v...).Error())
} }
// ErrorStack writes v along with call stack into error log. // ErrorStack writes v along with call stack into error log.
func ErrorStack(v ...interface{}) { func ErrorStack(v ...any) {
// there is newline in stack string // there is newline in stack string
writeStack(fmt.Sprint(v...)) writeStack(fmt.Sprint(v...))
} }
// ErrorStackf writes v along with call stack in format into error log. // ErrorStackf writes v along with call stack in format into error log.
func ErrorStackf(format string, v ...interface{}) { func ErrorStackf(format string, v ...any) {
// there is newline in stack string // there is newline in stack string
writeStack(fmt.Sprintf(format, v...)) writeStack(fmt.Sprintf(format, v...))
} }
// Errorv writes v into error log with json content. // Errorv writes v into error log with json content.
// No call stack attached, because not elegant to pack the messages. // No call stack attached, because not elegant to pack the messages.
func Errorv(v interface{}) { func Errorv(v any) {
writeError(v) writeError(v)
} }
@@ -131,7 +131,7 @@ func Errorw(msg string, fields ...LogField) {
} }
// Field returns a LogField for the given key and value. // Field returns a LogField for the given key and value.
func Field(key string, value interface{}) LogField { func Field(key string, value any) LogField {
switch val := value.(type) { switch val := value.(type) {
case error: case error:
return LogField{Key: key, Value: val.Error()} return LogField{Key: key, Value: val.Error()}
@@ -169,17 +169,17 @@ func Field(key string, value interface{}) LogField {
} }
// Info writes v into access log. // Info writes v into access log.
func Info(v ...interface{}) { func Info(v ...any) {
writeInfo(fmt.Sprint(v...)) writeInfo(fmt.Sprint(v...))
} }
// Infof writes v with format into access log. // Infof writes v with format into access log.
func Infof(format string, v ...interface{}) { func Infof(format string, v ...any) {
writeInfo(fmt.Sprintf(format, v...)) writeInfo(fmt.Sprintf(format, v...))
} }
// Infov writes v into access log with json content. // Infov writes v into access log with json content.
func Infov(v interface{}) { func Infov(v any) {
writeInfo(v) writeInfo(v)
} }
@@ -197,7 +197,12 @@ func Must(err error) {
msg := err.Error() msg := err.Error()
log.Print(msg) log.Print(msg)
getWriter().Severe(msg) getWriter().Severe(msg)
os.Exit(1)
if ExitOnFatal.True() {
os.Exit(1)
} else {
panic(msg)
}
} }
// MustSetup sets up logging with given config c. It exits on error. // MustSetup sets up logging with given config c. It exits on error.
@@ -263,27 +268,27 @@ func SetUp(c LogConf) (err error) {
} }
// Severe writes v into severe log. // Severe writes v into severe log.
func Severe(v ...interface{}) { func Severe(v ...any) {
writeSevere(fmt.Sprint(v...)) writeSevere(fmt.Sprint(v...))
} }
// Severef writes v with format into severe log. // Severef writes v with format into severe log.
func Severef(format string, v ...interface{}) { func Severef(format string, v ...any) {
writeSevere(fmt.Sprintf(format, v...)) writeSevere(fmt.Sprintf(format, v...))
} }
// Slow writes v into slow log. // Slow writes v into slow log.
func Slow(v ...interface{}) { func Slow(v ...any) {
writeSlow(fmt.Sprint(v...)) writeSlow(fmt.Sprint(v...))
} }
// Slowf writes v with format into slow log. // Slowf writes v with format into slow log.
func Slowf(format string, v ...interface{}) { func Slowf(format string, v ...any) {
writeSlow(fmt.Sprintf(format, v...)) writeSlow(fmt.Sprintf(format, v...))
} }
// Slowv writes v into slow log with json content. // Slowv writes v into slow log with json content.
func Slowv(v interface{}) { func Slowv(v any) {
writeSlow(v) writeSlow(v)
} }
@@ -293,12 +298,12 @@ func Sloww(msg string, fields ...LogField) {
} }
// Stat writes v into stat log. // Stat writes v into stat log.
func Stat(v ...interface{}) { func Stat(v ...any) {
writeStat(fmt.Sprint(v...)) writeStat(fmt.Sprint(v...))
} }
// Statf writes v with format into stat log. // Statf writes v with format into stat log.
func Statf(format string, v ...interface{}) { func Statf(format string, v ...any) {
writeStat(fmt.Sprintf(format, v...)) writeStat(fmt.Sprintf(format, v...))
} }
@@ -422,19 +427,19 @@ func shallLogStat() bool {
return atomic.LoadUint32(&disableStat) == 0 return atomic.LoadUint32(&disableStat) == 0
} }
func writeDebug(val interface{}, fields ...LogField) { func writeDebug(val any, fields ...LogField) {
if shallLog(DebugLevel) { if shallLog(DebugLevel) {
getWriter().Debug(val, addCaller(fields...)...) getWriter().Debug(val, addCaller(fields...)...)
} }
} }
func writeError(val interface{}, fields ...LogField) { func writeError(val any, fields ...LogField) {
if shallLog(ErrorLevel) { if shallLog(ErrorLevel) {
getWriter().Error(val, addCaller(fields...)...) getWriter().Error(val, addCaller(fields...)...)
} }
} }
func writeInfo(val interface{}, fields ...LogField) { func writeInfo(val any, fields ...LogField) {
if shallLog(InfoLevel) { if shallLog(InfoLevel) {
getWriter().Info(val, addCaller(fields...)...) getWriter().Info(val, addCaller(fields...)...)
} }
@@ -446,7 +451,7 @@ func writeSevere(msg string) {
} }
} }
func writeSlow(val interface{}, fields ...LogField) { func writeSlow(val any, fields ...LogField) {
if shallLog(ErrorLevel) { if shallLog(ErrorLevel) {
getWriter().Slow(val, addCaller(fields...)...) getWriter().Slow(val, addCaller(fields...)...)
} }

View File

@@ -24,54 +24,58 @@ var (
_ Writer = (*mockWriter)(nil) _ Writer = (*mockWriter)(nil)
) )
func init() {
ExitOnFatal.Set(false)
}
type mockWriter struct { type mockWriter struct {
lock sync.Mutex lock sync.Mutex
builder strings.Builder builder strings.Builder
} }
func (mw *mockWriter) Alert(v interface{}) { func (mw *mockWriter) Alert(v any) {
mw.lock.Lock() mw.lock.Lock()
defer mw.lock.Unlock() defer mw.lock.Unlock()
output(&mw.builder, levelAlert, v) output(&mw.builder, levelAlert, v)
} }
func (mw *mockWriter) Debug(v interface{}, fields ...LogField) { func (mw *mockWriter) Debug(v any, fields ...LogField) {
mw.lock.Lock() mw.lock.Lock()
defer mw.lock.Unlock() defer mw.lock.Unlock()
output(&mw.builder, levelDebug, v, fields...) output(&mw.builder, levelDebug, v, fields...)
} }
func (mw *mockWriter) Error(v interface{}, fields ...LogField) { func (mw *mockWriter) Error(v any, fields ...LogField) {
mw.lock.Lock() mw.lock.Lock()
defer mw.lock.Unlock() defer mw.lock.Unlock()
output(&mw.builder, levelError, v, fields...) output(&mw.builder, levelError, v, fields...)
} }
func (mw *mockWriter) Info(v interface{}, fields ...LogField) { func (mw *mockWriter) Info(v any, fields ...LogField) {
mw.lock.Lock() mw.lock.Lock()
defer mw.lock.Unlock() defer mw.lock.Unlock()
output(&mw.builder, levelInfo, v, fields...) output(&mw.builder, levelInfo, v, fields...)
} }
func (mw *mockWriter) Severe(v interface{}) { func (mw *mockWriter) Severe(v any) {
mw.lock.Lock() mw.lock.Lock()
defer mw.lock.Unlock() defer mw.lock.Unlock()
output(&mw.builder, levelSevere, v) output(&mw.builder, levelSevere, v)
} }
func (mw *mockWriter) Slow(v interface{}, fields ...LogField) { func (mw *mockWriter) Slow(v any, fields ...LogField) {
mw.lock.Lock() mw.lock.Lock()
defer mw.lock.Unlock() defer mw.lock.Unlock()
output(&mw.builder, levelSlow, v, fields...) output(&mw.builder, levelSlow, v, fields...)
} }
func (mw *mockWriter) Stack(v interface{}) { func (mw *mockWriter) Stack(v any) {
mw.lock.Lock() mw.lock.Lock()
defer mw.lock.Unlock() defer mw.lock.Unlock()
output(&mw.builder, levelError, v) output(&mw.builder, levelError, v)
} }
func (mw *mockWriter) Stat(v interface{}, fields ...LogField) { func (mw *mockWriter) Stat(v any, fields ...LogField) {
mw.lock.Lock() mw.lock.Lock()
defer mw.lock.Unlock() defer mw.lock.Unlock()
output(&mw.builder, levelStat, v, fields...) output(&mw.builder, levelStat, v, fields...)
@@ -103,41 +107,41 @@ func TestField(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
f LogField f LogField
want map[string]interface{} want map[string]any
}{ }{
{ {
name: "error", name: "error",
f: Field("foo", errors.New("bar")), f: Field("foo", errors.New("bar")),
want: map[string]interface{}{ want: map[string]any{
"foo": "bar", "foo": "bar",
}, },
}, },
{ {
name: "errors", name: "errors",
f: Field("foo", []error{errors.New("bar"), errors.New("baz")}), f: Field("foo", []error{errors.New("bar"), errors.New("baz")}),
want: map[string]interface{}{ want: map[string]any{
"foo": []interface{}{"bar", "baz"}, "foo": []any{"bar", "baz"},
}, },
}, },
{ {
name: "strings", name: "strings",
f: Field("foo", []string{"bar", "baz"}), f: Field("foo", []string{"bar", "baz"}),
want: map[string]interface{}{ want: map[string]any{
"foo": []interface{}{"bar", "baz"}, "foo": []any{"bar", "baz"},
}, },
}, },
{ {
name: "duration", name: "duration",
f: Field("foo", time.Second), f: Field("foo", time.Second),
want: map[string]interface{}{ want: map[string]any{
"foo": "1s", "foo": "1s",
}, },
}, },
{ {
name: "durations", name: "durations",
f: Field("foo", []time.Duration{time.Second, 2 * time.Second}), f: Field("foo", []time.Duration{time.Second, 2 * time.Second}),
want: map[string]interface{}{ want: map[string]any{
"foo": []interface{}{"1s", "2s"}, "foo": []any{"1s", "2s"},
}, },
}, },
{ {
@@ -146,22 +150,22 @@ func TestField(t *testing.T) {
time.Date(2020, time.January, 1, 0, 0, 0, 0, time.UTC), time.Date(2020, time.January, 1, 0, 0, 0, 0, time.UTC),
time.Date(2020, time.January, 2, 0, 0, 0, 0, time.UTC), time.Date(2020, time.January, 2, 0, 0, 0, 0, time.UTC),
}), }),
want: map[string]interface{}{ want: map[string]any{
"foo": []interface{}{"2020-01-01 00:00:00 +0000 UTC", "2020-01-02 00:00:00 +0000 UTC"}, "foo": []any{"2020-01-01 00:00:00 +0000 UTC", "2020-01-02 00:00:00 +0000 UTC"},
}, },
}, },
{ {
name: "stringer", name: "stringer",
f: Field("foo", ValStringer{val: "bar"}), f: Field("foo", ValStringer{val: "bar"}),
want: map[string]interface{}{ want: map[string]any{
"foo": "bar", "foo": "bar",
}, },
}, },
{ {
name: "stringers", name: "stringers",
f: Field("foo", []fmt.Stringer{ValStringer{val: "bar"}, ValStringer{val: "baz"}}), f: Field("foo", []fmt.Stringer{ValStringer{val: "bar"}, ValStringer{val: "baz"}}),
want: map[string]interface{}{ want: map[string]any{
"foo": []interface{}{"bar", "baz"}, "foo": []any{"bar", "baz"},
}, },
}, },
} }
@@ -208,12 +212,18 @@ func TestFileLineConsoleMode(t *testing.T) {
assert.True(t, w.Contains(fmt.Sprintf("%s:%d", file, line+1))) assert.True(t, w.Contains(fmt.Sprintf("%s:%d", file, line+1)))
} }
func TestMust(t *testing.T) {
assert.Panics(t, func() {
Must(errors.New("foo"))
})
}
func TestStructedLogAlert(t *testing.T) { func TestStructedLogAlert(t *testing.T) {
w := new(mockWriter) w := new(mockWriter)
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLog(t, levelAlert, w, func(v ...interface{}) { doTestStructedLog(t, levelAlert, w, func(v ...any) {
Alert(fmt.Sprint(v...)) Alert(fmt.Sprint(v...))
}) })
} }
@@ -223,7 +233,7 @@ func TestStructedLogDebug(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLog(t, levelDebug, w, func(v ...interface{}) { doTestStructedLog(t, levelDebug, w, func(v ...any) {
Debug(v...) Debug(v...)
}) })
} }
@@ -233,7 +243,7 @@ func TestStructedLogDebugf(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLog(t, levelDebug, w, func(v ...interface{}) { doTestStructedLog(t, levelDebug, w, func(v ...any) {
Debugf(fmt.Sprint(v...)) Debugf(fmt.Sprint(v...))
}) })
} }
@@ -243,7 +253,7 @@ func TestStructedLogDebugv(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLog(t, levelDebug, w, func(v ...interface{}) { doTestStructedLog(t, levelDebug, w, func(v ...any) {
Debugv(fmt.Sprint(v...)) Debugv(fmt.Sprint(v...))
}) })
} }
@@ -253,7 +263,7 @@ func TestStructedLogDebugw(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLog(t, levelDebug, w, func(v ...interface{}) { doTestStructedLog(t, levelDebug, w, func(v ...any) {
Debugw(fmt.Sprint(v...), Field("foo", time.Second)) Debugw(fmt.Sprint(v...), Field("foo", time.Second))
}) })
} }
@@ -263,7 +273,7 @@ func TestStructedLogError(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLog(t, levelError, w, func(v ...interface{}) { doTestStructedLog(t, levelError, w, func(v ...any) {
Error(v...) Error(v...)
}) })
} }
@@ -273,7 +283,7 @@ func TestStructedLogErrorf(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLog(t, levelError, w, func(v ...interface{}) { doTestStructedLog(t, levelError, w, func(v ...any) {
Errorf("%s", fmt.Sprint(v...)) Errorf("%s", fmt.Sprint(v...))
}) })
} }
@@ -283,7 +293,7 @@ func TestStructedLogErrorv(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLog(t, levelError, w, func(v ...interface{}) { doTestStructedLog(t, levelError, w, func(v ...any) {
Errorv(fmt.Sprint(v...)) Errorv(fmt.Sprint(v...))
}) })
} }
@@ -293,7 +303,7 @@ func TestStructedLogErrorw(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLog(t, levelError, w, func(v ...interface{}) { doTestStructedLog(t, levelError, w, func(v ...any) {
Errorw(fmt.Sprint(v...), Field("foo", "bar")) Errorw(fmt.Sprint(v...), Field("foo", "bar"))
}) })
} }
@@ -303,7 +313,7 @@ func TestStructedLogInfo(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLog(t, levelInfo, w, func(v ...interface{}) { doTestStructedLog(t, levelInfo, w, func(v ...any) {
Info(v...) Info(v...)
}) })
} }
@@ -313,7 +323,7 @@ func TestStructedLogInfof(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLog(t, levelInfo, w, func(v ...interface{}) { doTestStructedLog(t, levelInfo, w, func(v ...any) {
Infof("%s", fmt.Sprint(v...)) Infof("%s", fmt.Sprint(v...))
}) })
} }
@@ -323,7 +333,7 @@ func TestStructedLogInfov(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLog(t, levelInfo, w, func(v ...interface{}) { doTestStructedLog(t, levelInfo, w, func(v ...any) {
Infov(fmt.Sprint(v...)) Infov(fmt.Sprint(v...))
}) })
} }
@@ -333,7 +343,7 @@ func TestStructedLogInfow(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLog(t, levelInfo, w, func(v ...interface{}) { doTestStructedLog(t, levelInfo, w, func(v ...any) {
Infow(fmt.Sprint(v...), Field("foo", "bar")) Infow(fmt.Sprint(v...), Field("foo", "bar"))
}) })
} }
@@ -343,7 +353,7 @@ func TestStructedLogInfoConsoleAny(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLogConsole(t, w, func(v ...interface{}) { doTestStructedLogConsole(t, w, func(v ...any) {
old := atomic.LoadUint32(&encoding) old := atomic.LoadUint32(&encoding)
atomic.StoreUint32(&encoding, plainEncodingType) atomic.StoreUint32(&encoding, plainEncodingType)
defer func() { defer func() {
@@ -359,7 +369,7 @@ func TestStructedLogInfoConsoleAnyString(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLogConsole(t, w, func(v ...interface{}) { doTestStructedLogConsole(t, w, func(v ...any) {
old := atomic.LoadUint32(&encoding) old := atomic.LoadUint32(&encoding)
atomic.StoreUint32(&encoding, plainEncodingType) atomic.StoreUint32(&encoding, plainEncodingType)
defer func() { defer func() {
@@ -375,7 +385,7 @@ func TestStructedLogInfoConsoleAnyError(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLogConsole(t, w, func(v ...interface{}) { doTestStructedLogConsole(t, w, func(v ...any) {
old := atomic.LoadUint32(&encoding) old := atomic.LoadUint32(&encoding)
atomic.StoreUint32(&encoding, plainEncodingType) atomic.StoreUint32(&encoding, plainEncodingType)
defer func() { defer func() {
@@ -391,7 +401,7 @@ func TestStructedLogInfoConsoleAnyStringer(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLogConsole(t, w, func(v ...interface{}) { doTestStructedLogConsole(t, w, func(v ...any) {
old := atomic.LoadUint32(&encoding) old := atomic.LoadUint32(&encoding)
atomic.StoreUint32(&encoding, plainEncodingType) atomic.StoreUint32(&encoding, plainEncodingType)
defer func() { defer func() {
@@ -409,7 +419,7 @@ func TestStructedLogInfoConsoleText(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLogConsole(t, w, func(v ...interface{}) { doTestStructedLogConsole(t, w, func(v ...any) {
old := atomic.LoadUint32(&encoding) old := atomic.LoadUint32(&encoding)
atomic.StoreUint32(&encoding, plainEncodingType) atomic.StoreUint32(&encoding, plainEncodingType)
defer func() { defer func() {
@@ -425,7 +435,7 @@ func TestStructedLogSlow(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLog(t, levelSlow, w, func(v ...interface{}) { doTestStructedLog(t, levelSlow, w, func(v ...any) {
Slow(v...) Slow(v...)
}) })
} }
@@ -435,7 +445,7 @@ func TestStructedLogSlowf(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLog(t, levelSlow, w, func(v ...interface{}) { doTestStructedLog(t, levelSlow, w, func(v ...any) {
Slowf(fmt.Sprint(v...)) Slowf(fmt.Sprint(v...))
}) })
} }
@@ -445,7 +455,7 @@ func TestStructedLogSlowv(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLog(t, levelSlow, w, func(v ...interface{}) { doTestStructedLog(t, levelSlow, w, func(v ...any) {
Slowv(fmt.Sprint(v...)) Slowv(fmt.Sprint(v...))
}) })
} }
@@ -455,7 +465,7 @@ func TestStructedLogSloww(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLog(t, levelSlow, w, func(v ...interface{}) { doTestStructedLog(t, levelSlow, w, func(v ...any) {
Sloww(fmt.Sprint(v...), Field("foo", time.Second)) Sloww(fmt.Sprint(v...), Field("foo", time.Second))
}) })
} }
@@ -465,7 +475,7 @@ func TestStructedLogStat(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLog(t, levelStat, w, func(v ...interface{}) { doTestStructedLog(t, levelStat, w, func(v ...any) {
Stat(v...) Stat(v...)
}) })
} }
@@ -475,7 +485,7 @@ func TestStructedLogStatf(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLog(t, levelStat, w, func(v ...interface{}) { doTestStructedLog(t, levelStat, w, func(v ...any) {
Statf(fmt.Sprint(v...)) Statf(fmt.Sprint(v...))
}) })
} }
@@ -485,7 +495,7 @@ func TestStructedLogSevere(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLog(t, levelSevere, w, func(v ...interface{}) { doTestStructedLog(t, levelSevere, w, func(v ...any) {
Severe(v...) Severe(v...)
}) })
} }
@@ -495,7 +505,7 @@ func TestStructedLogSeveref(t *testing.T) {
old := writer.Swap(w) old := writer.Swap(w)
defer writer.Store(old) defer writer.Store(old)
doTestStructedLog(t, levelSevere, w, func(v ...interface{}) { doTestStructedLog(t, levelSevere, w, func(v ...any) {
Severef(fmt.Sprint(v...)) Severef(fmt.Sprint(v...))
}) })
} }
@@ -507,7 +517,7 @@ func TestStructedLogWithDuration(t *testing.T) {
defer writer.Store(old) defer writer.Store(old)
WithDuration(time.Second).Info(message) WithDuration(time.Second).Info(message)
var entry map[string]interface{} var entry map[string]any
if err := json.Unmarshal([]byte(w.String()), &entry); err != nil { if err := json.Unmarshal([]byte(w.String()), &entry); err != nil {
t.Error(err) t.Error(err)
} }
@@ -574,26 +584,38 @@ func TestSetup(t *testing.T) {
atomic.StoreUint32(&encoding, jsonEncodingType) atomic.StoreUint32(&encoding, jsonEncodingType)
}() }()
setupOnce = sync.Once{}
MustSetup(LogConf{
ServiceName: "any",
Mode: "console",
Encoding: "json",
TimeFormat: timeFormat,
})
setupOnce = sync.Once{}
MustSetup(LogConf{ MustSetup(LogConf{
ServiceName: "any", ServiceName: "any",
Mode: "console", Mode: "console",
TimeFormat: timeFormat, TimeFormat: timeFormat,
}) })
setupOnce = sync.Once{}
MustSetup(LogConf{ MustSetup(LogConf{
ServiceName: "any", ServiceName: "any",
Mode: "file", Mode: "file",
Path: os.TempDir(), Path: os.TempDir(),
}) })
setupOnce = sync.Once{}
MustSetup(LogConf{ MustSetup(LogConf{
ServiceName: "any", ServiceName: "any",
Mode: "volume", Mode: "volume",
Path: os.TempDir(), Path: os.TempDir(),
}) })
setupOnce = sync.Once{}
MustSetup(LogConf{ MustSetup(LogConf{
ServiceName: "any", ServiceName: "any",
Mode: "console", Mode: "console",
TimeFormat: timeFormat, TimeFormat: timeFormat,
}) })
setupOnce = sync.Once{}
MustSetup(LogConf{ MustSetup(LogConf{
ServiceName: "any", ServiceName: "any",
Mode: "console", Mode: "console",
@@ -767,11 +789,11 @@ func put(b []byte) {
} }
} }
func doTestStructedLog(t *testing.T, level string, w *mockWriter, write func(...interface{})) { func doTestStructedLog(t *testing.T, level string, w *mockWriter, write func(...any)) {
const message = "hello there" const message = "hello there"
write(message) write(message)
var entry map[string]interface{} var entry map[string]any
if err := json.Unmarshal([]byte(w.String()), &entry); err != nil { if err := json.Unmarshal([]byte(w.String()), &entry); err != nil {
t.Error(err) t.Error(err)
} }
@@ -782,7 +804,7 @@ func doTestStructedLog(t *testing.T, level string, w *mockWriter, write func(...
assert.True(t, strings.Contains(val.(string), message)) assert.True(t, strings.Contains(val.(string), message))
} }
func doTestStructedLogConsole(t *testing.T, w *mockWriter, write func(...interface{})) { func doTestStructedLogConsole(t *testing.T, w *mockWriter, write func(...any)) {
const message = "hello there" const message = "hello there"
write(message) write(message)
assert.True(t, strings.Contains(w.String(), message)) assert.True(t, strings.Contains(w.String(), message))
@@ -822,8 +844,8 @@ func (v ValStringer) String() string {
return v.val return v.val
} }
func validateFields(t *testing.T, content string, fields map[string]interface{}) { func validateFields(t *testing.T, content string, fields map[string]any) {
var m map[string]interface{} var m map[string]any
if err := json.Unmarshal([]byte(content), &m); err != nil { if err := json.Unmarshal([]byte(content), &m); err != nil {
t.Error(err) t.Error(err)
} }

View File

@@ -52,27 +52,27 @@ type LogConf struct {
```go ```go
type Logger interface { type Logger interface {
// Error logs a message at error level. // Error logs a message at error level.
Error(...interface{}) Error(...any)
// Errorf logs a message at error level. // Errorf logs a message at error level.
Errorf(string, ...interface{}) Errorf(string, ...any)
// Errorv logs a message at error level. // Errorv logs a message at error level.
Errorv(interface{}) Errorv(any)
// Errorw logs a message at error level. // Errorw logs a message at error level.
Errorw(string, ...LogField) Errorw(string, ...LogField)
// Info logs a message at info level. // Info logs a message at info level.
Info(...interface{}) Info(...any)
// Infof logs a message at info level. // Infof logs a message at info level.
Infof(string, ...interface{}) Infof(string, ...any)
// Infov logs a message at info level. // Infov logs a message at info level.
Infov(interface{}) Infov(any)
// Infow logs a message at info level. // Infow logs a message at info level.
Infow(string, ...LogField) Infow(string, ...LogField)
// Slow logs a message at slow level. // Slow logs a message at slow level.
Slow(...interface{}) Slow(...any)
// Slowf logs a message at slow level. // Slowf logs a message at slow level.
Slowf(string, ...interface{}) Slowf(string, ...any)
// Slowv logs a message at slow level. // Slowv logs a message at slow level.
Slowv(interface{}) Slowv(any)
// Sloww logs a message at slow level. // Sloww logs a message at slow level.
Sloww(string, ...LogField) Sloww(string, ...LogField)
// WithContext returns a new logger with the given context. // WithContext returns a new logger with the given context.
@@ -165,7 +165,7 @@ func NewSensitiveLogger(writer logx.Writer) *SensitiveLogger {
} }
} }
func (l *SensitiveLogger) Info(msg interface{}, fields ...logx.LogField) { func (l *SensitiveLogger) Info(msg any, fields ...logx.LogField) {
if m, ok := msg.(Message); ok { if m, ok := msg.(Message); ok {
l.Writer.Info(Message{ l.Writer.Info(Message{
Name: m.Name, Name: m.Name,

View File

@@ -51,27 +51,27 @@ type LogConf struct {
```go ```go
type Logger interface { type Logger interface {
// Error logs a message at error level. // Error logs a message at error level.
Error(...interface{}) Error(...any)
// Errorf logs a message at error level. // Errorf logs a message at error level.
Errorf(string, ...interface{}) Errorf(string, ...any)
// Errorv logs a message at error level. // Errorv logs a message at error level.
Errorv(interface{}) Errorv(any)
// Errorw logs a message at error level. // Errorw logs a message at error level.
Errorw(string, ...LogField) Errorw(string, ...LogField)
// Info logs a message at info level. // Info logs a message at info level.
Info(...interface{}) Info(...any)
// Infof logs a message at info level. // Infof logs a message at info level.
Infof(string, ...interface{}) Infof(string, ...any)
// Infov logs a message at info level. // Infov logs a message at info level.
Infov(interface{}) Infov(any)
// Infow logs a message at info level. // Infow logs a message at info level.
Infow(string, ...LogField) Infow(string, ...LogField)
// Slow logs a message at slow level. // Slow logs a message at slow level.
Slow(...interface{}) Slow(...any)
// Slowf logs a message at slow level. // Slowf logs a message at slow level.
Slowf(string, ...interface{}) Slowf(string, ...any)
// Slowv logs a message at slow level. // Slowv logs a message at slow level.
Slowv(interface{}) Slowv(any)
// Sloww logs a message at slow level. // Sloww logs a message at slow level.
Sloww(string, ...LogField) Sloww(string, ...LogField)
// WithContext returns a new logger with the given context. // WithContext returns a new logger with the given context.
@@ -164,7 +164,7 @@ func NewSensitiveLogger(writer logx.Writer) *SensitiveLogger {
} }
} }
func (l *SensitiveLogger) Info(msg interface{}, fields ...logx.LogField) { func (l *SensitiveLogger) Info(msg any, fields ...logx.LogField) {
if m, ok := msg.(Message); ok { if m, ok := msg.(Message); ok {
l.Writer.Info(Message{ l.Writer.Info(Message{
Name: m.Name, Name: m.Name,

View File

@@ -40,15 +40,15 @@ type richLogger struct {
fields []LogField fields []LogField
} }
func (l *richLogger) Debug(v ...interface{}) { func (l *richLogger) Debug(v ...any) {
l.debug(fmt.Sprint(v...)) l.debug(fmt.Sprint(v...))
} }
func (l *richLogger) Debugf(format string, v ...interface{}) { func (l *richLogger) Debugf(format string, v ...any) {
l.debug(fmt.Sprintf(format, v...)) l.debug(fmt.Sprintf(format, v...))
} }
func (l *richLogger) Debugv(v interface{}) { func (l *richLogger) Debugv(v any) {
l.debug(v) l.debug(v)
} }
@@ -56,15 +56,15 @@ func (l *richLogger) Debugw(msg string, fields ...LogField) {
l.debug(msg, fields...) l.debug(msg, fields...)
} }
func (l *richLogger) Error(v ...interface{}) { func (l *richLogger) Error(v ...any) {
l.err(fmt.Sprint(v...)) l.err(fmt.Sprint(v...))
} }
func (l *richLogger) Errorf(format string, v ...interface{}) { func (l *richLogger) Errorf(format string, v ...any) {
l.err(fmt.Sprintf(format, v...)) l.err(fmt.Sprintf(format, v...))
} }
func (l *richLogger) Errorv(v interface{}) { func (l *richLogger) Errorv(v any) {
l.err(fmt.Sprint(v)) l.err(fmt.Sprint(v))
} }
@@ -72,15 +72,15 @@ func (l *richLogger) Errorw(msg string, fields ...LogField) {
l.err(msg, fields...) l.err(msg, fields...)
} }
func (l *richLogger) Info(v ...interface{}) { func (l *richLogger) Info(v ...any) {
l.info(fmt.Sprint(v...)) l.info(fmt.Sprint(v...))
} }
func (l *richLogger) Infof(format string, v ...interface{}) { func (l *richLogger) Infof(format string, v ...any) {
l.info(fmt.Sprintf(format, v...)) l.info(fmt.Sprintf(format, v...))
} }
func (l *richLogger) Infov(v interface{}) { func (l *richLogger) Infov(v any) {
l.info(v) l.info(v)
} }
@@ -88,15 +88,15 @@ func (l *richLogger) Infow(msg string, fields ...LogField) {
l.info(msg, fields...) l.info(msg, fields...)
} }
func (l *richLogger) Slow(v ...interface{}) { func (l *richLogger) Slow(v ...any) {
l.slow(fmt.Sprint(v...)) l.slow(fmt.Sprint(v...))
} }
func (l *richLogger) Slowf(format string, v ...interface{}) { func (l *richLogger) Slowf(format string, v ...any) {
l.slow(fmt.Sprintf(format, v...)) l.slow(fmt.Sprintf(format, v...))
} }
func (l *richLogger) Slowv(v interface{}) { func (l *richLogger) Slowv(v any) {
l.slow(v) l.slow(v)
} }
@@ -156,25 +156,25 @@ func (l *richLogger) buildFields(fields ...LogField) []LogField {
return fields return fields
} }
func (l *richLogger) debug(v interface{}, fields ...LogField) { func (l *richLogger) debug(v any, fields ...LogField) {
if shallLog(DebugLevel) { if shallLog(DebugLevel) {
getWriter().Debug(v, l.buildFields(fields...)...) getWriter().Debug(v, l.buildFields(fields...)...)
} }
} }
func (l *richLogger) err(v interface{}, fields ...LogField) { func (l *richLogger) err(v any, fields ...LogField) {
if shallLog(ErrorLevel) { if shallLog(ErrorLevel) {
getWriter().Error(v, l.buildFields(fields...)...) getWriter().Error(v, l.buildFields(fields...)...)
} }
} }
func (l *richLogger) info(v interface{}, fields ...LogField) { func (l *richLogger) info(v any, fields ...LogField) {
if shallLog(InfoLevel) { if shallLog(InfoLevel) {
getWriter().Info(v, l.buildFields(fields...)...) getWriter().Info(v, l.buildFields(fields...)...)
} }
} }
func (l *richLogger) slow(v interface{}, fields ...LogField) { func (l *richLogger) slow(v any, fields ...LogField) {
if shallLog(ErrorLevel) { if shallLog(ErrorLevel) {
getWriter().Slow(v, l.buildFields(fields...)...) getWriter().Slow(v, l.buildFields(fields...)...)
} }

View File

@@ -237,7 +237,7 @@ func NewLogger(filename string, rule RotateRule, compress bool) (*RotateLogger,
rule: rule, rule: rule,
compress: compress, compress: compress,
} }
if err := l.init(); err != nil { if err := l.initialize(); err != nil {
return nil, err return nil, err
} }
@@ -281,7 +281,7 @@ func (l *RotateLogger) getBackupFilename() string {
return l.backup return l.backup
} }
func (l *RotateLogger) init() error { func (l *RotateLogger) initialize() error {
l.backup = l.rule.BackupFileName() l.backup = l.rule.BackupFileName()
if fileInfo, err := os.Stat(l.filename); err != nil { if fileInfo, err := os.Stat(l.filename); err != nil {

View File

@@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/fs" "github.com/zeromicro/go-zero/core/fs"
"github.com/zeromicro/go-zero/core/stringx"
) )
func TestDailyRotateRuleMarkRotated(t *testing.T) { func TestDailyRotateRuleMarkRotated(t *testing.T) {
@@ -232,6 +233,23 @@ func TestRotateLoggerWithSizeLimitRotateRuleMayCompressFileTrue(t *testing.T) {
assert.NotNil(t, err) assert.NotNil(t, err)
} }
func TestRotateLoggerWithSizeLimitRotateRuleMayCompressFileFailed(t *testing.T) {
old := os.Stdout
os.Stdout = os.NewFile(0, os.DevNull)
defer func() {
os.Stdout = old
}()
filename := stringx.RandId()
logger, err := NewLogger(filename, new(SizeLimitRotateRule), true)
defer os.Remove(filename)
if assert.NoError(t, err) {
assert.NotPanics(t, func() {
logger.maybeCompressFile(stringx.RandId())
})
}
}
func TestRotateLoggerWithSizeLimitRotateRuleRotate(t *testing.T) { func TestRotateLoggerWithSizeLimitRotateRuleRotate(t *testing.T) {
filename, err := fs.TempFilenameWithText("foo") filename, err := fs.TempFilenameWithText("foo")
assert.Nil(t, err) assert.Nil(t, err)

View File

@@ -42,7 +42,7 @@ func captureOutput(f func()) string {
} }
func getContent(jsonStr string) string { func getContent(jsonStr string) string {
var entry map[string]interface{} var entry map[string]any
json.Unmarshal([]byte(jsonStr), &entry) json.Unmarshal([]byte(jsonStr), &entry)
val, ok := entry[contentKey] val, ok := entry[contentKey]

View File

@@ -1,6 +1,10 @@
package logx package logx
import "errors" import (
"errors"
"github.com/zeromicro/go-zero/core/syncx"
)
const ( const (
// DebugLevel logs everything // DebugLevel logs everything
@@ -61,6 +65,8 @@ var (
ErrLogPathNotSet = errors.New("log path must be set") ErrLogPathNotSet = errors.New("log path must be set")
// ErrLogServiceNameNotSet is an error that indicates that the service name is not set. // ErrLogServiceNameNotSet is an error that indicates that the service name is not set.
ErrLogServiceNameNotSet = errors.New("log service name must be set") ErrLogServiceNameNotSet = errors.New("log service name must be set")
// ExitOnFatal defines whether to exit on fatal errors, defined here to make it easier to test.
ExitOnFatal = syncx.ForAtomicBool(true)
truncatedField = Field(truncatedKey, true) truncatedField = Field(truncatedKey, true)
) )

View File

@@ -16,15 +16,15 @@ import (
type ( type (
Writer interface { Writer interface {
Alert(v interface{}) Alert(v any)
Close() error Close() error
Debug(v interface{}, fields ...LogField) Debug(v any, fields ...LogField)
Error(v interface{}, fields ...LogField) Error(v any, fields ...LogField)
Info(v interface{}, fields ...LogField) Info(v any, fields ...LogField)
Severe(v interface{}) Severe(v any)
Slow(v interface{}, fields ...LogField) Slow(v any, fields ...LogField)
Stack(v interface{}) Stack(v any)
Stat(v interface{}, fields ...LogField) Stat(v any, fields ...LogField)
} }
atomicWriter struct { atomicWriter struct {
@@ -171,7 +171,7 @@ func newFileWriter(c LogConf) (Writer, error) {
}, nil }, nil
} }
func (w *concreteWriter) Alert(v interface{}) { func (w *concreteWriter) Alert(v any) {
output(w.errorLog, levelAlert, v) output(w.errorLog, levelAlert, v)
} }
@@ -195,62 +195,62 @@ func (w *concreteWriter) Close() error {
return w.statLog.Close() return w.statLog.Close()
} }
func (w *concreteWriter) Debug(v interface{}, fields ...LogField) { func (w *concreteWriter) Debug(v any, fields ...LogField) {
output(w.infoLog, levelDebug, v, fields...) output(w.infoLog, levelDebug, v, fields...)
} }
func (w *concreteWriter) Error(v interface{}, fields ...LogField) { func (w *concreteWriter) Error(v any, fields ...LogField) {
output(w.errorLog, levelError, v, fields...) output(w.errorLog, levelError, v, fields...)
} }
func (w *concreteWriter) Info(v interface{}, fields ...LogField) { func (w *concreteWriter) Info(v any, fields ...LogField) {
output(w.infoLog, levelInfo, v, fields...) output(w.infoLog, levelInfo, v, fields...)
} }
func (w *concreteWriter) Severe(v interface{}) { func (w *concreteWriter) Severe(v any) {
output(w.severeLog, levelFatal, v) output(w.severeLog, levelFatal, v)
} }
func (w *concreteWriter) Slow(v interface{}, fields ...LogField) { func (w *concreteWriter) Slow(v any, fields ...LogField) {
output(w.slowLog, levelSlow, v, fields...) output(w.slowLog, levelSlow, v, fields...)
} }
func (w *concreteWriter) Stack(v interface{}) { func (w *concreteWriter) Stack(v any) {
output(w.stackLog, levelError, v) output(w.stackLog, levelError, v)
} }
func (w *concreteWriter) Stat(v interface{}, fields ...LogField) { func (w *concreteWriter) Stat(v any, fields ...LogField) {
output(w.statLog, levelStat, v, fields...) output(w.statLog, levelStat, v, fields...)
} }
type nopWriter struct{} type nopWriter struct{}
func (n nopWriter) Alert(_ interface{}) { func (n nopWriter) Alert(_ any) {
} }
func (n nopWriter) Close() error { func (n nopWriter) Close() error {
return nil return nil
} }
func (n nopWriter) Debug(_ interface{}, _ ...LogField) { func (n nopWriter) Debug(_ any, _ ...LogField) {
} }
func (n nopWriter) Error(_ interface{}, _ ...LogField) { func (n nopWriter) Error(_ any, _ ...LogField) {
} }
func (n nopWriter) Info(_ interface{}, _ ...LogField) { func (n nopWriter) Info(_ any, _ ...LogField) {
} }
func (n nopWriter) Severe(_ interface{}) { func (n nopWriter) Severe(_ any) {
} }
func (n nopWriter) Slow(_ interface{}, _ ...LogField) { func (n nopWriter) Slow(_ any, _ ...LogField) {
} }
func (n nopWriter) Stack(_ interface{}) { func (n nopWriter) Stack(_ any) {
} }
func (n nopWriter) Stat(_ interface{}, _ ...LogField) { func (n nopWriter) Stat(_ any, _ ...LogField) {
} }
func buildPlainFields(fields ...LogField) []string { func buildPlainFields(fields ...LogField) []string {
@@ -277,7 +277,7 @@ func combineGlobalFields(fields []LogField) []LogField {
return ret return ret
} }
func output(writer io.Writer, level string, val interface{}, fields ...LogField) { func output(writer io.Writer, level string, val any, fields ...LogField) {
// only truncate string content, don't know how to truncate the values of other types. // only truncate string content, don't know how to truncate the values of other types.
if v, ok := val.(string); ok { if v, ok := val.(string); ok {
maxLen := atomic.LoadUint32(&maxContentLength) maxLen := atomic.LoadUint32(&maxContentLength)
@@ -330,7 +330,7 @@ func wrapLevelWithColor(level string) string {
return color.WithColorPadding(level, colour) return color.WithColorPadding(level, colour)
} }
func writeJson(writer io.Writer, info interface{}) { func writeJson(writer io.Writer, info any) {
if content, err := json.Marshal(info); err != nil { if content, err := json.Marshal(info); err != nil {
log.Println(err.Error()) log.Println(err.Error())
} else if writer == nil { } else if writer == nil {
@@ -340,7 +340,7 @@ func writeJson(writer io.Writer, info interface{}) {
} }
} }
func writePlainAny(writer io.Writer, level string, val interface{}, fields ...string) { func writePlainAny(writer io.Writer, level string, val any, fields ...string) {
level = wrapLevelWithColor(level) level = wrapLevelWithColor(level)
switch v := val.(type) { switch v := val.(type) {
@@ -377,7 +377,7 @@ func writePlainText(writer io.Writer, level, msg string, fields ...string) {
} }
} }
func writePlainValue(writer io.Writer, level string, val interface{}, fields ...string) { func writePlainValue(writer io.Writer, level string, val any, fields ...string) {
var buf bytes.Buffer var buf bytes.Buffer
buf.WriteString(getTimestamp()) buf.WriteString(getTimestamp())
buf.WriteByte(plainEncodingSep) buf.WriteByte(plainEncodingSep)

View File

@@ -108,7 +108,7 @@ func TestNopWriter(t *testing.T) {
w.Stack("foo") w.Stack("foo")
w.Stat("foo") w.Stat("foo")
w.Slow("foo") w.Slow("foo")
w.Close() _ = w.Close()
}) })
} }

View File

@@ -11,17 +11,17 @@ const jsonTagKey = "json"
var jsonUnmarshaler = NewUnmarshaler(jsonTagKey) var jsonUnmarshaler = NewUnmarshaler(jsonTagKey)
// UnmarshalJsonBytes unmarshals content into v. // UnmarshalJsonBytes unmarshals content into v.
func UnmarshalJsonBytes(content []byte, v interface{}, opts ...UnmarshalOption) error { func UnmarshalJsonBytes(content []byte, v any, opts ...UnmarshalOption) error {
return unmarshalJsonBytes(content, v, getJsonUnmarshaler(opts...)) return unmarshalJsonBytes(content, v, getJsonUnmarshaler(opts...))
} }
// UnmarshalJsonMap unmarshals content from m into v. // UnmarshalJsonMap unmarshals content from m into v.
func UnmarshalJsonMap(m map[string]interface{}, v interface{}, opts ...UnmarshalOption) error { func UnmarshalJsonMap(m map[string]any, v any, opts ...UnmarshalOption) error {
return getJsonUnmarshaler(opts...).Unmarshal(m, v) return getJsonUnmarshaler(opts...).Unmarshal(m, v)
} }
// UnmarshalJsonReader unmarshals content from reader into v. // UnmarshalJsonReader unmarshals content from reader into v.
func UnmarshalJsonReader(reader io.Reader, v interface{}, opts ...UnmarshalOption) error { func UnmarshalJsonReader(reader io.Reader, v any, opts ...UnmarshalOption) error {
return unmarshalJsonReader(reader, v, getJsonUnmarshaler(opts...)) return unmarshalJsonReader(reader, v, getJsonUnmarshaler(opts...))
} }
@@ -33,8 +33,8 @@ func getJsonUnmarshaler(opts ...UnmarshalOption) *Unmarshaler {
return jsonUnmarshaler return jsonUnmarshaler
} }
func unmarshalJsonBytes(content []byte, v interface{}, unmarshaler *Unmarshaler) error { func unmarshalJsonBytes(content []byte, v any, unmarshaler *Unmarshaler) error {
var m interface{} var m any
if err := jsonx.Unmarshal(content, &m); err != nil { if err := jsonx.Unmarshal(content, &m); err != nil {
return err return err
} }
@@ -42,8 +42,8 @@ func unmarshalJsonBytes(content []byte, v interface{}, unmarshaler *Unmarshaler)
return unmarshaler.Unmarshal(m, v) return unmarshaler.Unmarshal(m, v)
} }
func unmarshalJsonReader(reader io.Reader, v interface{}, unmarshaler *Unmarshaler) error { func unmarshalJsonReader(reader io.Reader, v any, unmarshaler *Unmarshaler) error {
var m interface{} var m any
if err := jsonx.UnmarshalFromReader(reader, &m); err != nil { if err := jsonx.UnmarshalFromReader(reader, &m); err != nil {
return err return err
} }

View File

@@ -871,7 +871,7 @@ func TestUnmarshalReaderError(t *testing.T) {
func TestUnmarshalMap(t *testing.T) { func TestUnmarshalMap(t *testing.T) {
t.Run("nil map and valid", func(t *testing.T) { t.Run("nil map and valid", func(t *testing.T) {
var m map[string]interface{} var m map[string]any
var v struct { var v struct {
Any string `json:",optional"` Any string `json:",optional"`
} }
@@ -882,7 +882,7 @@ func TestUnmarshalMap(t *testing.T) {
}) })
t.Run("empty map but not valid", func(t *testing.T) { t.Run("empty map but not valid", func(t *testing.T) {
m := map[string]interface{}{} m := map[string]any{}
var v struct { var v struct {
Any string Any string
} }
@@ -892,7 +892,7 @@ func TestUnmarshalMap(t *testing.T) {
}) })
t.Run("empty map and valid", func(t *testing.T) { t.Run("empty map and valid", func(t *testing.T) {
m := map[string]interface{}{} m := map[string]any{}
var v struct { var v struct {
Any string `json:",optional"` Any string `json:",optional"`
} }
@@ -905,7 +905,7 @@ func TestUnmarshalMap(t *testing.T) {
}) })
t.Run("valid map", func(t *testing.T) { t.Run("valid map", func(t *testing.T) {
m := map[string]interface{}{ m := map[string]any{
"Any": "foo", "Any": "foo",
} }
var v struct { var v struct {

View File

@@ -13,8 +13,8 @@ const (
// Marshal marshals the given val and returns the map that contains the fields. // Marshal marshals the given val and returns the map that contains the fields.
// optional=another is not implemented, and it's hard to implement and not common used. // optional=another is not implemented, and it's hard to implement and not common used.
func Marshal(val interface{}) (map[string]map[string]interface{}, error) { func Marshal(val any) (map[string]map[string]any, error) {
ret := make(map[string]map[string]interface{}) ret := make(map[string]map[string]any)
tp := reflect.TypeOf(val) tp := reflect.TypeOf(val)
if tp.Kind() == reflect.Ptr { if tp.Kind() == reflect.Ptr {
tp = tp.Elem() tp = tp.Elem()
@@ -45,7 +45,7 @@ func getTag(field reflect.StructField) (string, bool) {
} }
func processMember(field reflect.StructField, value reflect.Value, func processMember(field reflect.StructField, value reflect.Value,
collector map[string]map[string]interface{}) error { collector map[string]map[string]any) error {
var key string var key string
var opt *fieldOptions var opt *fieldOptions
var err error var err error
@@ -73,7 +73,7 @@ func processMember(field reflect.StructField, value reflect.Value,
if ok { if ok {
m[key] = val m[key] = val
} else { } else {
m = map[string]interface{}{ m = map[string]any{
key: val, key: val,
} }
} }

View File

@@ -227,7 +227,7 @@ func TestMarshal_Range(t *testing.T) {
} }
func TestMarshal_RangeOut(t *testing.T) { func TestMarshal_RangeOut(t *testing.T) {
tests := []interface{}{ tests := []any{
struct { struct {
Int int `json:"int,range=[1:3]"` Int int `json:"int,range=[1:3]"`
}{ }{
@@ -262,7 +262,7 @@ func TestMarshal_RangeOut(t *testing.T) {
} }
func TestMarshal_RangeIllegal(t *testing.T) { func TestMarshal_RangeIllegal(t *testing.T) {
tests := []interface{}{ tests := []any{
struct { struct {
Int int `json:"int,range=[3:1]"` Int int `json:"int,range=[3:1]"`
}{ }{
@@ -284,7 +284,7 @@ func TestMarshal_RangeIllegal(t *testing.T) {
func TestMarshal_RangeLeftEqualsToRight(t *testing.T) { func TestMarshal_RangeLeftEqualsToRight(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
value interface{} value any
err error err error
}{ }{
{ {

View File

@@ -7,7 +7,7 @@ import (
) )
// UnmarshalTomlBytes unmarshals TOML bytes into the given v. // UnmarshalTomlBytes unmarshals TOML bytes into the given v.
func UnmarshalTomlBytes(content []byte, v interface{}, opts ...UnmarshalOption) error { func UnmarshalTomlBytes(content []byte, v any, opts ...UnmarshalOption) error {
b, err := encoding.TomlToJson(content) b, err := encoding.TomlToJson(content)
if err != nil { if err != nil {
return err return err
@@ -17,7 +17,7 @@ func UnmarshalTomlBytes(content []byte, v interface{}, opts ...UnmarshalOption)
} }
// UnmarshalTomlReader unmarshals TOML from the given io.Reader into the given v. // UnmarshalTomlReader unmarshals TOML from the given io.Reader into the given v.
func UnmarshalTomlReader(r io.Reader, v interface{}, opts ...UnmarshalOption) error { func UnmarshalTomlReader(r io.Reader, v any, opts ...UnmarshalOption) error {
b, err := io.ReadAll(r) b, err := io.ReadAll(r)
if err != nil { if err != nil {
return err return err

View File

@@ -30,9 +30,9 @@ var (
durationType = reflect.TypeOf(time.Duration(0)) durationType = reflect.TypeOf(time.Duration(0))
cacheKeys = make(map[string][]string) cacheKeys = make(map[string][]string)
cacheKeysLock sync.Mutex cacheKeysLock sync.Mutex
defaultCache = make(map[string]interface{}) defaultCache = make(map[string]any)
defaultCacheLock sync.Mutex defaultCacheLock sync.Mutex
emptyMap = map[string]interface{}{} emptyMap = map[string]any{}
emptyValue = reflect.ValueOf(lang.Placeholder) emptyValue = reflect.ValueOf(lang.Placeholder)
) )
@@ -47,6 +47,7 @@ type (
UnmarshalOption func(*unmarshalOptions) UnmarshalOption func(*unmarshalOptions)
unmarshalOptions struct { unmarshalOptions struct {
fillDefault bool
fromString bool fromString bool
canonicalKey func(key string) string canonicalKey func(key string) string
} }
@@ -66,12 +67,12 @@ func NewUnmarshaler(key string, opts ...UnmarshalOption) *Unmarshaler {
} }
// UnmarshalKey unmarshals m into v with tag key. // UnmarshalKey unmarshals m into v with tag key.
func UnmarshalKey(m map[string]interface{}, v interface{}) error { func UnmarshalKey(m map[string]any, v any) error {
return keyUnmarshaler.Unmarshal(m, v) return keyUnmarshaler.Unmarshal(m, v)
} }
// Unmarshal unmarshals m into v. // Unmarshal unmarshals m into v.
func (u *Unmarshaler) Unmarshal(i interface{}, v interface{}) error { func (u *Unmarshaler) Unmarshal(i any, v any) error {
valueType := reflect.TypeOf(v) valueType := reflect.TypeOf(v)
if valueType.Kind() != reflect.Ptr { if valueType.Kind() != reflect.Ptr {
return errValueNotSettable return errValueNotSettable
@@ -79,13 +80,13 @@ func (u *Unmarshaler) Unmarshal(i interface{}, v interface{}) error {
elemType := Deref(valueType) elemType := Deref(valueType)
switch iv := i.(type) { switch iv := i.(type) {
case map[string]interface{}: case map[string]any:
if elemType.Kind() != reflect.Struct { if elemType.Kind() != reflect.Struct {
return errTypeMismatch return errTypeMismatch
} }
return u.UnmarshalValuer(mapValuer(iv), v) return u.UnmarshalValuer(mapValuer(iv), v)
case []interface{}: case []any:
if elemType.Kind() != reflect.Slice { if elemType.Kind() != reflect.Slice {
return errTypeMismatch return errTypeMismatch
} }
@@ -97,11 +98,11 @@ func (u *Unmarshaler) Unmarshal(i interface{}, v interface{}) error {
} }
// UnmarshalValuer unmarshals m into v. // UnmarshalValuer unmarshals m into v.
func (u *Unmarshaler) UnmarshalValuer(m Valuer, v interface{}) error { func (u *Unmarshaler) UnmarshalValuer(m Valuer, v any) error {
return u.unmarshalWithFullName(simpleValuer{current: m}, v, "") return u.unmarshalWithFullName(simpleValuer{current: m}, v, "")
} }
func (u *Unmarshaler) fillMap(fieldType reflect.Type, value reflect.Value, mapValue interface{}) error { func (u *Unmarshaler) fillMap(fieldType reflect.Type, value reflect.Value, mapValue any) error {
if !value.CanSet() { if !value.CanSet() {
return errValueNotSettable return errValueNotSettable
} }
@@ -121,7 +122,7 @@ func (u *Unmarshaler) fillMap(fieldType reflect.Type, value reflect.Value, mapVa
return nil return nil
} }
func (u *Unmarshaler) fillMapFromString(value reflect.Value, mapValue interface{}) error { func (u *Unmarshaler) fillMapFromString(value reflect.Value, mapValue any) error {
if !value.CanSet() { if !value.CanSet() {
return errValueNotSettable return errValueNotSettable
} }
@@ -142,7 +143,7 @@ func (u *Unmarshaler) fillMapFromString(value reflect.Value, mapValue interface{
return nil return nil
} }
func (u *Unmarshaler) fillSlice(fieldType reflect.Type, value reflect.Value, mapValue interface{}) error { func (u *Unmarshaler) fillSlice(fieldType reflect.Type, value reflect.Value, mapValue any) error {
if !value.CanSet() { if !value.CanSet() {
return errValueNotSettable return errValueNotSettable
} }
@@ -172,7 +173,7 @@ func (u *Unmarshaler) fillSlice(fieldType reflect.Type, value reflect.Value, map
switch dereffedBaseKind { switch dereffedBaseKind {
case reflect.Struct: case reflect.Struct:
target := reflect.New(dereffedBaseType) target := reflect.New(dereffedBaseType)
if err := u.Unmarshal(ithValue.(map[string]interface{}), target.Interface()); err != nil { if err := u.Unmarshal(ithValue.(map[string]any), target.Interface()); err != nil {
return err return err
} }
@@ -196,8 +197,8 @@ func (u *Unmarshaler) fillSlice(fieldType reflect.Type, value reflect.Value, map
} }
func (u *Unmarshaler) fillSliceFromString(fieldType reflect.Type, value reflect.Value, func (u *Unmarshaler) fillSliceFromString(fieldType reflect.Type, value reflect.Value,
mapValue interface{}) error { mapValue any) error {
var slice []interface{} var slice []any
switch v := mapValue.(type) { switch v := mapValue.(type) {
case fmt.Stringer: case fmt.Stringer:
if err := jsonx.UnmarshalFromString(v.String(), &slice); err != nil { if err := jsonx.UnmarshalFromString(v.String(), &slice); err != nil {
@@ -226,14 +227,14 @@ func (u *Unmarshaler) fillSliceFromString(fieldType reflect.Type, value reflect.
} }
func (u *Unmarshaler) fillSliceValue(slice reflect.Value, index int, func (u *Unmarshaler) fillSliceValue(slice reflect.Value, index int,
baseKind reflect.Kind, value interface{}) error { baseKind reflect.Kind, value any) error {
ithVal := slice.Index(index) ithVal := slice.Index(index)
switch v := value.(type) { switch v := value.(type) {
case fmt.Stringer: case fmt.Stringer:
return setValueFromString(baseKind, ithVal, v.String()) return setValueFromString(baseKind, ithVal, v.String())
case string: case string:
return setValueFromString(baseKind, ithVal, v) return setValueFromString(baseKind, ithVal, v)
case map[string]interface{}: case map[string]any:
return u.fillMap(ithVal.Type(), ithVal, value) return u.fillMap(ithVal.Type(), ithVal, value)
default: default:
// don't need to consider the difference between int, int8, int16, int32, int64, // don't need to consider the difference between int, int8, int16, int32, int64,
@@ -281,13 +282,17 @@ func (u *Unmarshaler) fillSliceWithDefault(derefedType reflect.Type, value refle
return u.fillSlice(derefedType, value, slice) return u.fillSlice(derefedType, value, slice)
} }
func (u *Unmarshaler) generateMap(keyType, elemType reflect.Type, mapValue interface{}) (reflect.Value, error) { func (u *Unmarshaler) generateMap(keyType, elemType reflect.Type, mapValue any) (reflect.Value, error) {
mapType := reflect.MapOf(keyType, elemType) mapType := reflect.MapOf(keyType, elemType)
valueType := reflect.TypeOf(mapValue) valueType := reflect.TypeOf(mapValue)
if mapType == valueType { if mapType == valueType {
return reflect.ValueOf(mapValue), nil return reflect.ValueOf(mapValue), nil
} }
if keyType != valueType.Key() {
return emptyValue, errTypeMismatch
}
refValue := reflect.ValueOf(mapValue) refValue := reflect.ValueOf(mapValue)
targetValue := reflect.MakeMapWithSize(mapType, refValue.Len()) targetValue := reflect.MakeMapWithSize(mapType, refValue.Len())
dereffedElemType := Deref(elemType) dereffedElemType := Deref(elemType)
@@ -306,7 +311,7 @@ func (u *Unmarshaler) generateMap(keyType, elemType reflect.Type, mapValue inter
targetValue.SetMapIndex(key, target.Elem()) targetValue.SetMapIndex(key, target.Elem())
case reflect.Struct: case reflect.Struct:
keythMap, ok := keythData.(map[string]interface{}) keythMap, ok := keythData.(map[string]any)
if !ok { if !ok {
return emptyValue, errTypeMismatch return emptyValue, errTypeMismatch
} }
@@ -318,7 +323,7 @@ func (u *Unmarshaler) generateMap(keyType, elemType reflect.Type, mapValue inter
SetMapIndexValue(elemType, targetValue, key, target.Elem()) SetMapIndexValue(elemType, targetValue, key, target.Elem())
case reflect.Map: case reflect.Map:
keythMap, ok := keythData.(map[string]interface{}) keythMap, ok := keythData.(map[string]any)
if !ok { if !ok {
return emptyValue, errTypeMismatch return emptyValue, errTypeMismatch
} }
@@ -485,7 +490,7 @@ func (u *Unmarshaler) processAnonymousStructFieldOptional(fieldType reflect.Type
} }
if filled && required != requiredFilled { if filled && required != requiredFilled {
return fmt.Errorf("%s is not fully set", key) return fmt.Errorf("%q is not fully set", key)
} }
return nil return nil
@@ -513,7 +518,7 @@ func (u *Unmarshaler) processFieldNotFromString(fieldType reflect.Type, value re
switch { switch {
case valueKind == reflect.Map && typeKind == reflect.Struct: case valueKind == reflect.Map && typeKind == reflect.Struct:
mv, ok := mapValue.(map[string]interface{}) mv, ok := mapValue.(map[string]any)
if !ok { if !ok {
return errTypeMismatch return errTypeMismatch
} }
@@ -536,7 +541,7 @@ func (u *Unmarshaler) processFieldNotFromString(fieldType reflect.Type, value re
} }
func (u *Unmarshaler) processFieldPrimitive(fieldType reflect.Type, value reflect.Value, func (u *Unmarshaler) processFieldPrimitive(fieldType reflect.Type, value reflect.Value,
mapValue interface{}, opts *fieldOptionsWithContext, fullName string) error { mapValue any, opts *fieldOptionsWithContext, fullName string) error {
typeKind := Deref(fieldType).Kind() typeKind := Deref(fieldType).Kind()
valueKind := reflect.TypeOf(mapValue).Kind() valueKind := reflect.TypeOf(mapValue).Kind()
@@ -631,7 +636,7 @@ func (u *Unmarshaler) processFieldStruct(fieldType reflect.Type, value reflect.V
} }
func (u *Unmarshaler) processFieldTextUnmarshaler(fieldType reflect.Type, value reflect.Value, func (u *Unmarshaler) processFieldTextUnmarshaler(fieldType reflect.Type, value reflect.Value,
mapValue interface{}) (bool, error) { mapValue any) (bool, error) {
var tval encoding.TextUnmarshaler var tval encoding.TextUnmarshaler
var ok bool var ok bool
@@ -690,6 +695,10 @@ func (u *Unmarshaler) processFieldWithEnvValue(fieldType reflect.Type, value ref
func (u *Unmarshaler) processNamedField(field reflect.StructField, value reflect.Value, func (u *Unmarshaler) processNamedField(field reflect.StructField, value reflect.Value,
m valuerWithParent, fullName string) error { m valuerWithParent, fullName string) error {
if !field.IsExported() {
return nil
}
key, opts, err := u.parseOptionsWithContext(field, m, fullName) key, opts, err := u.parseOptionsWithContext(field, m, fullName)
if err != nil { if err != nil {
return err return err
@@ -710,7 +719,14 @@ func (u *Unmarshaler) processNamedField(field reflect.StructField, value reflect
valuer := createValuer(m, opts) valuer := createValuer(m, opts)
mapValue, hasValue := getValue(valuer, canonicalKey) mapValue, hasValue := getValue(valuer, canonicalKey)
if !hasValue {
// When fillDefault is used, m is a null value, hasValue must be false, all priority judgments fillDefault.
if u.opts.fillDefault {
if !value.IsZero() {
return fmt.Errorf("set the default value, %q must be zero", fullName)
}
return u.processNamedFieldWithoutValue(field.Type, value, opts, fullName)
} else if !hasValue {
return u.processNamedFieldWithoutValue(field.Type, value, opts, fullName) return u.processNamedFieldWithoutValue(field.Type, value, opts, fullName)
} }
@@ -728,11 +744,11 @@ func (u *Unmarshaler) processNamedFieldWithValue(fieldType reflect.Type, value r
return nil return nil
} }
return fmt.Errorf("field %s mustn't be nil", key) return fmt.Errorf("field %q mustn't be nil", key)
} }
if !value.CanSet() { if !value.CanSet() {
return fmt.Errorf("field %s is not settable", key) return fmt.Errorf("field %q is not settable", key)
} }
maybeNewValue(fieldType, value) maybeNewValue(fieldType, value)
@@ -756,7 +772,7 @@ func (u *Unmarshaler) processNamedFieldWithValue(fieldType reflect.Type, value r
} }
func (u *Unmarshaler) processNamedFieldWithValueFromString(fieldType reflect.Type, value reflect.Value, func (u *Unmarshaler) processNamedFieldWithValueFromString(fieldType reflect.Type, value reflect.Value,
mapValue interface{}, key string, opts *fieldOptionsWithContext, fullName string) error { mapValue any, key string, opts *fieldOptionsWithContext, fullName string) error {
valueKind := reflect.TypeOf(mapValue).Kind() valueKind := reflect.TypeOf(mapValue).Kind()
if valueKind != reflect.String { if valueKind != reflect.String {
return fmt.Errorf("the value in map is not string, but %s", valueKind) return fmt.Errorf("the value in map is not string, but %s", valueKind)
@@ -776,7 +792,7 @@ func (u *Unmarshaler) processNamedFieldWithValueFromString(fieldType reflect.Typ
} }
if !stringx.Contains(options, checkValue) { if !stringx.Contains(options, checkValue) {
return fmt.Errorf(`value "%s" for field "%s" is not defined in options "%v"`, return fmt.Errorf(`value "%s" for field %q is not defined in options "%v"`,
mapValue, key, options) mapValue, key, options)
} }
} }
@@ -801,6 +817,15 @@ func (u *Unmarshaler) processNamedFieldWithoutValue(fieldType reflect.Type, valu
} }
} }
if u.opts.fillDefault {
if fieldType.Kind() != reflect.Ptr && fieldKind == reflect.Struct {
return u.processFieldNotFromString(fieldType, value, valueWithParent{
value: emptyMap,
}, opts, fullName)
}
return nil
}
switch fieldKind { switch fieldKind {
case reflect.Array, reflect.Map, reflect.Slice: case reflect.Array, reflect.Map, reflect.Slice:
if !opts.optional() { if !opts.optional() {
@@ -832,7 +857,7 @@ func (u *Unmarshaler) processNamedFieldWithoutValue(fieldType reflect.Type, valu
return nil return nil
} }
func (u *Unmarshaler) unmarshalWithFullName(m valuerWithParent, v interface{}, fullName string) error { func (u *Unmarshaler) unmarshalWithFullName(m valuerWithParent, v any, fullName string) error {
rv := reflect.ValueOf(v) rv := reflect.ValueOf(v)
if err := ValidatePtr(&rv); err != nil { if err := ValidatePtr(&rv); err != nil {
return err return err
@@ -853,7 +878,14 @@ func (u *Unmarshaler) unmarshalWithFullName(m valuerWithParent, v interface{}, f
numFields := baseType.NumField() numFields := baseType.NumField()
for i := 0; i < numFields; i++ { for i := 0; i < numFields; i++ {
if err := u.processField(baseType.Field(i), valElem.Field(i), m, fullName); err != nil { typeField := baseType.Field(i)
valueField := valElem.Field(i)
if err := u.processField(typeField, valueField, m, fullName); err != nil {
if len(fullName) > 0 {
err = fmt.Errorf("%w, fullName: %s, field: %s, type: %s",
err, fullName, typeField.Name, valueField.Type().Name())
}
return err return err
} }
} }
@@ -868,13 +900,20 @@ func WithStringValues() UnmarshalOption {
} }
} }
// WithCanonicalKeyFunc customizes an Unmarshaler with Canonical Key func // WithCanonicalKeyFunc customizes an Unmarshaler with Canonical Key func.
func WithCanonicalKeyFunc(f func(string) string) UnmarshalOption { func WithCanonicalKeyFunc(f func(string) string) UnmarshalOption {
return func(opt *unmarshalOptions) { return func(opt *unmarshalOptions) {
opt.canonicalKey = f opt.canonicalKey = f
} }
} }
// WithDefault customizes an Unmarshaler with fill default values.
func WithDefault() UnmarshalOption {
return func(opt *unmarshalOptions) {
opt.fillDefault = true
}
}
func createValuer(v valuerWithParent, opts *fieldOptionsWithContext) valuerWithParent { func createValuer(v valuerWithParent, opts *fieldOptionsWithContext) valuerWithParent {
if opts.inherit() { if opts.inherit() {
return recursiveValuer{ return recursiveValuer{
@@ -900,7 +939,7 @@ func fillDurationValue(fieldType reflect.Type, value reflect.Value, dur string)
return nil return nil
} }
func fillPrimitive(fieldType reflect.Type, value reflect.Value, mapValue interface{}, func fillPrimitive(fieldType reflect.Type, value reflect.Value, mapValue any,
opts *fieldOptionsWithContext, fullName string) error { opts *fieldOptionsWithContext, fullName string) error {
if !value.CanSet() { if !value.CanSet() {
return errValueNotSettable return errValueNotSettable
@@ -929,7 +968,7 @@ func fillPrimitive(fieldType reflect.Type, value reflect.Value, mapValue interfa
} }
} }
func fillWithSameType(fieldType reflect.Type, value reflect.Value, mapValue interface{}, func fillWithSameType(fieldType reflect.Type, value reflect.Value, mapValue any,
opts *fieldOptionsWithContext) error { opts *fieldOptionsWithContext) error {
if !value.CanSet() { if !value.CanSet() {
return errValueNotSettable return errValueNotSettable
@@ -952,12 +991,12 @@ func fillWithSameType(fieldType reflect.Type, value reflect.Value, mapValue inte
} }
// getValue gets the value for the specific key, the key can be in the format of parentKey.childKey // getValue gets the value for the specific key, the key can be in the format of parentKey.childKey
func getValue(m valuerWithParent, key string) (interface{}, bool) { func getValue(m valuerWithParent, key string) (any, bool) {
keys := readKeys(key) keys := readKeys(key)
return getValueWithChainedKeys(m, keys) return getValueWithChainedKeys(m, keys)
} }
func getValueWithChainedKeys(m valuerWithParent, keys []string) (interface{}, bool) { func getValueWithChainedKeys(m valuerWithParent, keys []string) (any, bool) {
switch len(keys) { switch len(keys) {
case 0: case 0:
return nil, false return nil, false
@@ -966,7 +1005,7 @@ func getValueWithChainedKeys(m valuerWithParent, keys []string) (interface{}, bo
return v, ok return v, ok
default: default:
if v, ok := m.Value(keys[0]); ok { if v, ok := m.Value(keys[0]); ok {
if nextm, ok := v.(map[string]interface{}); ok { if nextm, ok := v.(map[string]any); ok {
return getValueWithChainedKeys(recursiveValuer{ return getValueWithChainedKeys(recursiveValuer{
current: mapValuer(nextm), current: mapValuer(nextm),
parent: m, parent: m,
@@ -1000,11 +1039,11 @@ func join(elem ...string) string {
} }
func newInitError(name string) error { func newInitError(name string) error {
return fmt.Errorf("field %s is not set", name) return fmt.Errorf("field %q is not set", name)
} }
func newTypeMismatchError(name string) error { func newTypeMismatchError(name string) error {
return fmt.Errorf("error: type mismatch for field %s", name) return fmt.Errorf("type mismatch for field %q", name)
} }
func readKeys(key string) []string { func readKeys(key string) []string {
@@ -1025,7 +1064,7 @@ func readKeys(key string) []string {
return keys return keys
} }
func setSameKindValue(targetType reflect.Type, target reflect.Value, value interface{}) { func setSameKindValue(targetType reflect.Type, target reflect.Value, value any) {
if reflect.ValueOf(value).Type().AssignableTo(targetType) { if reflect.ValueOf(value).Type().AssignableTo(targetType) {
target.Set(reflect.ValueOf(value)) target.Set(reflect.ValueOf(value))
} else { } else {

File diff suppressed because it is too large Load Diff

View File

@@ -64,7 +64,7 @@ func Deref(t reflect.Type) reflect.Type {
} }
// Repr returns the string representation of v. // Repr returns the string representation of v.
func Repr(v interface{}) string { func Repr(v any) string {
return lang.Repr(v) return lang.Repr(v)
} }
@@ -89,7 +89,7 @@ func ValidatePtr(v *reflect.Value) error {
return nil return nil
} }
func convertTypeFromString(kind reflect.Kind, str string) (interface{}, error) { func convertTypeFromString(kind reflect.Kind, str string) (any, error) {
switch kind { switch kind {
case reflect.Bool: case reflect.Bool:
switch strings.ToLower(str) { switch strings.ToLower(str) {
@@ -255,7 +255,7 @@ func parseGroupedSegments(val string) []string {
// don't modify returned fieldOptions, it's cached and shared among different calls. // don't modify returned fieldOptions, it's cached and shared among different calls.
func parseKeyAndOptions(tagName string, field reflect.StructField) (string, *fieldOptions, error) { func parseKeyAndOptions(tagName string, field reflect.StructField) (string, *fieldOptions, error) {
value := field.Tag.Get(tagName) value := strings.TrimSpace(field.Tag.Get(tagName))
if len(value) == 0 { if len(value) == 0 {
return field.Name, nil, nil return field.Name, nil, nil
} }
@@ -370,7 +370,7 @@ func parseOption(fieldOpts *fieldOptions, fieldName, option string) error {
fieldOpts.Optional = true fieldOpts.Optional = true
fieldOpts.OptionalDep = segs[1] fieldOpts.OptionalDep = segs[1]
default: default:
return fmt.Errorf("field %s has wrong optional", fieldName) return fmt.Errorf("field %q has wrong optional", fieldName)
} }
case option == optionalOption: case option == optionalOption:
fieldOpts.Optional = true fieldOpts.Optional = true
@@ -429,7 +429,7 @@ func parseOptions(val string) []string {
func parseProperty(field, tag, val string) (string, error) { func parseProperty(field, tag, val string) (string, error) {
segs := strings.Split(val, equalToken) segs := strings.Split(val, equalToken)
if len(segs) != 2 { if len(segs) != 2 {
return "", fmt.Errorf("field %s has wrong %s", field, tag) return "", fmt.Errorf("field %q has wrong tag value %q", field, tag)
} }
return strings.TrimSpace(segs[1]), nil return strings.TrimSpace(segs[1]), nil
@@ -484,7 +484,7 @@ func parseSegments(val string) []string {
return segments return segments
} }
func setMatchedPrimitiveValue(kind reflect.Kind, value reflect.Value, v interface{}) error { func setMatchedPrimitiveValue(kind reflect.Kind, value reflect.Value, v any) error {
switch kind { switch kind {
case reflect.Bool: case reflect.Bool:
value.SetBool(v.(bool)) value.SetBool(v.(bool))
@@ -536,7 +536,7 @@ func structValueRequired(tag string, tp reflect.Type) (bool, error) {
return required, err return required, err
} }
func toFloat64(v interface{}) (float64, bool) { func toFloat64(v any) (float64, bool) {
switch val := v.(type) { switch val := v.(type) {
case int: case int:
return float64(val), true return float64(val), true
@@ -623,12 +623,12 @@ func validateNumberRange(fv float64, nr *numberRange) error {
return nil return nil
} }
func validateValueInOptions(val interface{}, options []string) error { func validateValueInOptions(val any, options []string) error {
if len(options) > 0 { if len(options) > 0 {
switch v := val.(type) { switch v := val.(type) {
case string: case string:
if !stringx.Contains(options, v) { if !stringx.Contains(options, v) {
return fmt.Errorf(`error: value "%s" is not defined in options "%v"`, v, options) return fmt.Errorf(`error: value %q is not defined in options "%v"`, v, options)
} }
default: default:
if !stringx.Contains(options, Repr(v)) { if !stringx.Contains(options, Repr(v)) {
@@ -640,7 +640,7 @@ func validateValueInOptions(val interface{}, options []string) error {
return nil return nil
} }
func validateValueRange(mapValue interface{}, opts *fieldOptionsWithContext) error { func validateValueRange(mapValue any, opts *fieldOptionsWithContext) error {
if opts == nil || opts.Range == nil { if opts == nil || opts.Range == nil {
return nil return nil
} }

View File

@@ -144,6 +144,10 @@ func TestParseSegments(t *testing.T) {
input: "", input: "",
expect: []string{}, expect: []string{},
}, },
{
input: " ",
expect: []string{},
},
{ {
input: ",", input: ",",
expect: []string{""}, expect: []string{""},
@@ -258,7 +262,7 @@ func TestSetValueFormatErrors(t *testing.T) {
IntValue int IntValue int
UintValue uint UintValue uint
FloatValue float32 FloatValue float32
MapValue map[string]interface{} MapValue map[string]any
} }
var bar Bar var bar Bar

View File

@@ -4,7 +4,7 @@ type (
// A Valuer interface defines the way to get values from the underlying object with keys. // A Valuer interface defines the way to get values from the underlying object with keys.
Valuer interface { Valuer interface {
// Value gets the value associated with the given key. // Value gets the value associated with the given key.
Value(key string) (interface{}, bool) Value(key string) (any, bool)
} }
// A valuerWithParent defines a node that has a parent node. // A valuerWithParent defines a node that has a parent node.
@@ -22,26 +22,26 @@ type (
// A valueWithParent is used to wrap the value with its parent. // A valueWithParent is used to wrap the value with its parent.
valueWithParent struct { valueWithParent struct {
value interface{} value any
parent valuerWithParent parent valuerWithParent
} }
// mapValuer is a type for map to meet the Valuer interface. // mapValuer is a type for map to meet the Valuer interface.
mapValuer map[string]interface{} mapValuer map[string]any
// simpleValuer is a type to get value from current node. // simpleValuer is a type to get value from current node.
simpleValuer node simpleValuer node
// recursiveValuer is a type to get the value recursively from current and parent nodes. // recursiveValuer is a type to get the value recursively from current and parent nodes.
recursiveValuer node recursiveValuer node
) )
// Value gets the value assciated with the given key from mv. // Value gets the value associated with the given key from mv.
func (mv mapValuer) Value(key string) (interface{}, bool) { func (mv mapValuer) Value(key string) (any, bool) {
v, ok := mv[key] v, ok := mv[key]
return v, ok return v, ok
} }
// Value gets the value associated with the given key from sv. // Value gets the value associated with the given key from sv.
func (sv simpleValuer) Value(key string) (interface{}, bool) { func (sv simpleValuer) Value(key string) (any, bool) {
v, ok := sv.current.Value(key) v, ok := sv.current.Value(key)
return v, ok return v, ok
} }
@@ -60,7 +60,7 @@ func (sv simpleValuer) Parent() valuerWithParent {
// Value gets the value associated with the given key from rv, // Value gets the value associated with the given key from rv,
// and it will inherit the value from parent nodes. // and it will inherit the value from parent nodes.
func (rv recursiveValuer) Value(key string) (interface{}, bool) { func (rv recursiveValuer) Value(key string) (any, bool) {
val, ok := rv.current.Value(key) val, ok := rv.current.Value(key)
if !ok { if !ok {
if parent := rv.Parent(); parent != nil { if parent := rv.Parent(); parent != nil {
@@ -70,7 +70,7 @@ func (rv recursiveValuer) Value(key string) (interface{}, bool) {
return nil, false return nil, false
} }
vm, ok := val.(map[string]interface{}) vm, ok := val.(map[string]any)
if !ok { if !ok {
return val, true return val, true
} }
@@ -85,7 +85,7 @@ func (rv recursiveValuer) Value(key string) (interface{}, bool) {
return val, true return val, true
} }
pm, ok := pv.(map[string]interface{}) pm, ok := pv.(map[string]any)
if !ok { if !ok {
return val, true return val, true
} }

View File

@@ -7,17 +7,17 @@ import (
) )
func TestMapValuerWithInherit_Value(t *testing.T) { func TestMapValuerWithInherit_Value(t *testing.T) {
input := map[string]interface{}{ input := map[string]any{
"discovery": map[string]interface{}{ "discovery": map[string]any{
"host": "localhost", "host": "localhost",
"port": 8080, "port": 8080,
}, },
"component": map[string]interface{}{ "component": map[string]any{
"name": "test", "name": "test",
}, },
} }
valuer := recursiveValuer{ valuer := recursiveValuer{
current: mapValuer(input["component"].(map[string]interface{})), current: mapValuer(input["component"].(map[string]any)),
parent: simpleValuer{ parent: simpleValuer{
current: mapValuer(input), current: mapValuer(input),
}, },
@@ -26,24 +26,24 @@ func TestMapValuerWithInherit_Value(t *testing.T) {
val, ok := valuer.Value("discovery") val, ok := valuer.Value("discovery")
assert.True(t, ok) assert.True(t, ok)
m, ok := val.(map[string]interface{}) m, ok := val.(map[string]any)
assert.True(t, ok) assert.True(t, ok)
assert.Equal(t, "localhost", m["host"]) assert.Equal(t, "localhost", m["host"])
assert.Equal(t, 8080, m["port"]) assert.Equal(t, 8080, m["port"])
} }
func TestRecursiveValuer_Value(t *testing.T) { func TestRecursiveValuer_Value(t *testing.T) {
input := map[string]interface{}{ input := map[string]any{
"component": map[string]interface{}{ "component": map[string]any{
"name": "test", "name": "test",
"foo": map[string]interface{}{ "foo": map[string]any{
"bar": "baz", "bar": "baz",
}, },
}, },
"foo": "value", "foo": "value",
} }
valuer := recursiveValuer{ valuer := recursiveValuer{
current: mapValuer(input["component"].(map[string]interface{})), current: mapValuer(input["component"].(map[string]any)),
parent: simpleValuer{ parent: simpleValuer{
current: mapValuer(input), current: mapValuer(input),
}, },
@@ -51,7 +51,7 @@ func TestRecursiveValuer_Value(t *testing.T) {
val, ok := valuer.Value("foo") val, ok := valuer.Value("foo")
assert.True(t, ok) assert.True(t, ok)
assert.EqualValues(t, map[string]interface{}{ assert.EqualValues(t, map[string]any{
"bar": "baz", "bar": "baz",
}, val) }, val)
} }

View File

@@ -7,7 +7,7 @@ import (
) )
// UnmarshalYamlBytes unmarshals content into v. // UnmarshalYamlBytes unmarshals content into v.
func UnmarshalYamlBytes(content []byte, v interface{}, opts ...UnmarshalOption) error { func UnmarshalYamlBytes(content []byte, v any, opts ...UnmarshalOption) error {
b, err := encoding.YamlToJson(content) b, err := encoding.YamlToJson(content)
if err != nil { if err != nil {
return err return err
@@ -17,7 +17,7 @@ func UnmarshalYamlBytes(content []byte, v interface{}, opts ...UnmarshalOption)
} }
// UnmarshalYamlReader unmarshals content from reader into v. // UnmarshalYamlReader unmarshals content from reader into v.
func UnmarshalYamlReader(reader io.Reader, v interface{}, opts ...UnmarshalOption) error { func UnmarshalYamlReader(reader io.Reader, v any, opts ...UnmarshalOption) error {
b, err := io.ReadAll(reader) b, err := io.ReadAll(reader)
if err != nil { if err != nil {
return err return err

View File

@@ -5,7 +5,7 @@ import "math"
const epsilon = 1e-6 const epsilon = 1e-6
// CalcEntropy calculates the entropy of m. // CalcEntropy calculates the entropy of m.
func CalcEntropy(m map[interface{}]int) float64 { func CalcEntropy(m map[any]int) float64 {
if len(m) == 0 || len(m) == 1 { if len(m) == 0 || len(m) == 1 {
return 1 return 1
} }

View File

@@ -9,7 +9,7 @@ import (
func TestCalcEntropy(t *testing.T) { func TestCalcEntropy(t *testing.T) {
const total = 1000 const total = 1000
const count = 100 const count = 100
m := make(map[interface{}]int, total) m := make(map[any]int, total)
for i := 0; i < total; i++ { for i := 0; i < total; i++ {
m[i] = count m[i] = count
} }

View File

@@ -61,7 +61,7 @@ func TestUnstable_Distribution(t *testing.T) {
_, ok := m[0] _, ok := m[0]
assert.False(t, ok) assert.False(t, ok)
mi := make(map[interface{}]int, len(m)) mi := make(map[any]int, len(m))
for k, v := range m { for k, v := range m {
mi[k] = v mi[k] = v
} }

View File

@@ -5,6 +5,7 @@ import (
"github.com/prometheus/client_golang/prometheus/testutil" "github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/proc"
"github.com/zeromicro/go-zero/core/prometheus" "github.com/zeromicro/go-zero/core/prometheus"
) )
@@ -17,6 +18,9 @@ func TestNewCounterVec(t *testing.T) {
}) })
defer counterVec.close() defer counterVec.close()
counterVecNil := NewCounterVec(nil) counterVecNil := NewCounterVec(nil)
counterVec.Inc("path", "code")
counterVec.Add(1, "path", "code")
proc.Shutdown()
assert.NotNil(t, counterVec) assert.NotNil(t, counterVec)
assert.Nil(t, counterVecNil) assert.Nil(t, counterVecNil)
} }

View File

@@ -5,6 +5,7 @@ import (
"github.com/prometheus/client_golang/prometheus/testutil" "github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/proc"
) )
func TestNewGaugeVec(t *testing.T) { func TestNewGaugeVec(t *testing.T) {
@@ -18,6 +19,8 @@ func TestNewGaugeVec(t *testing.T) {
gaugeVecNil := NewGaugeVec(nil) gaugeVecNil := NewGaugeVec(nil)
assert.NotNil(t, gaugeVec) assert.NotNil(t, gaugeVec)
assert.Nil(t, gaugeVecNil) assert.Nil(t, gaugeVecNil)
proc.Shutdown()
} }
func TestGaugeInc(t *testing.T) { func TestGaugeInc(t *testing.T) {

View File

@@ -6,6 +6,7 @@ import (
"github.com/prometheus/client_golang/prometheus/testutil" "github.com/prometheus/client_golang/prometheus/testutil"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/core/proc"
) )
func TestNewHistogramVec(t *testing.T) { func TestNewHistogramVec(t *testing.T) {
@@ -47,4 +48,6 @@ func TestHistogramObserve(t *testing.T) {
err := testutil.CollectAndCompare(hv.histogram, strings.NewReader(metadata+val)) err := testutil.CollectAndCompare(hv.histogram, strings.NewReader(metadata+val))
assert.Nil(t, err) assert.Nil(t, err)
proc.Shutdown()
} }

View File

@@ -7,7 +7,6 @@ import (
"sync/atomic" "sync/atomic"
"github.com/zeromicro/go-zero/core/errorx" "github.com/zeromicro/go-zero/core/errorx"
"github.com/zeromicro/go-zero/core/lang"
) )
const ( const (
@@ -24,30 +23,30 @@ var (
type ( type (
// ForEachFunc is used to do element processing, but no output. // ForEachFunc is used to do element processing, but no output.
ForEachFunc func(item interface{}) ForEachFunc[T any] func(item T)
// GenerateFunc is used to let callers send elements into source. // GenerateFunc is used to let callers send elements into source.
GenerateFunc func(source chan<- interface{}) GenerateFunc[T any] func(source chan<- T)
// MapFunc is used to do element processing and write the output to writer. // MapFunc is used to do element processing and write the output to writer.
MapFunc func(item interface{}, writer Writer) MapFunc[T, U any] func(item T, writer Writer[U])
// MapperFunc is used to do element processing and write the output to writer, // MapperFunc is used to do element processing and write the output to writer,
// use cancel func to cancel the processing. // use cancel func to cancel the processing.
MapperFunc func(item interface{}, writer Writer, cancel func(error)) MapperFunc[T, U any] func(item T, writer Writer[U], cancel func(error))
// ReducerFunc is used to reduce all the mapping output and write to writer, // ReducerFunc is used to reduce all the mapping output and write to writer,
// use cancel func to cancel the processing. // use cancel func to cancel the processing.
ReducerFunc func(pipe <-chan interface{}, writer Writer, cancel func(error)) ReducerFunc[U, V any] func(pipe <-chan U, writer Writer[V], cancel func(error))
// VoidReducerFunc is used to reduce all the mapping output, but no output. // VoidReducerFunc is used to reduce all the mapping output, but no output.
// Use cancel func to cancel the processing. // Use cancel func to cancel the processing.
VoidReducerFunc func(pipe <-chan interface{}, cancel func(error)) VoidReducerFunc[U any] func(pipe <-chan U, cancel func(error))
// Option defines the method to customize the mapreduce. // Option defines the method to customize the mapreduce.
Option func(opts *mapReduceOptions) Option func(opts *mapReduceOptions)
mapperContext struct { mapperContext[T, U any] struct {
ctx context.Context ctx context.Context
mapper MapFunc mapper MapFunc[T, U]
source <-chan interface{} source <-chan T
panicChan *onceChan panicChan *onceChan
collector chan<- interface{} collector chan<- U
doneChan <-chan lang.PlaceholderType doneChan <-chan struct{}
workers int workers int
} }
@@ -57,8 +56,8 @@ type (
} }
// Writer interface wraps Write method. // Writer interface wraps Write method.
Writer interface { Writer[T any] interface {
Write(v interface{}) Write(v T)
} }
) )
@@ -68,16 +67,15 @@ func Finish(fns ...func() error) error {
return nil return nil
} }
return MapReduceVoid(func(source chan<- interface{}) { return MapReduceVoid(func(source chan<- func() error) {
for _, fn := range fns { for _, fn := range fns {
source <- fn source <- fn
} }
}, func(item interface{}, writer Writer, cancel func(error)) { }, func(fn func() error, writer Writer[any], cancel func(error)) {
fn := item.(func() error)
if err := fn(); err != nil { if err := fn(); err != nil {
cancel(err) cancel(err)
} }
}, func(pipe <-chan interface{}, cancel func(error)) { }, func(pipe <-chan any, cancel func(error)) {
}, WithWorkers(len(fns))) }, WithWorkers(len(fns)))
} }
@@ -87,27 +85,26 @@ func FinishVoid(fns ...func()) {
return return
} }
ForEach(func(source chan<- interface{}) { ForEach(func(source chan<- func()) {
for _, fn := range fns { for _, fn := range fns {
source <- fn source <- fn
} }
}, func(item interface{}) { }, func(fn func()) {
fn := item.(func())
fn() fn()
}, WithWorkers(len(fns))) }, WithWorkers(len(fns)))
} }
// ForEach maps all elements from given generate but no output. // ForEach maps all elements from given generate but no output.
func ForEach(generate GenerateFunc, mapper ForEachFunc, opts ...Option) { func ForEach[T any](generate GenerateFunc[T], mapper ForEachFunc[T], opts ...Option) {
options := buildOptions(opts...) options := buildOptions(opts...)
panicChan := &onceChan{channel: make(chan interface{})} panicChan := &onceChan{channel: make(chan any)}
source := buildSource(generate, panicChan) source := buildSource(generate, panicChan)
collector := make(chan interface{}) collector := make(chan any)
done := make(chan lang.PlaceholderType) done := make(chan struct{})
go executeMappers(mapperContext{ go executeMappers(mapperContext[T, any]{
ctx: options.ctx, ctx: options.ctx,
mapper: func(item interface{}, _ Writer) { mapper: func(item T, _ Writer[any]) {
mapper(item) mapper(item)
}, },
source: source, source: source,
@@ -131,26 +128,26 @@ func ForEach(generate GenerateFunc, mapper ForEachFunc, opts ...Option) {
// MapReduce maps all elements generated from given generate func, // MapReduce maps all elements generated from given generate func,
// and reduces the output elements with given reducer. // and reduces the output elements with given reducer.
func MapReduce(generate GenerateFunc, mapper MapperFunc, reducer ReducerFunc, func MapReduce[T, U, V any](generate GenerateFunc[T], mapper MapperFunc[T, U], reducer ReducerFunc[U, V],
opts ...Option) (interface{}, error) { opts ...Option) (V, error) {
panicChan := &onceChan{channel: make(chan interface{})} panicChan := &onceChan{channel: make(chan any)}
source := buildSource(generate, panicChan) source := buildSource(generate, panicChan)
return mapReduceWithPanicChan(source, panicChan, mapper, reducer, opts...) return mapReduceWithPanicChan(source, panicChan, mapper, reducer, opts...)
} }
// MapReduceChan maps all elements from source, and reduce the output elements with given reducer. // MapReduceChan maps all elements from source, and reduce the output elements with given reducer.
func MapReduceChan(source <-chan interface{}, mapper MapperFunc, reducer ReducerFunc, func MapReduceChan[T, U, V any](source <-chan T, mapper MapperFunc[T, U], reducer ReducerFunc[U, V],
opts ...Option) (interface{}, error) { opts ...Option) (V, error) {
panicChan := &onceChan{channel: make(chan interface{})} panicChan := &onceChan{channel: make(chan any)}
return mapReduceWithPanicChan(source, panicChan, mapper, reducer, opts...) return mapReduceWithPanicChan(source, panicChan, mapper, reducer, opts...)
} }
// mapReduceWithPanicChan maps all elements from source, and reduce the output elements with given reducer. // mapReduceWithPanicChan maps all elements from source, and reduce the output elements with given reducer.
func mapReduceWithPanicChan(source <-chan interface{}, panicChan *onceChan, mapper MapperFunc, func mapReduceWithPanicChan[T, U, V any](source <-chan T, panicChan *onceChan, mapper MapperFunc[T, U],
reducer ReducerFunc, opts ...Option) (interface{}, error) { reducer ReducerFunc[U, V], opts ...Option) (val V, err error) {
options := buildOptions(opts...) options := buildOptions(opts...)
// output is used to write the final result // output is used to write the final result
output := make(chan interface{}) output := make(chan V)
defer func() { defer func() {
// reducer can only write once, if more, panic // reducer can only write once, if more, panic
for range output { for range output {
@@ -159,12 +156,12 @@ func mapReduceWithPanicChan(source <-chan interface{}, panicChan *onceChan, mapp
}() }()
// collector is used to collect data from mapper, and consume in reducer // collector is used to collect data from mapper, and consume in reducer
collector := make(chan interface{}, options.workers) collector := make(chan U, options.workers)
// if done is closed, all mappers and reducer should stop processing // if done is closed, all mappers and reducer should stop processing
done := make(chan lang.PlaceholderType) done := make(chan struct{})
writer := newGuardedWriter(options.ctx, output, done) writer := newGuardedWriter(options.ctx, output, done)
var closeOnce sync.Once var closeOnce sync.Once
// use atomic.Value to avoid data race // use atomic type to avoid data race
var retErr errorx.AtomicError var retErr errorx.AtomicError
finish := func() { finish := func() {
closeOnce.Do(func() { closeOnce.Do(func() {
@@ -195,9 +192,9 @@ func mapReduceWithPanicChan(source <-chan interface{}, panicChan *onceChan, mapp
reducer(collector, writer, cancel) reducer(collector, writer, cancel)
}() }()
go executeMappers(mapperContext{ go executeMappers(mapperContext[T, U]{
ctx: options.ctx, ctx: options.ctx,
mapper: func(item interface{}, w Writer) { mapper: func(item T, w Writer[U]) {
mapper(item, w, cancel) mapper(item, w, cancel)
}, },
source: source, source: source,
@@ -210,26 +207,29 @@ func mapReduceWithPanicChan(source <-chan interface{}, panicChan *onceChan, mapp
select { select {
case <-options.ctx.Done(): case <-options.ctx.Done():
cancel(context.DeadlineExceeded) cancel(context.DeadlineExceeded)
return nil, context.DeadlineExceeded err = context.DeadlineExceeded
case v := <-panicChan.channel: case v := <-panicChan.channel:
// drain output here, otherwise for loop panic in defer // drain output here, otherwise for loop panic in defer
drain(output) drain(output)
panic(v) panic(v)
case v, ok := <-output: case v, ok := <-output:
if err := retErr.Load(); err != nil { if e := retErr.Load(); e != nil {
return nil, err err = e
} else if ok { } else if ok {
return v, nil val = v
} else { } else {
return nil, ErrReduceNoOutput err = ErrReduceNoOutput
} }
} }
return
} }
// MapReduceVoid maps all elements generated from given generate, // MapReduceVoid maps all elements generated from given generate,
// and reduce the output elements with given reducer. // and reduce the output elements with given reducer.
func MapReduceVoid(generate GenerateFunc, mapper MapperFunc, reducer VoidReducerFunc, opts ...Option) error { func MapReduceVoid[T, U any](generate GenerateFunc[T], mapper MapperFunc[T, U],
_, err := MapReduce(generate, mapper, func(input <-chan interface{}, writer Writer, cancel func(error)) { reducer VoidReducerFunc[U], opts ...Option) error {
_, err := MapReduce(generate, mapper, func(input <-chan U, writer Writer[any], cancel func(error)) {
reducer(input, cancel) reducer(input, cancel)
}, opts...) }, opts...)
if errors.Is(err, ErrReduceNoOutput) { if errors.Is(err, ErrReduceNoOutput) {
@@ -266,8 +266,8 @@ func buildOptions(opts ...Option) *mapReduceOptions {
return options return options
} }
func buildSource(generate GenerateFunc, panicChan *onceChan) chan interface{} { func buildSource[T any](generate GenerateFunc[T], panicChan *onceChan) chan T {
source := make(chan interface{}) source := make(chan T)
go func() { go func() {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@@ -283,13 +283,13 @@ func buildSource(generate GenerateFunc, panicChan *onceChan) chan interface{} {
} }
// drain drains the channel. // drain drains the channel.
func drain(channel <-chan interface{}) { func drain[T any](channel <-chan T) {
// drain the channel // drain the channel
for range channel { for range channel {
} }
} }
func executeMappers(mCtx mapperContext) { func executeMappers[T, U any](mCtx mapperContext[T, U]) {
var wg sync.WaitGroup var wg sync.WaitGroup
defer func() { defer func() {
wg.Wait() wg.Wait()
@@ -298,7 +298,7 @@ func executeMappers(mCtx mapperContext) {
}() }()
var failed int32 var failed int32
pool := make(chan lang.PlaceholderType, mCtx.workers) pool := make(chan struct{}, mCtx.workers)
writer := newGuardedWriter(mCtx.ctx, mCtx.collector, mCtx.doneChan) writer := newGuardedWriter(mCtx.ctx, mCtx.collector, mCtx.doneChan)
for atomic.LoadInt32(&failed) == 0 { for atomic.LoadInt32(&failed) == 0 {
select { select {
@@ -306,7 +306,7 @@ func executeMappers(mCtx mapperContext) {
return return
case <-mCtx.doneChan: case <-mCtx.doneChan:
return return
case pool <- lang.Placeholder: case pool <- struct{}{}:
item, ok := <-mCtx.source item, ok := <-mCtx.source
if !ok { if !ok {
<-pool <-pool
@@ -346,22 +346,21 @@ func once(fn func(error)) func(error) {
} }
} }
type guardedWriter struct { type guardedWriter[T any] struct {
ctx context.Context ctx context.Context
channel chan<- interface{} channel chan<- T
done <-chan lang.PlaceholderType done <-chan struct{}
} }
func newGuardedWriter(ctx context.Context, channel chan<- interface{}, func newGuardedWriter[T any](ctx context.Context, channel chan<- T, done <-chan struct{}) guardedWriter[T] {
done <-chan lang.PlaceholderType) guardedWriter { return guardedWriter[T]{
return guardedWriter{
ctx: ctx, ctx: ctx,
channel: channel, channel: channel,
done: done, done: done,
} }
} }
func (gw guardedWriter) Write(v interface{}) { func (gw guardedWriter[T]) Write(v T) {
select { select {
case <-gw.ctx.Done(): case <-gw.ctx.Done():
return return
@@ -373,11 +372,11 @@ func (gw guardedWriter) Write(v interface{}) {
} }
type onceChan struct { type onceChan struct {
channel chan interface{} channel chan any
wrote int32 wrote int32
} }
func (oc *onceChan) write(val interface{}) { func (oc *onceChan) write(val any) {
if atomic.CompareAndSwapInt32(&oc.wrote, 0, 1) { if atomic.CompareAndSwapInt32(&oc.wrote, 0, 1) {
oc.channel <- val oc.channel <- val
} }

View File

@@ -1,6 +1,3 @@
//go:build go1.18
// +build go1.18
package mr package mr
import ( import (
@@ -18,9 +15,9 @@ import (
func FuzzMapReduce(f *testing.F) { func FuzzMapReduce(f *testing.F) {
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
f.Add(uint(10), uint(runtime.NumCPU())) f.Add(int64(10), runtime.NumCPU())
f.Fuzz(func(t *testing.T, num, workers uint) { f.Fuzz(func(t *testing.T, n int64, workers int) {
n := int64(num)%5000 + 5000 n = n%5000 + 5000
genPanic := rand.Intn(100) == 0 genPanic := rand.Intn(100) == 0
mapperPanic := rand.Intn(100) == 0 mapperPanic := rand.Intn(100) == 0
reducerPanic := rand.Intn(100) == 0 reducerPanic := rand.Intn(100) == 0
@@ -29,34 +26,33 @@ func FuzzMapReduce(f *testing.F) {
reducerIdx := rand.Int63n(n) reducerIdx := rand.Int63n(n)
squareSum := (n - 1) * n * (2*n - 1) / 6 squareSum := (n - 1) * n * (2*n - 1) / 6
fn := func() (interface{}, error) { fn := func() (int64, error) {
defer goleak.VerifyNone(t, goleak.IgnoreCurrent()) defer goleak.VerifyNone(t, goleak.IgnoreCurrent())
return MapReduce(func(source chan<- interface{}) { return MapReduce(func(source chan<- int64) {
for i := int64(0); i < n; i++ { for i := int64(0); i < n; i++ {
source <- i source <- i
if genPanic && i == genIdx { if genPanic && i == genIdx {
panic("foo") panic("foo")
} }
} }
}, func(item interface{}, writer Writer, cancel func(error)) { }, func(v int64, writer Writer[int64], cancel func(error)) {
v := item.(int64)
if mapperPanic && v == mapperIdx { if mapperPanic && v == mapperIdx {
panic("bar") panic("bar")
} }
writer.Write(v * v) writer.Write(v * v)
}, func(pipe <-chan interface{}, writer Writer, cancel func(error)) { }, func(pipe <-chan int64, writer Writer[int64], cancel func(error)) {
var idx int64 var idx int64
var total int64 var total int64
for v := range pipe { for v := range pipe {
if reducerPanic && idx == reducerIdx { if reducerPanic && idx == reducerIdx {
panic("baz") panic("baz")
} }
total += v.(int64) total += v
idx++ idx++
} }
writer.Write(total) writer.Write(total)
}, WithWorkers(int(workers)%50+runtime.NumCPU()/2)) }, WithWorkers(workers%50+runtime.NumCPU()))
} }
if genPanic || mapperPanic || reducerPanic { if genPanic || mapperPanic || reducerPanic {
@@ -72,7 +68,7 @@ func FuzzMapReduce(f *testing.F) {
} else { } else {
val, err := fn() val, err := fn()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, squareSum, val.(int64)) assert.Equal(t, squareSum, val)
} }
}) })
} }

View File

@@ -1,5 +1,4 @@
//go:build fuzz //go:build fuzz
// +build fuzz
package mr package mr
@@ -54,28 +53,27 @@ func TestMapReduceRandom(t *testing.T) {
reducerIdx := rand.Int63n(n) reducerIdx := rand.Int63n(n)
squareSum := (n - 1) * n * (2*n - 1) / 6 squareSum := (n - 1) * n * (2*n - 1) / 6
fn := func() (interface{}, error) { fn := func() (int64, error) {
return MapReduce(func(source chan<- interface{}) { return MapReduce(func(source chan<- int64) {
for i := int64(0); i < n; i++ { for i := int64(0); i < n; i++ {
source <- i source <- i
if genPanic && i == genIdx { if genPanic && i == genIdx {
panic("foo") panic("foo")
} }
} }
}, func(item interface{}, writer Writer, cancel func(error)) { }, func(v int64, writer Writer[int64], cancel func(error)) {
v := item.(int64)
if mapperPanic && v == mapperIdx { if mapperPanic && v == mapperIdx {
panic("bar") panic("bar")
} }
writer.Write(v * v) writer.Write(v * v)
}, func(pipe <-chan interface{}, writer Writer, cancel func(error)) { }, func(pipe <-chan int64, writer Writer[int64], cancel func(error)) {
var idx int64 var idx int64
var total int64 var total int64
for v := range pipe { for v := range pipe {
if reducerPanic && idx == reducerIdx { if reducerPanic && idx == reducerIdx {
panic("baz") panic("baz")
} }
total += v.(int64) total += v
idx++ idx++
} }
writer.Write(total) writer.Write(total)
@@ -95,7 +93,7 @@ func TestMapReduceRandom(t *testing.T) {
} else { } else {
val, err := fn() val, err := fn()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, squareSum, val.(int64)) assert.Equal(t, squareSum, val)
} }
bar.Increment() bar.Increment()
}) })

View File

@@ -3,7 +3,7 @@ package mr
import ( import (
"context" "context"
"errors" "errors"
"io" "io/ioutil"
"log" "log"
"runtime" "runtime"
"sync/atomic" "sync/atomic"
@@ -17,7 +17,7 @@ import (
var errDummy = errors.New("dummy") var errDummy = errors.New("dummy")
func init() { func init() {
log.SetOutput(io.Discard) log.SetOutput(ioutil.Discard)
} }
func TestFinish(t *testing.T) { func TestFinish(t *testing.T) {
@@ -91,11 +91,11 @@ func TestForEach(t *testing.T) {
defer goleak.VerifyNone(t) defer goleak.VerifyNone(t)
var count uint32 var count uint32
ForEach(func(source chan<- interface{}) { ForEach(func(source chan<- int) {
for i := 0; i < tasks; i++ { for i := 0; i < tasks; i++ {
source <- i source <- i
} }
}, func(item interface{}) { }, func(item int) {
atomic.AddUint32(&count, 1) atomic.AddUint32(&count, 1)
}, WithWorkers(-1)) }, WithWorkers(-1))
@@ -106,12 +106,12 @@ func TestForEach(t *testing.T) {
defer goleak.VerifyNone(t) defer goleak.VerifyNone(t)
var count uint32 var count uint32
ForEach(func(source chan<- interface{}) { ForEach(func(source chan<- int) {
for i := 0; i < tasks; i++ { for i := 0; i < tasks; i++ {
source <- i source <- i
} }
}, func(item interface{}) { }, func(item int) {
if item.(int)%2 == 0 { if item%2 == 0 {
atomic.AddUint32(&count, 1) atomic.AddUint32(&count, 1)
} }
}) })
@@ -123,11 +123,11 @@ func TestForEach(t *testing.T) {
defer goleak.VerifyNone(t) defer goleak.VerifyNone(t)
assert.PanicsWithValue(t, "foo", func() { assert.PanicsWithValue(t, "foo", func() {
ForEach(func(source chan<- interface{}) { ForEach(func(source chan<- int) {
for i := 0; i < tasks; i++ { for i := 0; i < tasks; i++ {
source <- i source <- i
} }
}, func(item interface{}) { }, func(item int) {
panic("foo") panic("foo")
}) })
}) })
@@ -139,9 +139,9 @@ func TestGeneratePanic(t *testing.T) {
t.Run("all", func(t *testing.T) { t.Run("all", func(t *testing.T) {
assert.PanicsWithValue(t, "foo", func() { assert.PanicsWithValue(t, "foo", func() {
ForEach(func(source chan<- interface{}) { ForEach(func(source chan<- int) {
panic("foo") panic("foo")
}, func(item interface{}) { }, func(item int) {
}) })
}) })
}) })
@@ -154,14 +154,14 @@ func TestMapperPanic(t *testing.T) {
var run int32 var run int32
t.Run("all", func(t *testing.T) { t.Run("all", func(t *testing.T) {
assert.PanicsWithValue(t, "foo", func() { assert.PanicsWithValue(t, "foo", func() {
_, _ = MapReduce(func(source chan<- interface{}) { _, _ = MapReduce(func(source chan<- int) {
for i := 0; i < tasks; i++ { for i := 0; i < tasks; i++ {
source <- i source <- i
} }
}, func(item interface{}, writer Writer, cancel func(error)) { }, func(item int, writer Writer[int], cancel func(error)) {
atomic.AddInt32(&run, 1) atomic.AddInt32(&run, 1)
panic("foo") panic("foo")
}, func(pipe <-chan interface{}, writer Writer, cancel func(error)) { }, func(pipe <-chan int, writer Writer[int], cancel func(error)) {
}) })
}) })
assert.True(t, atomic.LoadInt32(&run) < tasks/2) assert.True(t, atomic.LoadInt32(&run) < tasks/2)
@@ -173,10 +173,10 @@ func TestMapReduce(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
mapper MapperFunc mapper MapperFunc[int, int]
reducer ReducerFunc reducer ReducerFunc[int, int]
expectErr error expectErr error
expectValue interface{} expectValue int
}{ }{
{ {
name: "simple", name: "simple",
@@ -185,8 +185,7 @@ func TestMapReduce(t *testing.T) {
}, },
{ {
name: "cancel with error", name: "cancel with error",
mapper: func(item interface{}, writer Writer, cancel func(error)) { mapper: func(v int, writer Writer[int], cancel func(error)) {
v := item.(int)
if v%3 == 0 { if v%3 == 0 {
cancel(errDummy) cancel(errDummy)
} }
@@ -196,22 +195,20 @@ func TestMapReduce(t *testing.T) {
}, },
{ {
name: "cancel with nil", name: "cancel with nil",
mapper: func(item interface{}, writer Writer, cancel func(error)) { mapper: func(v int, writer Writer[int], cancel func(error)) {
v := item.(int)
if v%3 == 0 { if v%3 == 0 {
cancel(nil) cancel(nil)
} }
writer.Write(v * v) writer.Write(v * v)
}, },
expectErr: ErrCancelWithNil, expectErr: ErrCancelWithNil,
expectValue: nil,
}, },
{ {
name: "cancel with more", name: "cancel with more",
reducer: func(pipe <-chan interface{}, writer Writer, cancel func(error)) { reducer: func(pipe <-chan int, writer Writer[int], cancel func(error)) {
var result int var result int
for item := range pipe { for item := range pipe {
result += item.(int) result += item
if result > 10 { if result > 10 {
cancel(errDummy) cancel(errDummy)
} }
@@ -226,21 +223,20 @@ func TestMapReduce(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
if test.mapper == nil { if test.mapper == nil {
test.mapper = func(item interface{}, writer Writer, cancel func(error)) { test.mapper = func(v int, writer Writer[int], cancel func(error)) {
v := item.(int)
writer.Write(v * v) writer.Write(v * v)
} }
} }
if test.reducer == nil { if test.reducer == nil {
test.reducer = func(pipe <-chan interface{}, writer Writer, cancel func(error)) { test.reducer = func(pipe <-chan int, writer Writer[int], cancel func(error)) {
var result int var result int
for item := range pipe { for item := range pipe {
result += item.(int) result += item
} }
writer.Write(result) writer.Write(result)
} }
} }
value, err := MapReduce(func(source chan<- interface{}) { value, err := MapReduce(func(source chan<- int) {
for i := 1; i < 5; i++ { for i := 1; i < 5; i++ {
source <- i source <- i
} }
@@ -256,22 +252,21 @@ func TestMapReduce(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
if test.mapper == nil { if test.mapper == nil {
test.mapper = func(item interface{}, writer Writer, cancel func(error)) { test.mapper = func(v int, writer Writer[int], cancel func(error)) {
v := item.(int)
writer.Write(v * v) writer.Write(v * v)
} }
} }
if test.reducer == nil { if test.reducer == nil {
test.reducer = func(pipe <-chan interface{}, writer Writer, cancel func(error)) { test.reducer = func(pipe <-chan int, writer Writer[int], cancel func(error)) {
var result int var result int
for item := range pipe { for item := range pipe {
result += item.(int) result += item
} }
writer.Write(result) writer.Write(result)
} }
} }
source := make(chan interface{}) source := make(chan int)
go func() { go func() {
for i := 1; i < 5; i++ { for i := 1; i < 5; i++ {
source <- i source <- i
@@ -291,13 +286,13 @@ func TestMapReduceWithReduerWriteMoreThanOnce(t *testing.T) {
defer goleak.VerifyNone(t) defer goleak.VerifyNone(t)
assert.Panics(t, func() { assert.Panics(t, func() {
MapReduce(func(source chan<- interface{}) { MapReduce(func(source chan<- int) {
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
source <- i source <- i
} }
}, func(item interface{}, writer Writer, cancel func(error)) { }, func(item int, writer Writer[int], cancel func(error)) {
writer.Write(item) writer.Write(item)
}, func(pipe <-chan interface{}, writer Writer, cancel func(error)) { }, func(pipe <-chan int, writer Writer[string], cancel func(error)) {
drain(pipe) drain(pipe)
writer.Write("one") writer.Write("one")
writer.Write("two") writer.Write("two")
@@ -311,8 +306,8 @@ func TestMapReduceVoid(t *testing.T) {
var value uint32 var value uint32
tests := []struct { tests := []struct {
name string name string
mapper MapperFunc mapper MapperFunc[int, int]
reducer VoidReducerFunc reducer VoidReducerFunc[int]
expectValue uint32 expectValue uint32
expectErr error expectErr error
}{ }{
@@ -323,8 +318,7 @@ func TestMapReduceVoid(t *testing.T) {
}, },
{ {
name: "cancel with error", name: "cancel with error",
mapper: func(item interface{}, writer Writer, cancel func(error)) { mapper: func(v int, writer Writer[int], cancel func(error)) {
v := item.(int)
if v%3 == 0 { if v%3 == 0 {
cancel(errDummy) cancel(errDummy)
} }
@@ -334,8 +328,7 @@ func TestMapReduceVoid(t *testing.T) {
}, },
{ {
name: "cancel with nil", name: "cancel with nil",
mapper: func(item interface{}, writer Writer, cancel func(error)) { mapper: func(v int, writer Writer[int], cancel func(error)) {
v := item.(int)
if v%3 == 0 { if v%3 == 0 {
cancel(nil) cancel(nil)
} }
@@ -345,9 +338,9 @@ func TestMapReduceVoid(t *testing.T) {
}, },
{ {
name: "cancel with more", name: "cancel with more",
reducer: func(pipe <-chan interface{}, cancel func(error)) { reducer: func(pipe <-chan int, cancel func(error)) {
for item := range pipe { for item := range pipe {
result := atomic.AddUint32(&value, uint32(item.(int))) result := atomic.AddUint32(&value, uint32(item))
if result > 10 { if result > 10 {
cancel(errDummy) cancel(errDummy)
} }
@@ -362,19 +355,18 @@ func TestMapReduceVoid(t *testing.T) {
atomic.StoreUint32(&value, 0) atomic.StoreUint32(&value, 0)
if test.mapper == nil { if test.mapper == nil {
test.mapper = func(item interface{}, writer Writer, cancel func(error)) { test.mapper = func(v int, writer Writer[int], cancel func(error)) {
v := item.(int)
writer.Write(v * v) writer.Write(v * v)
} }
} }
if test.reducer == nil { if test.reducer == nil {
test.reducer = func(pipe <-chan interface{}, cancel func(error)) { test.reducer = func(pipe <-chan int, cancel func(error)) {
for item := range pipe { for item := range pipe {
atomic.AddUint32(&value, uint32(item.(int))) atomic.AddUint32(&value, uint32(item))
} }
} }
} }
err := MapReduceVoid(func(source chan<- interface{}) { err := MapReduceVoid(func(source chan<- int) {
for i := 1; i < 5; i++ { for i := 1; i < 5; i++ {
source <- i source <- i
} }
@@ -392,18 +384,17 @@ func TestMapReduceVoidWithDelay(t *testing.T) {
defer goleak.VerifyNone(t) defer goleak.VerifyNone(t)
var result []int var result []int
err := MapReduceVoid(func(source chan<- interface{}) { err := MapReduceVoid(func(source chan<- int) {
source <- 0 source <- 0
source <- 1 source <- 1
}, func(item interface{}, writer Writer, cancel func(error)) { }, func(i int, writer Writer[int], cancel func(error)) {
i := item.(int)
if i == 0 { if i == 0 {
time.Sleep(time.Millisecond * 50) time.Sleep(time.Millisecond * 50)
} }
writer.Write(i) writer.Write(i)
}, func(pipe <-chan interface{}, cancel func(error)) { }, func(pipe <-chan int, cancel func(error)) {
for item := range pipe { for item := range pipe {
i := item.(int) i := item
result = append(result, i) result = append(result, i)
} }
}) })
@@ -417,13 +408,12 @@ func TestMapReducePanic(t *testing.T) {
defer goleak.VerifyNone(t) defer goleak.VerifyNone(t)
assert.Panics(t, func() { assert.Panics(t, func() {
_, _ = MapReduce(func(source chan<- interface{}) { _, _ = MapReduce(func(source chan<- int) {
source <- 0 source <- 0
source <- 1 source <- 1
}, func(item interface{}, writer Writer, cancel func(error)) { }, func(i int, writer Writer[int], cancel func(error)) {
i := item.(int)
writer.Write(i) writer.Write(i)
}, func(pipe <-chan interface{}, writer Writer, cancel func(error)) { }, func(pipe <-chan int, writer Writer[int], cancel func(error)) {
for range pipe { for range pipe {
panic("panic") panic("panic")
} }
@@ -435,17 +425,16 @@ func TestMapReducePanicOnce(t *testing.T) {
defer goleak.VerifyNone(t) defer goleak.VerifyNone(t)
assert.Panics(t, func() { assert.Panics(t, func() {
_, _ = MapReduce(func(source chan<- interface{}) { _, _ = MapReduce(func(source chan<- int) {
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
source <- i source <- i
} }
}, func(item interface{}, writer Writer, cancel func(error)) { }, func(i int, writer Writer[int], cancel func(error)) {
i := item.(int)
if i == 0 { if i == 0 {
panic("foo") panic("foo")
} }
writer.Write(i) writer.Write(i)
}, func(pipe <-chan interface{}, writer Writer, cancel func(error)) { }, func(pipe <-chan int, writer Writer[int], cancel func(error)) {
for range pipe { for range pipe {
panic("bar") panic("bar")
} }
@@ -457,12 +446,12 @@ func TestMapReducePanicBothMapperAndReducer(t *testing.T) {
defer goleak.VerifyNone(t) defer goleak.VerifyNone(t)
assert.Panics(t, func() { assert.Panics(t, func() {
_, _ = MapReduce(func(source chan<- interface{}) { _, _ = MapReduce(func(source chan<- int) {
source <- 0 source <- 0
source <- 1 source <- 1
}, func(item interface{}, writer Writer, cancel func(error)) { }, func(item int, writer Writer[int], cancel func(error)) {
panic("foo") panic("foo")
}, func(pipe <-chan interface{}, writer Writer, cancel func(error)) { }, func(pipe <-chan int, writer Writer[int], cancel func(error)) {
panic("bar") panic("bar")
}) })
}) })
@@ -472,18 +461,17 @@ func TestMapReduceVoidCancel(t *testing.T) {
defer goleak.VerifyNone(t) defer goleak.VerifyNone(t)
var result []int var result []int
err := MapReduceVoid(func(source chan<- interface{}) { err := MapReduceVoid(func(source chan<- int) {
source <- 0 source <- 0
source <- 1 source <- 1
}, func(item interface{}, writer Writer, cancel func(error)) { }, func(i int, writer Writer[int], cancel func(error)) {
i := item.(int)
if i == 1 { if i == 1 {
cancel(errors.New("anything")) cancel(errors.New("anything"))
} }
writer.Write(i) writer.Write(i)
}, func(pipe <-chan interface{}, cancel func(error)) { }, func(pipe <-chan int, cancel func(error)) {
for item := range pipe { for item := range pipe {
i := item.(int) i := item
result = append(result, i) result = append(result, i)
} }
}) })
@@ -496,21 +484,19 @@ func TestMapReduceVoidCancelWithRemains(t *testing.T) {
var done int32 var done int32
var result []int var result []int
err := MapReduceVoid(func(source chan<- interface{}) { err := MapReduceVoid(func(source chan<- int) {
for i := 0; i < defaultWorkers*2; i++ { for i := 0; i < defaultWorkers*2; i++ {
source <- i source <- i
} }
atomic.AddInt32(&done, 1) atomic.AddInt32(&done, 1)
}, func(item interface{}, writer Writer, cancel func(error)) { }, func(i int, writer Writer[int], cancel func(error)) {
i := item.(int)
if i == defaultWorkers/2 { if i == defaultWorkers/2 {
cancel(errors.New("anything")) cancel(errors.New("anything"))
} }
writer.Write(i) writer.Write(i)
}, func(pipe <-chan interface{}, cancel func(error)) { }, func(pipe <-chan int, cancel func(error)) {
for item := range pipe { for item := range pipe {
i := item.(int) result = append(result, item)
result = append(result, i)
} }
}) })
assert.NotNil(t, err) assert.NotNil(t, err)
@@ -522,18 +508,18 @@ func TestMapReduceWithoutReducerWrite(t *testing.T) {
defer goleak.VerifyNone(t) defer goleak.VerifyNone(t)
uids := []int{1, 2, 3} uids := []int{1, 2, 3}
res, err := MapReduce(func(source chan<- interface{}) { res, err := MapReduce(func(source chan<- int) {
for _, uid := range uids { for _, uid := range uids {
source <- uid source <- uid
} }
}, func(item interface{}, writer Writer, cancel func(error)) { }, func(item int, writer Writer[int], cancel func(error)) {
writer.Write(item) writer.Write(item)
}, func(pipe <-chan interface{}, writer Writer, cancel func(error)) { }, func(pipe <-chan int, writer Writer[int], cancel func(error)) {
drain(pipe) drain(pipe)
// not calling writer.Write(...), should not panic // not calling writer.Write(...), should not panic
}) })
assert.Equal(t, ErrReduceNoOutput, err) assert.Equal(t, ErrReduceNoOutput, err)
assert.Nil(t, res) assert.Equal(t, 0, res)
} }
func TestMapReduceVoidPanicInReducer(t *testing.T) { func TestMapReduceVoidPanicInReducer(t *testing.T) {
@@ -542,15 +528,14 @@ func TestMapReduceVoidPanicInReducer(t *testing.T) {
const message = "foo" const message = "foo"
assert.Panics(t, func() { assert.Panics(t, func() {
var done int32 var done int32
_ = MapReduceVoid(func(source chan<- interface{}) { _ = MapReduceVoid(func(source chan<- int) {
for i := 0; i < defaultWorkers*2; i++ { for i := 0; i < defaultWorkers*2; i++ {
source <- i source <- i
} }
atomic.AddInt32(&done, 1) atomic.AddInt32(&done, 1)
}, func(item interface{}, writer Writer, cancel func(error)) { }, func(i int, writer Writer[int], cancel func(error)) {
i := item.(int)
writer.Write(i) writer.Write(i)
}, func(pipe <-chan interface{}, cancel func(error)) { }, func(pipe <-chan int, cancel func(error)) {
panic(message) panic(message)
}, WithWorkers(1)) }, WithWorkers(1))
}) })
@@ -561,13 +546,12 @@ func TestForEachWithContext(t *testing.T) {
var done int32 var done int32
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
ForEach(func(source chan<- interface{}) { ForEach(func(source chan<- int) {
for i := 0; i < defaultWorkers*2; i++ { for i := 0; i < defaultWorkers*2; i++ {
source <- i source <- i
} }
atomic.AddInt32(&done, 1) atomic.AddInt32(&done, 1)
}, func(item interface{}) { }, func(i int) {
i := item.(int)
if i == defaultWorkers/2 { if i == defaultWorkers/2 {
cancel() cancel()
} }
@@ -580,20 +564,19 @@ func TestMapReduceWithContext(t *testing.T) {
var done int32 var done int32
var result []int var result []int
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
err := MapReduceVoid(func(source chan<- interface{}) { err := MapReduceVoid(func(source chan<- int) {
for i := 0; i < defaultWorkers*2; i++ { for i := 0; i < defaultWorkers*2; i++ {
source <- i source <- i
} }
atomic.AddInt32(&done, 1) atomic.AddInt32(&done, 1)
}, func(item interface{}, writer Writer, c func(error)) { }, func(i int, writer Writer[int], c func(error)) {
i := item.(int)
if i == defaultWorkers/2 { if i == defaultWorkers/2 {
cancel() cancel()
} }
writer.Write(i) writer.Write(i)
}, func(pipe <-chan interface{}, cancel func(error)) { }, func(pipe <-chan int, cancel func(error)) {
for item := range pipe { for item := range pipe {
i := item.(int) i := item
result = append(result, i) result = append(result, i)
} }
}, WithContext(ctx)) }, WithContext(ctx))
@@ -604,19 +587,19 @@ func TestMapReduceWithContext(t *testing.T) {
func BenchmarkMapReduce(b *testing.B) { func BenchmarkMapReduce(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
mapper := func(v interface{}, writer Writer, cancel func(error)) { mapper := func(v int64, writer Writer[int64], cancel func(error)) {
writer.Write(v.(int64) * v.(int64)) writer.Write(v * v)
} }
reducer := func(input <-chan interface{}, writer Writer, cancel func(error)) { reducer := func(input <-chan int64, writer Writer[int64], cancel func(error)) {
var result int64 var result int64
for v := range input { for v := range input {
result += v.(int64) result += v
} }
writer.Write(result) writer.Write(result)
} }
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
MapReduce(func(input chan<- interface{}) { MapReduce(func(input chan<- int64) {
for j := 0; j < 2; j++ { for j := 0; j < 2; j++ {
input <- int64(j) input <- int64(j)
} }

View File

@@ -58,20 +58,19 @@ import (
) )
func main() { func main() {
val, err := mr.MapReduce(func(source chan<- interface{}) { val, err := mr.MapReduce(func(source chan<- int) {
// generator // generator
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
source <- i source <- i
} }
}, func(item interface{}, writer mr.Writer, cancel func(error)) { }, func(i int, writer mr.Writer[int], cancel func(error)) {
// mapper // mapper
i := item.(int)
writer.Write(i * i) writer.Write(i * i)
}, func(pipe <-chan interface{}, writer mr.Writer, cancel func(error)) { }, func(pipe <-chan int, writer mr.Writer[int], cancel func(error)) {
// reducer // reducer
var sum int var sum int
for i := range pipe { for i := range pipe {
sum += i.(int) sum += i
} }
writer.Write(sum) writer.Write(sum)
}) })

View File

@@ -59,20 +59,19 @@ import (
) )
func main() { func main() {
val, err := mr.MapReduce(func(source chan<- interface{}) { val, err := mr.MapReduce(func(source chan<- int) {
// generator // generator
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
source <- i source <- i
} }
}, func(item interface{}, writer mr.Writer, cancel func(error)) { }, func(i int, writer mr.Writer[int], cancel func(error)) {
// mapper // mapper
i := item.(int)
writer.Write(i * i) writer.Write(i * i)
}, func(pipe <-chan interface{}, writer mr.Writer, cancel func(error)) { }, func(pipe <-chan int, writer mr.Writer[int], cancel func(error)) {
// reducer // reducer
var sum int var sum int
for i := range pipe { for i := range pipe {
sum += i.(int) sum += i
} }
writer.Write(sum) writer.Write(sum)
}) })

View File

@@ -1,5 +1,4 @@
//go:build windows //go:build windows
// +build windows
package proc package proc

View File

@@ -1,5 +1,4 @@
//go:build linux || darwin //go:build linux || darwin
// +build linux darwin
package proc package proc

Some files were not shown because too many files have changed in this diff Show More