提供基础框架
This commit is contained in:
parent
654143f5a6
commit
8f1e814ef6
|
@ -0,0 +1,41 @@
|
|||
module gitlink.org.cn/JointCloud/pcm-participant-client
|
||||
|
||||
go 1.23.0
|
||||
|
||||
replace gitlink.org.cn/JointCloud/pcm-participant-common v0.0.0 => ../common
|
||||
|
||||
require (
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
gitlink.org.cn/JointCloud/pcm-participant-common v0.0.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/antonfisher/nested-logrus-formatter v1.3.1 // indirect
|
||||
github.com/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/crypto v0.23.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
google.golang.org/protobuf v1.34.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
|
@ -0,0 +1,102 @@
|
|||
github.com/antonfisher/nested-logrus-formatter v1.3.1 h1:NFJIr+pzwv5QLHTPyKz9UMEoHck02Q9L0FP13b/xSbQ=
|
||||
github.com/antonfisher/nested-logrus-formatter v1.3.1/go.mod h1:6WTfyWFkBc9+zyBaKIqRrg/KwMqBbodBjgbHjDz7zjA=
|
||||
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
|
||||
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
|
||||
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
|
||||
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
|
||||
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
|
||||
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
|
@ -0,0 +1,22 @@
|
|||
package http
|
||||
|
||||
type Response struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data any `json:"data"`
|
||||
}
|
||||
|
||||
func OK(data any) Response {
|
||||
return Response{
|
||||
Code: 200,
|
||||
Message: "",
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func Failed(code int, msg string) Response {
|
||||
return Response{
|
||||
Code: code,
|
||||
Message: msg,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
errorcode "gitlink.org.cn/JointCloud/pcm-participant-common/consts"
|
||||
"gitlink.org.cn/JointCloud/pcm-participant-common/pkgs/logger"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type PCMService struct {
|
||||
*Server
|
||||
}
|
||||
|
||||
func (s *Server) PCMSvc() *PCMService {
|
||||
return &PCMService{
|
||||
Server: s,
|
||||
}
|
||||
}
|
||||
|
||||
type SubmitReq struct {
|
||||
}
|
||||
|
||||
func (s *PCMService) Submit(ctx *gin.Context) {
|
||||
log := logger.WithField("HTTP", "JobSet.LocalFileUploaded")
|
||||
|
||||
var req SubmitReq
|
||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||
log.Warnf("binding body: %s", err.Error())
|
||||
ctx.JSON(http.StatusBadRequest, Failed(errorcode.BadRequest, "missing argument or invalid argument"))
|
||||
return
|
||||
}
|
||||
|
||||
s.svc.PCMSvc()
|
||||
|
||||
ctx.JSON(http.StatusOK, OK(nil))
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"gitlink.org.cn/JointCloud/pcm-participant-client/internal/services"
|
||||
"gitlink.org.cn/JointCloud/pcm-participant-common/pkgs/logger"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
engine *gin.Engine
|
||||
listenAddr string
|
||||
svc *services.Service
|
||||
}
|
||||
|
||||
func NewServer(listenAddr string, svc *services.Service) (*Server, error) {
|
||||
engine := gin.New()
|
||||
|
||||
return &Server{
|
||||
engine: engine,
|
||||
listenAddr: listenAddr,
|
||||
svc: svc,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) Serve() error {
|
||||
s.initRouters()
|
||||
|
||||
logger.Infof("start serving http at: %s", s.listenAddr)
|
||||
err := s.engine.Run(s.listenAddr)
|
||||
|
||||
if err != nil {
|
||||
logger.Infof("http stopped with error: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Infof("http stopped")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) initRouters() {
|
||||
s.engine.POST("/pcm/submit", s.PCMSvc().Submit)
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"gitlink.org.cn/JointCloud/pcm-participant-client/internal/http"
|
||||
"gitlink.org.cn/JointCloud/pcm-participant-client/internal/services"
|
||||
"gitlink.org.cn/JointCloud/pcm-participant-common/pkgs/logger"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
svc := services.NewService()
|
||||
|
||||
httpSvr, err := http.NewServer("7895", svc)
|
||||
if err != nil {
|
||||
logger.Errorf(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = httpSvr.Serve()
|
||||
if err != nil {
|
||||
logger.Errorf(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package services
|
||||
|
||||
type PCMService struct {
|
||||
*Service
|
||||
}
|
||||
|
||||
func (svc *Service) PCMSvc() *PCMService {
|
||||
return &PCMService{Service: svc}
|
||||
}
|
||||
|
||||
func (svc *PCMService) Submit() {
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package services
|
||||
|
||||
type Service struct {
|
||||
}
|
||||
|
||||
func NewService() *Service {
|
||||
return &Service{}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package errorcode
|
||||
|
||||
const (
|
||||
OK = 200
|
||||
Unauthorized = 401
|
||||
Forbidden = 403
|
||||
NotFound = 404
|
||||
InternalError = 500
|
||||
BadRequest = 400
|
||||
UnknownError = 999
|
||||
Unknown = 999
|
||||
)
|
|
@ -1 +0,0 @@
|
|||
package common
|
|
@ -0,0 +1,30 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"gitlink.org.cn/JointCloud/pcm-participant-common/pkgs/types"
|
||||
"gitlink.org.cn/JointCloud/pcm-participant-common/utils/serder"
|
||||
)
|
||||
|
||||
type SubmitJobInfo interface {
|
||||
GetLocalJobID() string
|
||||
}
|
||||
|
||||
var JobInfoTypeUnion = types.NewTypeUnion[SubmitJobInfo](
|
||||
(*NormalJobInfo)(nil),
|
||||
)
|
||||
|
||||
var _ = serder.UseTypeUnionInternallyTagged(&JobInfoTypeUnion, "type")
|
||||
|
||||
type JobInfoBase struct {
|
||||
LocalJobID string `json:"localJobID"`
|
||||
}
|
||||
|
||||
func (i *JobInfoBase) GetLocalJobID() string {
|
||||
return i.LocalJobID
|
||||
}
|
||||
|
||||
type NormalJobInfo struct {
|
||||
serder.Metadata `union:"Normal"`
|
||||
JobInfoBase
|
||||
Type string `json:"type"`
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package define
|
||||
|
||||
import definemodel "gitlink.org.cn/JointCloud/pcm-participant-common/define/model"
|
||||
|
||||
type PCMProvider interface {
|
||||
Submit(info definemodel.SubmitJobInfo) (string, string, error)
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
module gitlink.org.cn/JointCloud/pcm-participant-common
|
||||
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/antonfisher/nested-logrus-formatter v1.3.1
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/modern-go/reflect2 v1.0.2
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/smartystreets/goconvey v1.8.1
|
||||
github.com/zyedidia/generic v1.2.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/gopherjs/gopherjs v1.17.2 // indirect
|
||||
github.com/jtolds/gls v4.20.0+incompatible // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||
github.com/smarty/assertions v1.15.0 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
)
|
|
@ -0,0 +1,38 @@
|
|||
github.com/antonfisher/nested-logrus-formatter v1.3.1 h1:NFJIr+pzwv5QLHTPyKz9UMEoHck02Q9L0FP13b/xSbQ=
|
||||
github.com/antonfisher/nested-logrus-formatter v1.3.1/go.mod h1:6WTfyWFkBc9+zyBaKIqRrg/KwMqBbodBjgbHjDz7zjA=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
|
||||
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
|
||||
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
|
||||
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
|
||||
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/zyedidia/generic v1.2.1 h1:Zv5KS/N2m0XZZiuLS82qheRG4X1o5gsWreGb0hR7XDc=
|
||||
github.com/zyedidia/generic v1.2.1/go.mod h1:ly2RBz4mnz1yeuVbQA/VFwGjK3mnHGRj1JuoG336Bis=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -0,0 +1,8 @@
|
|||
package logger
|
||||
|
||||
type Config struct {
|
||||
Output string `json:"output"` // 输出日志的方式。file:输出到文件,stdout:输出到标准输出
|
||||
OutputFileName string `json:"outputFileName"` // 输出日志的文件名,只在Output字段为file时有意义
|
||||
OutputDirectory string `json:"outputDirectory"` // 输出日志的目录,只在Output字段为file时有意义
|
||||
Level string `json:"level"`
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
nested "github.com/antonfisher/nested-logrus-formatter"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gitlink.org.cn/JointCloud/pcm-participant-common/utils/reflect2"
|
||||
)
|
||||
|
||||
// Std 是一个输出日志到标准输出的Logger,适用于没有设计好日志输出方案时的临时使用。
|
||||
var Std Logger
|
||||
|
||||
// init 初始化包,设置日志格式为不带颜色的Nested格式,日志级别为Debug,输出到标准输出。
|
||||
func init() {
|
||||
logger := logrus.New()
|
||||
logger.SetFormatter(&nested.Formatter{
|
||||
TimestampFormat: "2006-01-02 15:04:05",
|
||||
NoColors: true,
|
||||
NoFieldsColors: true,
|
||||
})
|
||||
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.SetOutput(os.Stdout)
|
||||
Std = &logrusLogger{entry: logger.WithField("TODO", "")}
|
||||
}
|
||||
|
||||
// Init 初始化全局默认的日志器,根据配置设置日志级别和输出位置。
|
||||
//
|
||||
// 参数:
|
||||
//
|
||||
// cfg *Config: 日志配置项,包括日志级别和输出位置等。
|
||||
//
|
||||
// 返回值:
|
||||
//
|
||||
// error: 初始化过程中的任何错误。
|
||||
func Init(cfg *Config) error {
|
||||
logrus.SetFormatter(&nested.Formatter{
|
||||
TimestampFormat: "2006-01-02 15:04:05",
|
||||
NoColors: true,
|
||||
NoFieldsColors: true,
|
||||
})
|
||||
|
||||
// 设置日志级别
|
||||
level, ok := loggerLevels[strings.ToUpper(cfg.Level)]
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid log level: %s", cfg.Level)
|
||||
}
|
||||
|
||||
logrus.SetLevel(level)
|
||||
|
||||
// 设置日志输出位置
|
||||
output := strings.ToUpper(cfg.Output)
|
||||
|
||||
if output == OUTPUT_FILE {
|
||||
logFilePath := filepath.Join(cfg.OutputDirectory, cfg.OutputFileName+".log")
|
||||
|
||||
// 创建日志文件所在的目录
|
||||
if err := os.MkdirAll(cfg.OutputDirectory, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 打开或创建日志文件
|
||||
file, err := os.OpenFile(logFilePath, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.SetOutput(file)
|
||||
|
||||
} else if output == OUTPUT_STDOUT {
|
||||
logrus.SetOutput(os.Stdout)
|
||||
} else {
|
||||
logrus.SetOutput(os.Stdout)
|
||||
logrus.Warnf("unsupported output: %s, will output to stdout", output)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 下面是日志记录的方法,它们分别对应不同的日志级别和格式。
|
||||
// 这些方法最终都会调用logrus对应的方法来记录日志。
|
||||
|
||||
func Debug(args ...interface{}) {
|
||||
logrus.Debug(args...)
|
||||
}
|
||||
|
||||
func Debugf(format string, args ...interface{}) {
|
||||
logrus.Debugf(format, args...)
|
||||
}
|
||||
|
||||
func Info(args ...interface{}) {
|
||||
logrus.Info(args...)
|
||||
}
|
||||
|
||||
func Infof(format string, args ...interface{}) {
|
||||
logrus.Infof(format, args...)
|
||||
}
|
||||
|
||||
func Warn(args ...interface{}) {
|
||||
logrus.Warn(args...)
|
||||
}
|
||||
|
||||
func Warnf(format string, args ...interface{}) {
|
||||
logrus.Warnf(format, args...)
|
||||
}
|
||||
|
||||
func Error(args ...interface{}) {
|
||||
logrus.Error(args...)
|
||||
}
|
||||
|
||||
func Errorf(format string, args ...interface{}) {
|
||||
logrus.Errorf(format, args...)
|
||||
}
|
||||
|
||||
func Fatal(args ...interface{}) {
|
||||
logrus.Fatal(args...)
|
||||
}
|
||||
|
||||
func Fatalf(format string, args ...interface{}) {
|
||||
logrus.Fatalf(format, args...)
|
||||
}
|
||||
|
||||
func Panic(args ...interface{}) {
|
||||
logrus.Panic(args...)
|
||||
}
|
||||
|
||||
func Panicf(format string, args ...interface{}) {
|
||||
logrus.Panicf(format, args...)
|
||||
}
|
||||
|
||||
// WithField 创建并返回一个新的Logger,该Logger在记录日志时会包含额外的字段。
|
||||
//
|
||||
// 参数:
|
||||
//
|
||||
// key string: 字段键。
|
||||
// val any: 字段值。
|
||||
//
|
||||
// 返回值:
|
||||
//
|
||||
// Logger: 包含指定字段的Logger。
|
||||
func WithField(key string, val any) Logger {
|
||||
return &logrusLogger{
|
||||
entry: logrus.WithField(key, val),
|
||||
}
|
||||
}
|
||||
|
||||
// WithType 创建并返回一个新的Logger,该Logger在记录日志时会包含类型的字段。
|
||||
//
|
||||
// 参数:
|
||||
//
|
||||
// key string: 字段键。
|
||||
//
|
||||
// 返回值:
|
||||
//
|
||||
// Logger: 包含指定类型字段的Logger。
|
||||
func WithType[T any](key string) Logger {
|
||||
return &logrusLogger{
|
||||
entry: logrus.WithField(key, reflect2.TypeOf[T]().Name()),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package logger
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
TRACE_LEVEL = "TRACE"
|
||||
DEBUG_LEVEL = "DEBUG"
|
||||
INFO_LEVEL = "INFO"
|
||||
WARN_LEVEL = "WARN"
|
||||
ERROR_LEVEL = "ERROR"
|
||||
FATAL_LEVEL = "FATAL"
|
||||
PANIC_LEVEL = "PANIC"
|
||||
|
||||
OUTPUT_FILE = "FILE"
|
||||
OUTPUT_STDOUT = "STDOUT"
|
||||
)
|
||||
|
||||
var loggerLevels = map[string]logrus.Level{
|
||||
TRACE_LEVEL: logrus.TraceLevel,
|
||||
DEBUG_LEVEL: logrus.DebugLevel,
|
||||
INFO_LEVEL: logrus.InfoLevel,
|
||||
WARN_LEVEL: logrus.WarnLevel,
|
||||
ERROR_LEVEL: logrus.ErrorLevel,
|
||||
FATAL_LEVEL: logrus.FatalLevel,
|
||||
PANIC_LEVEL: logrus.PanicLevel,
|
||||
}
|
||||
|
||||
type Logger interface {
|
||||
Debug(args ...interface{})
|
||||
Debugf(format string, args ...interface{})
|
||||
|
||||
Info(args ...interface{})
|
||||
Infof(format string, args ...interface{})
|
||||
|
||||
Warn(args ...interface{})
|
||||
Warnf(format string, args ...interface{})
|
||||
|
||||
Error(args ...interface{})
|
||||
Errorf(format string, args ...interface{})
|
||||
|
||||
Fatal(args ...interface{})
|
||||
Fatalf(format string, args ...interface{})
|
||||
|
||||
Panic(args ...interface{})
|
||||
Panicf(format string, args ...interface{})
|
||||
|
||||
WithField(key string, val any) Logger
|
||||
|
||||
WithType(key string, typ reflect.Type) Logger
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func Test_FormatStruct(t *testing.T) {
|
||||
type Struct2 struct {
|
||||
Int int
|
||||
}
|
||||
|
||||
type Struct struct {
|
||||
Arr []int
|
||||
NilArr []int
|
||||
FixedArr [5]int
|
||||
St2 Struct2
|
||||
St2Ptr *Struct2
|
||||
NilSt2Ptr *Struct2
|
||||
Struct2
|
||||
}
|
||||
|
||||
st := Struct{
|
||||
Arr: []int{1, 2, 3, 4},
|
||||
NilArr: nil,
|
||||
FixedArr: [5]int{1, 2, 3, 4, 5},
|
||||
St2: Struct2{
|
||||
Int: 123,
|
||||
},
|
||||
St2Ptr: &Struct2{
|
||||
Int: 456,
|
||||
},
|
||||
NilSt2Ptr: nil,
|
||||
Struct2: Struct2{
|
||||
Int: 789,
|
||||
},
|
||||
}
|
||||
|
||||
fmtedStr := "len(Arr): 4, NilArr: <nil>, len(FixedArr): 5, St2: <Struct2>, St2Ptr: &<Struct2>, NilSt2Ptr: <nil>, Int: 789"
|
||||
|
||||
Convey("基本格式", t, func() {
|
||||
So(fmt.Sprintf("%v", FormatStruct(st)), ShouldEqual, fmtedStr)
|
||||
})
|
||||
|
||||
Convey("指针", t, func() {
|
||||
So(fmt.Sprintf("%v", FormatStruct(&st)), ShouldEqual, fmtedStr)
|
||||
})
|
||||
|
||||
Convey("interface", t, func() {
|
||||
var ift any = st
|
||||
So(fmt.Sprintf("%v", FormatStruct(ift)), ShouldEqual, fmtedStr)
|
||||
})
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package logger
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type logrusLogger struct {
|
||||
entry *logrus.Entry
|
||||
}
|
||||
|
||||
func (l *logrusLogger) Debug(args ...interface{}) {
|
||||
l.entry.Debug(args...)
|
||||
}
|
||||
func (l *logrusLogger) Debugf(format string, args ...interface{}) {
|
||||
l.entry.Debugf(format, args...)
|
||||
}
|
||||
|
||||
func (l *logrusLogger) Info(args ...interface{}) {
|
||||
l.entry.Info(args...)
|
||||
}
|
||||
func (l *logrusLogger) Infof(format string, args ...interface{}) {
|
||||
l.entry.Infof(format, args...)
|
||||
}
|
||||
|
||||
func (l *logrusLogger) Warn(args ...interface{}) {
|
||||
l.entry.Warn(args...)
|
||||
}
|
||||
func (l *logrusLogger) Warnf(format string, args ...interface{}) {
|
||||
l.entry.Warnf(format, args...)
|
||||
}
|
||||
|
||||
func (l *logrusLogger) Error(args ...interface{}) {
|
||||
l.entry.Error(args...)
|
||||
}
|
||||
func (l *logrusLogger) Errorf(format string, args ...interface{}) {
|
||||
l.entry.Errorf(format, args...)
|
||||
}
|
||||
|
||||
func (l *logrusLogger) Fatal(args ...interface{}) {
|
||||
l.entry.Fatal(args...)
|
||||
}
|
||||
func (l *logrusLogger) Fatalf(format string, args ...interface{}) {
|
||||
l.entry.Fatalf(format, args...)
|
||||
}
|
||||
|
||||
func (l *logrusLogger) Panic(args ...interface{}) {
|
||||
l.entry.Panic(args...)
|
||||
}
|
||||
func (l *logrusLogger) Panicf(format string, args ...interface{}) {
|
||||
l.entry.Panicf(format, args...)
|
||||
}
|
||||
|
||||
func (l *logrusLogger) WithField(key string, val any) Logger {
|
||||
return &logrusLogger{
|
||||
entry: l.entry.WithField(key, val),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *logrusLogger) WithType(key string, typ reflect.Type) Logger {
|
||||
return &logrusLogger{
|
||||
entry: l.entry.WithField(key, typ.Name()),
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type structFormatter struct {
|
||||
val any
|
||||
}
|
||||
|
||||
func (f *structFormatter) String() string {
|
||||
realVal := reflect.ValueOf(f.val)
|
||||
for {
|
||||
kind := realVal.Type().Kind()
|
||||
|
||||
if kind == reflect.Struct {
|
||||
sb := strings.Builder{}
|
||||
f.structString(realVal, &sb)
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
if kind == reflect.Pointer {
|
||||
realVal = realVal.Elem()
|
||||
continue
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v", f.val)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *structFormatter) structString(val reflect.Value, strBuilder *strings.Builder) {
|
||||
typ := val.Type()
|
||||
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
fieldInfo := typ.Field(i)
|
||||
fieldValue := val.Field(i)
|
||||
fieldType := fieldInfo.Type
|
||||
fieldKind := fieldType.Kind()
|
||||
|
||||
if i > 0 {
|
||||
strBuilder.WriteString(", ")
|
||||
}
|
||||
|
||||
switch fieldKind {
|
||||
case reflect.Slice:
|
||||
if fieldValue.IsNil() {
|
||||
strBuilder.WriteString(fieldInfo.Name)
|
||||
strBuilder.WriteString(": <nil>")
|
||||
|
||||
} else {
|
||||
strBuilder.WriteString("len(")
|
||||
strBuilder.WriteString(fieldInfo.Name)
|
||||
strBuilder.WriteString("): ")
|
||||
strBuilder.WriteString(fmt.Sprintf("%d", fieldValue.Len()))
|
||||
}
|
||||
|
||||
case reflect.Array:
|
||||
strBuilder.WriteString("len(")
|
||||
strBuilder.WriteString(fieldInfo.Name)
|
||||
strBuilder.WriteString("): ")
|
||||
strBuilder.WriteString(fmt.Sprintf("%d", fieldValue.Len()))
|
||||
|
||||
case reflect.Struct:
|
||||
if fieldInfo.Anonymous {
|
||||
f.structString(fieldValue, strBuilder)
|
||||
} else {
|
||||
strBuilder.WriteString(fieldInfo.Name)
|
||||
strBuilder.WriteString(": <")
|
||||
strBuilder.WriteString(fieldType.Name())
|
||||
strBuilder.WriteString(">")
|
||||
}
|
||||
|
||||
case reflect.Pointer:
|
||||
strBuilder.WriteString(fieldInfo.Name)
|
||||
if fieldValue.IsNil() {
|
||||
strBuilder.WriteString(": <nil>")
|
||||
} else {
|
||||
strBuilder.WriteString(": &<")
|
||||
strBuilder.WriteString(fieldType.Elem().Name())
|
||||
strBuilder.WriteString(">")
|
||||
}
|
||||
|
||||
default:
|
||||
strBuilder.WriteString(fieldInfo.Name)
|
||||
strBuilder.WriteString(": ")
|
||||
strBuilder.WriteString(fmt.Sprintf("%v", fieldValue))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// FormatStruct 输出结构体的内容。
|
||||
// 1. 数组类型只会输出长度
|
||||
// 2. 内部的结构体的内容不会再输出,包括embeded字段
|
||||
func FormatStruct(val any) any {
|
||||
return &structFormatter{
|
||||
val: val,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package types
|
||||
|
||||
func Ref[T any](val T) *T {
|
||||
return &val
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"gitlink.org.cn/JointCloud/pcm-participant-common/utils/reflect2"
|
||||
)
|
||||
|
||||
type AnyTypeUnion struct {
|
||||
// 这个集合的类型
|
||||
UnionType reflect2.Type
|
||||
// 集合中包含的类型,即遇到UnionType类型的值时,它内部的实际类型的范围
|
||||
ElementTypes []reflect2.Type
|
||||
}
|
||||
|
||||
func (u *AnyTypeUnion) Include(typ reflect2.Type) bool {
|
||||
for _, t := range u.ElementTypes {
|
||||
if t == typ {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (u *AnyTypeUnion) Add(typ reflect2.Type) error {
|
||||
if !typ.AssignableTo(u.UnionType) {
|
||||
return fmt.Errorf("type is not assignable to union type")
|
||||
}
|
||||
|
||||
u.ElementTypes = append(u.ElementTypes, typ)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 描述一个类型集合
|
||||
type TypeUnion[T any] struct {
|
||||
AnyTypeUnion
|
||||
}
|
||||
|
||||
func (u *TypeUnion[T]) AddT(nilValue T) {
|
||||
u.ElementTypes = append(u.ElementTypes, reflect.TypeOf(nilValue))
|
||||
}
|
||||
|
||||
func (u *TypeUnion[T]) ToAny() *AnyTypeUnion {
|
||||
return &u.AnyTypeUnion
|
||||
}
|
||||
|
||||
// 创建一个TypeUnion。泛型参数为Union的类型,形参为Union中包含的类型的一个实例,无实际用途,仅用于获取类型。
|
||||
func NewTypeUnion[TU any](eleValues ...TU) TypeUnion[TU] {
|
||||
var eleTypes []reflect.Type
|
||||
for _, v := range eleValues {
|
||||
eleTypes = append(eleTypes, reflect.TypeOf(v))
|
||||
}
|
||||
|
||||
return TypeUnion[TU]{
|
||||
AnyTypeUnion{
|
||||
UnionType: reflect2.TypeOf[TU](),
|
||||
ElementTypes: eleTypes,
|
||||
},
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
package register
|
|
@ -0,0 +1,24 @@
|
|||
package reflect2
|
||||
|
||||
import "reflect"
|
||||
|
||||
type Type = reflect.Type
|
||||
|
||||
// TypeOfValue 获得实际值的类型
|
||||
func TypeOfValue(val any) reflect.Type {
|
||||
return reflect.TypeOf(val)
|
||||
}
|
||||
|
||||
// TypeOf 获得泛型的类型
|
||||
func TypeOf[T any]() reflect.Type {
|
||||
return reflect.TypeOf([0]T{}).Elem()
|
||||
}
|
||||
|
||||
// ElemTypeOf 获得泛型的类型。适用于数组、指针类型
|
||||
func ElemTypeOf[T any]() reflect.Type {
|
||||
return reflect.TypeOf([0]T{}).Elem().Elem()
|
||||
}
|
||||
|
||||
func TypeNameOf[T any]() string {
|
||||
return TypeOf[T]().Name()
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
package serder
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
mp "github.com/mitchellh/mapstructure"
|
||||
"gitlink.org.cn/JointCloud/pcm-participant-common/utils/reflect2"
|
||||
)
|
||||
|
||||
type Converter func(from reflect.Value, to reflect.Value) (interface{}, error)
|
||||
|
||||
type AnyToAnyOption struct {
|
||||
NoFromAny bool // 不判断目的字段是否实现了FromAny接口
|
||||
NoToAny bool // 不判断源字段是否实现了ToAny接口
|
||||
Converters []Converter // 字段类型转换函数
|
||||
// 当目的类型为map[string]any,是否要递归的将源类型的每一个字段都变成map[string]any。
|
||||
// 注:字段的类型(而不是实际值的类型)必须为结构体或者结构体指针。
|
||||
RecursiveStructToMap bool
|
||||
}
|
||||
|
||||
type FromAny interface {
|
||||
FromAny(val any) (ok bool, err error)
|
||||
}
|
||||
|
||||
type ToAny interface {
|
||||
ToAny(typ reflect.Type) (val any, ok bool, err error)
|
||||
}
|
||||
|
||||
// AnyToAny 相同结构的任意类型对象之间的转换
|
||||
func AnyToAny(src any, dst any, opts ...AnyToAnyOption) error {
|
||||
var opt AnyToAnyOption
|
||||
if len(opts) > 0 {
|
||||
opt = opts[0]
|
||||
}
|
||||
|
||||
var hooks []mp.DecodeHookFunc
|
||||
if !opt.NoToAny {
|
||||
hooks = append(hooks, toAny)
|
||||
}
|
||||
|
||||
if !opt.NoFromAny {
|
||||
hooks = append(hooks, fromAny)
|
||||
}
|
||||
|
||||
for _, c := range opt.Converters {
|
||||
hooks = append(hooks, c)
|
||||
}
|
||||
|
||||
if opt.RecursiveStructToMap {
|
||||
hooks = append(hooks, mp.RecursiveStructToMapHookFunc())
|
||||
}
|
||||
|
||||
config := &mp.DecoderConfig{
|
||||
TagName: "json",
|
||||
Squash: true,
|
||||
WeaklyTypedInput: true,
|
||||
Result: dst,
|
||||
DecodeHook: mp.ComposeDecodeHookFunc(hooks...),
|
||||
}
|
||||
|
||||
decoder, err := mp.NewDecoder(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return decoder.Decode(src)
|
||||
}
|
||||
|
||||
// fromAny 如果目的字段实现的FromAny接口,那么通过此接口实现字段类型转换
|
||||
func fromAny(srcType reflect.Type, targetType reflect.Type, data interface{}) (interface{}, error) {
|
||||
if reflect2.TypeOfValue(data) == targetType {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
if targetType.Implements(reflect2.TypeOf[FromAny]()) {
|
||||
// 非pointer receiver的FromAny没有意义,因为修改不了receiver的内容,所以这里只支持指针类型
|
||||
if targetType.Kind() == reflect.Pointer {
|
||||
val := reflect.New(targetType.Elem())
|
||||
anyIf := val.Interface().(FromAny)
|
||||
ok, err := anyIf.FromAny(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
return val.Interface(), nil
|
||||
}
|
||||
|
||||
} else if reflect.PointerTo(targetType).Implements(reflect2.TypeOf[FromAny]()) {
|
||||
val := reflect.New(targetType)
|
||||
anyIf := val.Interface().(FromAny)
|
||||
ok, err := anyIf.FromAny(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
return val.Interface(), nil
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// 如果源字段实现了ToAny接口,那么通过此接口实现字段类型转换
|
||||
func toAny(srcType reflect.Type, targetType reflect.Type, data interface{}) (interface{}, error) {
|
||||
dataType := reflect2.TypeOfValue(data)
|
||||
if dataType == targetType {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
if dataType.Implements(reflect2.TypeOf[ToAny]()) {
|
||||
anyIf := data.(ToAny)
|
||||
dstVal, ok, err := anyIf.ToAny(targetType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
return dstVal, nil
|
||||
} else if reflect.PointerTo(dataType).Implements(reflect2.TypeOf[ToAny]()) {
|
||||
dataVal := reflect.ValueOf(data)
|
||||
|
||||
dataPtrVal := reflect.New(dataType)
|
||||
dataPtrVal.Elem().Set(dataVal)
|
||||
|
||||
anyIf := dataPtrVal.Interface().(ToAny)
|
||||
dstVal, ok, err := anyIf.ToAny(targetType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
return dstVal, nil
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package json
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"gitlink.org.cn/JointCloud/pcm-participant-common/pkgs/types"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
unionHandler *UnionHandler
|
||||
exts []jsoniter.Extension
|
||||
}
|
||||
|
||||
func New() *Config {
|
||||
return &Config{
|
||||
unionHandler: &UnionHandler{
|
||||
internallyTagged: make(map[reflect.Type]*anyTypeUnionInternallyTagged),
|
||||
externallyTagged: make(map[reflect.Type]*anyTypeUnionExternallyTagged),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) UseUnionInternallyTagged(u *types.AnyTypeUnion, tagField string) *Config {
|
||||
iu := &anyTypeUnionInternallyTagged{
|
||||
Union: u,
|
||||
TagField: tagField,
|
||||
TagToType: make(map[string]reflect.Type),
|
||||
}
|
||||
|
||||
for _, eleType := range u.ElementTypes {
|
||||
iu.Add(eleType)
|
||||
}
|
||||
|
||||
c.unionHandler.internallyTagged[u.UnionType] = iu
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Config) UseUnionExternallyTagged(u *types.AnyTypeUnion) *Config {
|
||||
eu := &anyTypeUnionExternallyTagged{
|
||||
Union: u,
|
||||
TypeNameToType: make(map[string]reflect.Type),
|
||||
}
|
||||
|
||||
for _, eleType := range u.ElementTypes {
|
||||
eu.Add(eleType)
|
||||
}
|
||||
|
||||
c.unionHandler.externallyTagged[u.UnionType] = eu
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Config) UseExtension(ext jsoniter.Extension) *Config {
|
||||
c.exts = append(c.exts, ext)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Config) Build() Serder {
|
||||
cfg := jsoniter.Config{}
|
||||
api := cfg.Froze()
|
||||
|
||||
api.RegisterExtension(c.unionHandler)
|
||||
|
||||
for _, ext := range c.exts {
|
||||
api.RegisterExtension(ext)
|
||||
}
|
||||
|
||||
return Serder{
|
||||
cfg: *c,
|
||||
api: api,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
)
|
||||
|
||||
type Serder struct {
|
||||
cfg Config
|
||||
api jsoniter.API
|
||||
}
|
||||
|
||||
func (s *Serder) Encode(obj any) ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
enc := s.api.NewEncoder(buf)
|
||||
err := enc.Encode(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (s *Serder) Decode(data []byte, obj any) error {
|
||||
dec := s.api.NewDecoder(bytes.NewReader(data))
|
||||
return dec.Decode(&obj)
|
||||
}
|
|
@ -0,0 +1,318 @@
|
|||
package json
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/modern-go/reflect2"
|
||||
"gitlink.org.cn/JointCloud/pcm-participant-common/pkgs/types"
|
||||
stypes "gitlink.org.cn/JointCloud/pcm-participant-common/utils/serder/types"
|
||||
|
||||
ref2 "gitlink.org.cn/JointCloud/pcm-participant-common/utils/reflect2"
|
||||
)
|
||||
|
||||
type anyTypeUnionExternallyTagged struct {
|
||||
Union *types.AnyTypeUnion
|
||||
TypeNameToType map[string]reflect.Type
|
||||
}
|
||||
|
||||
func (u *anyTypeUnionExternallyTagged) Add(typ reflect.Type) error {
|
||||
err := u.Union.Add(typ)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
u.TypeNameToType[makeDerefFullTypeName(typ)] = typ
|
||||
return nil
|
||||
}
|
||||
|
||||
type TypeUnionExternallyTagged[T any] struct {
|
||||
anyTypeUnionExternallyTagged
|
||||
TUnion *types.TypeUnion[T]
|
||||
}
|
||||
|
||||
func (u *TypeUnionExternallyTagged[T]) AddT(nilValue T) error {
|
||||
u.Add(reflect.TypeOf(nilValue))
|
||||
return nil
|
||||
}
|
||||
|
||||
type anyTypeUnionInternallyTagged struct {
|
||||
Union *types.AnyTypeUnion
|
||||
TagField string
|
||||
TagToType map[string]reflect.Type
|
||||
}
|
||||
|
||||
func (u *anyTypeUnionInternallyTagged) Add(typ reflect.Type) error {
|
||||
err := u.Union.Add(typ)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 解引用直到得到结构体类型
|
||||
structType := typ
|
||||
for structType.Kind() == reflect.Pointer {
|
||||
structType = structType.Elem()
|
||||
}
|
||||
|
||||
// 要求内嵌Metadata结构体,那么结构体中的字段名就会是Metadata,
|
||||
field, ok := structType.FieldByName(ref2.TypeNameOf[stypes.Metadata]())
|
||||
if !ok {
|
||||
u.TagToType[makeDerefFullTypeName(structType)] = typ
|
||||
return nil
|
||||
}
|
||||
|
||||
// 为防同名,检查类型是不是也是Metadata
|
||||
if field.Type != ref2.TypeOf[stypes.Metadata]() {
|
||||
u.TagToType[makeDerefFullTypeName(structType)] = typ
|
||||
return nil
|
||||
}
|
||||
|
||||
tag := field.Tag.Get("union")
|
||||
if tag == "" {
|
||||
u.TagToType[makeDerefFullTypeName(structType)] = typ
|
||||
return nil
|
||||
}
|
||||
|
||||
u.TagToType[tag] = typ
|
||||
return nil
|
||||
}
|
||||
|
||||
type TypeUnionInternallyTagged[T any] struct {
|
||||
anyTypeUnionInternallyTagged
|
||||
TUnion *types.TypeUnion[T]
|
||||
}
|
||||
|
||||
func (u *TypeUnionInternallyTagged[T]) AddT(nilValue T) error {
|
||||
u.Add(reflect.TypeOf(nilValue))
|
||||
return nil
|
||||
}
|
||||
|
||||
type UnionHandler struct {
|
||||
internallyTagged map[reflect.Type]*anyTypeUnionInternallyTagged
|
||||
externallyTagged map[reflect.Type]*anyTypeUnionExternallyTagged
|
||||
}
|
||||
|
||||
func (h *UnionHandler) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) {
|
||||
|
||||
}
|
||||
|
||||
func (h *UnionHandler) CreateMapKeyDecoder(typ reflect2.Type) jsoniter.ValDecoder {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *UnionHandler) CreateMapKeyEncoder(typ reflect2.Type) jsoniter.ValEncoder {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *UnionHandler) CreateDecoder(typ reflect2.Type) jsoniter.ValDecoder {
|
||||
typ1 := typ.Type1()
|
||||
if it, ok := h.internallyTagged[typ1]; ok {
|
||||
return &InternallyTaggedDecoder{
|
||||
union: it,
|
||||
}
|
||||
}
|
||||
|
||||
if et, ok := h.externallyTagged[typ1]; ok {
|
||||
return &ExternallyTaggedDecoder{
|
||||
union: et,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *UnionHandler) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder {
|
||||
typ1 := typ.Type1()
|
||||
if it, ok := h.internallyTagged[typ1]; ok {
|
||||
return &InternallyTaggedEncoder{
|
||||
union: it,
|
||||
}
|
||||
}
|
||||
|
||||
if et, ok := h.externallyTagged[typ1]; ok {
|
||||
return &ExternallyTaggedEncoder{
|
||||
union: et,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *UnionHandler) DecorateDecoder(typ reflect2.Type, decoder jsoniter.ValDecoder) jsoniter.ValDecoder {
|
||||
return decoder
|
||||
}
|
||||
|
||||
func (h *UnionHandler) DecorateEncoder(typ reflect2.Type, encoder jsoniter.ValEncoder) jsoniter.ValEncoder {
|
||||
return encoder
|
||||
}
|
||||
|
||||
// 以下Encoder/Decoder都是在传入类型/目标类型是TypeUnion的基类(UnionType)时使用
|
||||
type InternallyTaggedEncoder struct {
|
||||
union *anyTypeUnionInternallyTagged
|
||||
}
|
||||
|
||||
func (e *InternallyTaggedEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *InternallyTaggedEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
var val any
|
||||
|
||||
if e.union.Union.UnionType.NumMethod() == 0 {
|
||||
// 无方法的interface底层都是eface结构体,所以可以直接转*any
|
||||
val = *(*any)(ptr)
|
||||
} else {
|
||||
// 有方法的interface底层都是iface结构体,可以将其转成eface,转换后不损失类型信息
|
||||
val = reflect2.IFaceToEFace(ptr)
|
||||
}
|
||||
|
||||
// 可以考虑检查一下Type字段有没有赋值,没有赋值则将其赋值为union Tag指定的值
|
||||
stream.WriteVal(val)
|
||||
}
|
||||
|
||||
type InternallyTaggedDecoder struct {
|
||||
union *anyTypeUnionInternallyTagged
|
||||
}
|
||||
|
||||
func (e *InternallyTaggedDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
nextTokenKind := iter.WhatIsNext()
|
||||
if nextTokenKind == jsoniter.NilValue {
|
||||
iter.Skip()
|
||||
return
|
||||
}
|
||||
|
||||
raw := iter.ReadAny()
|
||||
if raw.LastError() != nil {
|
||||
iter.ReportError("decode TaggedUnionType", "getting object raw:"+raw.LastError().Error())
|
||||
return
|
||||
}
|
||||
|
||||
tagField := raw.Get(e.union.TagField)
|
||||
if tagField.LastError() != nil {
|
||||
iter.ReportError("decode TaggedUnionType", "getting type tag field:"+tagField.LastError().Error())
|
||||
return
|
||||
}
|
||||
|
||||
typeTag := tagField.ToString()
|
||||
if typeTag == "" {
|
||||
iter.ReportError("decode TaggedUnionType", "type tag is empty")
|
||||
return
|
||||
}
|
||||
|
||||
typ, ok := e.union.TagToType[typeTag]
|
||||
if !ok {
|
||||
iter.ReportError("decode TaggedUnionType", fmt.Sprintf("unknow type tag %s in union %s", typeTag, e.union.Union.UnionType.Name()))
|
||||
return
|
||||
}
|
||||
|
||||
// 如果目标类型已经是个指针类型*T,那么在New的时候就需要使用T,
|
||||
// 否则New出来的是会是**T,这将导致后续的反序列化出问题
|
||||
if typ.Kind() == reflect.Pointer {
|
||||
val := reflect.New(typ.Elem())
|
||||
raw.ToVal(val.Interface())
|
||||
|
||||
retVal := reflect.NewAt(e.union.Union.UnionType, ptr)
|
||||
retVal.Elem().Set(val)
|
||||
|
||||
} else {
|
||||
val := reflect.New(typ)
|
||||
raw.ToVal(val.Interface())
|
||||
|
||||
retVal := reflect.NewAt(e.union.Union.UnionType, ptr)
|
||||
retVal.Elem().Set(val.Elem())
|
||||
}
|
||||
}
|
||||
|
||||
type ExternallyTaggedEncoder struct {
|
||||
union *anyTypeUnionExternallyTagged
|
||||
}
|
||||
|
||||
func (e *ExternallyTaggedEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *ExternallyTaggedEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
var val any
|
||||
|
||||
if e.union.Union.UnionType.NumMethod() == 0 {
|
||||
// 无方法的interface底层都是eface结构体,所以可以直接转*any
|
||||
val = *(*any)(ptr)
|
||||
} else {
|
||||
// 有方法的interface底层都是iface结构体,可以将其转成eface,转换后不损失类型信息
|
||||
val = reflect2.IFaceToEFace(ptr)
|
||||
}
|
||||
|
||||
if val == nil {
|
||||
stream.WriteNil()
|
||||
return
|
||||
}
|
||||
|
||||
stream.WriteObjectStart()
|
||||
valType := ref2.TypeOfValue(val)
|
||||
if !e.union.Union.Include(valType) {
|
||||
stream.Error = fmt.Errorf("type %v is not in union %v", valType, e.union.Union.UnionType)
|
||||
return
|
||||
}
|
||||
stream.WriteObjectField(makeDerefFullTypeName(valType))
|
||||
stream.WriteVal(val)
|
||||
stream.WriteObjectEnd()
|
||||
}
|
||||
|
||||
type ExternallyTaggedDecoder struct {
|
||||
union *anyTypeUnionExternallyTagged
|
||||
}
|
||||
|
||||
func (e *ExternallyTaggedDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
nextTkType := iter.WhatIsNext()
|
||||
|
||||
if nextTkType == jsoniter.NilValue {
|
||||
iter.Skip()
|
||||
return
|
||||
}
|
||||
|
||||
if nextTkType != jsoniter.ObjectValue {
|
||||
iter.ReportError("decode UnionType", fmt.Sprintf("unknow next token type %v", nextTkType))
|
||||
return
|
||||
}
|
||||
|
||||
typeStr := iter.ReadObject()
|
||||
if typeStr == "" {
|
||||
iter.ReportError("decode UnionType", "type string is empty")
|
||||
}
|
||||
|
||||
typ, ok := e.union.TypeNameToType[typeStr]
|
||||
if !ok {
|
||||
iter.ReportError("decode UnionType", fmt.Sprintf("unknow type string %s in union %v", typeStr, e.union.Union.UnionType))
|
||||
return
|
||||
}
|
||||
|
||||
// 如果目标类型已经是个指针类型*T,那么在New的时候就需要使用T,
|
||||
// 否则New出来的是会是**T,这将导致后续的反序列化出问题
|
||||
if typ.Kind() == reflect.Pointer {
|
||||
val := reflect.New(typ.Elem())
|
||||
iter.ReadVal(val.Interface())
|
||||
|
||||
retVal := reflect.NewAt(e.union.Union.UnionType, ptr)
|
||||
retVal.Elem().Set(val)
|
||||
|
||||
} else {
|
||||
val := reflect.New(typ)
|
||||
iter.ReadVal(val.Interface())
|
||||
|
||||
retVal := reflect.NewAt(e.union.Union.UnionType, ptr)
|
||||
retVal.Elem().Set(val.Elem())
|
||||
}
|
||||
|
||||
if iter.ReadObject() != "" {
|
||||
iter.ReportError("decode UnionType", "there should be only one fields in the json object")
|
||||
}
|
||||
}
|
||||
|
||||
func makeDerefFullTypeName(typ reflect.Type) string {
|
||||
realType := typ
|
||||
for realType.Kind() == reflect.Pointer {
|
||||
realType = realType.Elem()
|
||||
}
|
||||
return fmt.Sprintf("%s.%s", realType.PkgPath(), realType.Name())
|
||||
}
|
|
@ -0,0 +1,278 @@
|
|||
package serder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
var unionHandler = UnionHandler{
|
||||
internallyTagged: make(map[reflect.Type]*anyTypeUnionInternallyTagged),
|
||||
externallyTagged: make(map[reflect.Type]*anyTypeUnionExternallyTagged),
|
||||
}
|
||||
|
||||
var defaultAPI = func() jsoniter.API {
|
||||
api := jsoniter.Config{
|
||||
EscapeHTML: true,
|
||||
}.Froze()
|
||||
|
||||
api.RegisterExtension(&unionHandler)
|
||||
return api
|
||||
}()
|
||||
|
||||
// 将对象转为JSON字符串。支持TypeUnion。
|
||||
func ObjectToJSONEx[T any](obj T) ([]byte, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
enc := defaultAPI.NewEncoder(buf)
|
||||
// 这里使用&obj而直接不使用obj的原因是,Encode的形参类型为any,
|
||||
// 如果T是一个interface类型,将obj传递进去后,内部拿到的类型将会是obj的实际类型,
|
||||
// 使用&obj,那么内部拿到的将会是*T类型,通过一层一层解引用查找Encoder时,能找到T对应的TypeUnion
|
||||
err := enc.Encode(&obj)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// 将JSON字符串转为对象。支持TypeUnion。
|
||||
func JSONToObjectEx[T any](data []byte) (T, error) {
|
||||
var ret T
|
||||
dec := defaultAPI.NewDecoder(bytes.NewReader(data))
|
||||
err := dec.Decode(&ret)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// 将JSON字符串转为对象。支持TypeUnion。
|
||||
func JSONToObjectExRaw(data []byte, ret any) error {
|
||||
dec := defaultAPI.NewDecoder(bytes.NewReader(data))
|
||||
err := dec.Decode(&ret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 将JSON字符串转为对象。支持TypeUnion。
|
||||
//
|
||||
// 如果发现反序列化后的结果不对,但没有返回错误,可以考虑是JSON字符串有问题,
|
||||
// 尤其是在反序列化嵌套的TypeUnion时,如果内部的TypeUnion反序列化失败,错误是不会传递出来的(库的缺陷)
|
||||
func JSONToObjectStreamEx[T any](stream io.Reader) (T, error) {
|
||||
var ret T
|
||||
dec := defaultAPI.NewDecoder(stream)
|
||||
err := dec.Decode(&ret)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func JSONToObjectStreamExRaw(stream io.Reader, ret any) error {
|
||||
dec := defaultAPI.NewDecoder(stream)
|
||||
err := dec.Decode(ret)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 将对象转为JSON字符串。如果需要支持解析TypeUnion类型,则使用"Ex"结尾的同名函数。
|
||||
//
|
||||
// 注:[]byte会被base64编码,如果要JSON内容要给外部解析,那么应该避免使用[]byte。
|
||||
func ObjectToJSON(obj any) ([]byte, error) {
|
||||
return json.Marshal(obj)
|
||||
}
|
||||
|
||||
// 将对象转为JSON字符串。如果需要支持解析TypeUnion类型,则使用"Ex"结尾的同名函数。
|
||||
func ObjectToJSONStream(obj any) io.ReadCloser {
|
||||
pr, pw := io.Pipe()
|
||||
enc := json.NewEncoder(pw)
|
||||
|
||||
go func() {
|
||||
err := enc.Encode(obj)
|
||||
if err != nil && err != io.EOF {
|
||||
pw.CloseWithError(err)
|
||||
} else {
|
||||
pw.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
return pr
|
||||
}
|
||||
|
||||
// 将JSON字符串转为对象。如果需要支持解析TypeUnion类型,则使用"Ex"结尾的同名函数。
|
||||
func JSONToObject(data []byte, obj any) error {
|
||||
return json.Unmarshal(data, obj)
|
||||
}
|
||||
|
||||
// 将JSON字符串转为对象。如果需要支持解析TypeUnion类型,则使用"Ex"结尾的同名函数。
|
||||
func JSONToObjectStream(str io.Reader, obj any) error {
|
||||
dec := json.NewDecoder(str)
|
||||
err := dec.Decode(obj)
|
||||
if err != io.EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type TypeResolver interface {
|
||||
TypeToString(typ reflect.Type) (string, error)
|
||||
StringToType(typeStr string) (reflect.Type, error)
|
||||
}
|
||||
|
||||
type MapToObjectOption struct {
|
||||
NoRegisteredUnionTypes bool // 是否不使用全局注册的UnionType
|
||||
}
|
||||
|
||||
// TODO 使用这个函数来处理TypeUnion的地方都可以直接使用Ex系列的函数
|
||||
func MapToObject(m map[string]any, obj any, opt ...MapToObjectOption) error {
|
||||
var op MapToObjectOption
|
||||
if len(opt) > 0 {
|
||||
op = opt[0]
|
||||
}
|
||||
|
||||
unionTypeMapping := make(map[reflect.Type]*anyTypeUnionInternallyTagged)
|
||||
|
||||
if !op.NoRegisteredUnionTypes {
|
||||
for _, u := range unionHandler.internallyTagged {
|
||||
unionTypeMapping[u.Union.UnionType] = u
|
||||
}
|
||||
}
|
||||
|
||||
convs := []Converter{
|
||||
func(from reflect.Value, to reflect.Value) (interface{}, error) {
|
||||
toType := to.Type()
|
||||
info, ok := unionTypeMapping[toType]
|
||||
if !ok {
|
||||
return from.Interface(), nil
|
||||
}
|
||||
|
||||
mp := from.Interface().(map[string]any)
|
||||
tag, ok := mp[info.TagField]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("converting to %v: no tag field %s in map", toType, info.TagField)
|
||||
}
|
||||
|
||||
tagStr, ok := tag.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("converting to %v: tag field %s value is %v, which is not a string", toType, info.TagField, tag)
|
||||
}
|
||||
|
||||
eleType, ok := info.TagToType[tagStr]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("converting to %v: unknow type tag %s", toType, tagStr)
|
||||
}
|
||||
|
||||
to.Set(reflect.New(eleType).Elem())
|
||||
|
||||
return from.Interface(), nil
|
||||
},
|
||||
}
|
||||
|
||||
return AnyToAny(m, obj, AnyToAnyOption{
|
||||
Converters: convs,
|
||||
})
|
||||
}
|
||||
|
||||
func ObjectToMap(obj any) (map[string]any, error) {
|
||||
mp := make(map[string]any)
|
||||
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||||
TagName: "json",
|
||||
Result: &mp,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mp, dec.Decode(obj)
|
||||
}
|
||||
|
||||
// 1. 尝试解开所有引用
|
||||
//
|
||||
// 2. nil值将会是空字符串
|
||||
func ObjectToMapString(obj any) (map[string]string, error) {
|
||||
if obj == nil {
|
||||
return make(map[string]string), nil
|
||||
}
|
||||
|
||||
v := reflect.ValueOf(obj)
|
||||
for v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
if !v.IsValid() {
|
||||
return make(map[string]string), nil
|
||||
}
|
||||
if v.Kind() != reflect.Struct {
|
||||
return nil, fmt.Errorf("type %v is not a struct", v.Type())
|
||||
}
|
||||
|
||||
mp := make(map[string]string)
|
||||
objectToMapString(v, mp)
|
||||
return mp, nil
|
||||
}
|
||||
|
||||
func objectToMapString(val reflect.Value, mp map[string]string) {
|
||||
typ := val.Type()
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
vf := val.Field(i)
|
||||
tf := typ.Field(i)
|
||||
if tf.Anonymous {
|
||||
objectToMapString(vf, mp)
|
||||
continue
|
||||
}
|
||||
|
||||
fieldName := tf.Name
|
||||
|
||||
omitEmpty := false
|
||||
jsonTag := tf.Tag.Get("json")
|
||||
if jsonTag != "" {
|
||||
tagParts := strings.Split(jsonTag, ",")
|
||||
fieldName = strings.TrimSpace(tagParts[0])
|
||||
if len(tagParts) > 1 {
|
||||
for _, tagPart := range tagParts[1:] {
|
||||
tagPart = strings.TrimSpace(tagPart)
|
||||
if tagPart == "omitempty" {
|
||||
omitEmpty = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for vf.Kind() == reflect.Ptr {
|
||||
vf = vf.Elem()
|
||||
}
|
||||
|
||||
if !vf.IsValid() {
|
||||
if omitEmpty {
|
||||
continue
|
||||
}
|
||||
|
||||
mp[fieldName] = ""
|
||||
continue
|
||||
}
|
||||
|
||||
if vf.IsZero() && omitEmpty {
|
||||
continue
|
||||
}
|
||||
|
||||
if vf.Kind() == reflect.Array {
|
||||
|
||||
}
|
||||
|
||||
mp[fieldName] = fmt.Sprintf("%v", vf)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,801 @@
|
|||
package serder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"gitlink.org.cn/JointCloud/pcm-participant-common/pkgs/types"
|
||||
"gitlink.org.cn/JointCloud/pcm-participant-common/utils/reflect2"
|
||||
)
|
||||
|
||||
type FromAnyString struct {
|
||||
Str string
|
||||
}
|
||||
|
||||
func (a *FromAnyString) FromAny(val any) (bool, error) {
|
||||
if str, ok := val.(string); ok {
|
||||
a.Str = "@" + str
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
type ToAnyString struct {
|
||||
Str string
|
||||
}
|
||||
|
||||
func (a *ToAnyString) ToAny(typ reflect.Type) (val any, ok bool, err error) {
|
||||
if typ == reflect2.TypeOf[map[string]any]() {
|
||||
return map[string]any{
|
||||
"str": "@" + a.Str,
|
||||
}, true, nil
|
||||
}
|
||||
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
type FromAnySt struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
func (a *FromAnySt) FromAny(val any) (bool, error) {
|
||||
if st, ok := val.(ToAnySt); ok {
|
||||
a.Value = "From:" + st.Value
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
type ToAnySt struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
func (a *ToAnySt) ToAny(typ reflect.Type) (val any, ok bool, err error) {
|
||||
if typ == reflect2.TypeOf[FromAnySt]() {
|
||||
return FromAnySt{
|
||||
Value: "To:" + a.Value,
|
||||
}, true, nil
|
||||
}
|
||||
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
type DirToAnySt struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
func (a DirToAnySt) ToAny(typ reflect.Type) (val any, ok bool, err error) {
|
||||
if typ == reflect2.TypeOf[FromAnySt]() {
|
||||
return FromAnySt{
|
||||
Value: "DirTo:" + a.Value,
|
||||
}, true, nil
|
||||
}
|
||||
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
func Test_AnyToAny(t *testing.T) {
|
||||
Convey("包含用字符串保存的int数据", t, func() {
|
||||
type Struct struct {
|
||||
A string `json:"a"`
|
||||
B int `json:"b"`
|
||||
C int64 `json:"c,string"`
|
||||
}
|
||||
|
||||
mp := map[string]any{
|
||||
"a": "a",
|
||||
"b": 1,
|
||||
"c": "1234",
|
||||
}
|
||||
|
||||
var st Struct
|
||||
|
||||
err := AnyToAny(mp, &st)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(st.A, ShouldEqual, "a")
|
||||
So(st.B, ShouldEqual, 1)
|
||||
So(st.C, ShouldEqual, 1234)
|
||||
})
|
||||
|
||||
Convey("只有FromAny", t, func() {
|
||||
type Struct struct {
|
||||
Special FromAnyString `json:"str"`
|
||||
}
|
||||
|
||||
mp := map[string]any{
|
||||
"str": "test",
|
||||
}
|
||||
|
||||
var ret Struct
|
||||
err := AnyToAny(mp, &ret)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(ret.Special.Str, ShouldEqual, "@test")
|
||||
})
|
||||
|
||||
Convey("字段类型直接实现了FromAny", t, func() {
|
||||
type Struct struct {
|
||||
Special *FromAnyString `json:"str"`
|
||||
}
|
||||
|
||||
mp := map[string]any{
|
||||
"str": "test",
|
||||
}
|
||||
|
||||
var ret Struct
|
||||
err := AnyToAny(mp, &ret)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(ret.Special.Str, ShouldEqual, "@test")
|
||||
})
|
||||
|
||||
Convey("只有ToAny", t, func() {
|
||||
st := struct {
|
||||
Special ToAnyString `json:"str"`
|
||||
}{
|
||||
Special: ToAnyString{
|
||||
Str: "test",
|
||||
},
|
||||
}
|
||||
|
||||
ret := map[string]any{}
|
||||
|
||||
err := AnyToAny(st, &ret)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(ret["str"].(map[string]any)["str"], ShouldEqual, "@test")
|
||||
})
|
||||
|
||||
Convey("优先使用ToAny", t, func() {
|
||||
st1 := ToAnySt{
|
||||
Value: "test",
|
||||
}
|
||||
|
||||
st2 := FromAnySt{}
|
||||
|
||||
err := AnyToAny(st1, &st2)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(st2.Value, ShouldEqual, "To:test")
|
||||
})
|
||||
|
||||
Convey("使用Convertor", t, func() {
|
||||
type Struct1 struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
type Struct2 struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
st1 := Struct1{
|
||||
Value: "test",
|
||||
}
|
||||
|
||||
st2 := Struct2{}
|
||||
|
||||
err := AnyToAny(st1, &st2, AnyToAnyOption{
|
||||
Converters: []Converter{func(from reflect.Value, to reflect.Value) (interface{}, error) {
|
||||
if from.Type() == reflect2.TypeOf[Struct1]() && to.Type() == reflect2.TypeOf[Struct2]() {
|
||||
s1 := from.Interface().(Struct1)
|
||||
return Struct2{
|
||||
Value: "@" + s1.Value,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("should not arrive here!")
|
||||
}},
|
||||
})
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(st2.Value, ShouldEqual, "@test")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_MapToObject(t *testing.T) {
|
||||
type Base struct {
|
||||
Int int
|
||||
Bool bool
|
||||
String string
|
||||
Float float32
|
||||
}
|
||||
type ArraryStruct struct {
|
||||
IntArr []int
|
||||
StArr []Base
|
||||
ArrArr [][]int
|
||||
Nil []Base
|
||||
}
|
||||
type MapStruct struct {
|
||||
StrMap map[string]string
|
||||
StMap map[string]Base
|
||||
MapMap map[string]map[string]string
|
||||
Nil map[string]Base
|
||||
}
|
||||
|
||||
type Top struct {
|
||||
ArrSt ArraryStruct
|
||||
MapSt *MapStruct
|
||||
BaseIf any
|
||||
NilPtr *Base
|
||||
}
|
||||
Convey("结构体递归转换成map[string]any", t, func() {
|
||||
val := Top{
|
||||
ArrSt: ArraryStruct{
|
||||
IntArr: []int{1, 2, 3},
|
||||
StArr: []Base{
|
||||
{
|
||||
Int: 1,
|
||||
Bool: true,
|
||||
String: "test",
|
||||
Float: 1,
|
||||
},
|
||||
{
|
||||
Int: 2,
|
||||
Bool: false,
|
||||
String: "test2",
|
||||
Float: 2,
|
||||
},
|
||||
},
|
||||
ArrArr: [][]int{
|
||||
{1, 2, 3},
|
||||
{},
|
||||
nil,
|
||||
},
|
||||
Nil: nil,
|
||||
},
|
||||
MapSt: &MapStruct{
|
||||
StrMap: map[string]string{
|
||||
"a": "1",
|
||||
"b": "2",
|
||||
},
|
||||
StMap: map[string]Base{
|
||||
"a": {
|
||||
Int: 1,
|
||||
Bool: true,
|
||||
String: "test",
|
||||
Float: 1,
|
||||
},
|
||||
"b": {
|
||||
Int: 2,
|
||||
Bool: false,
|
||||
String: "test2",
|
||||
Float: 2,
|
||||
},
|
||||
},
|
||||
MapMap: map[string]map[string]string{
|
||||
"a": {
|
||||
"a": "1",
|
||||
"b": "2",
|
||||
},
|
||||
"b": nil,
|
||||
},
|
||||
Nil: nil,
|
||||
},
|
||||
BaseIf: Base{
|
||||
Int: 1,
|
||||
Bool: true,
|
||||
String: "test",
|
||||
Float: 1,
|
||||
},
|
||||
NilPtr: nil,
|
||||
}
|
||||
|
||||
retMp, err := ObjectToMap(val)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
exceptMap := map[string]any{
|
||||
"ArrSt": map[string]any{
|
||||
"IntArr": []any{1, 2, 3},
|
||||
"StArr": []any{
|
||||
map[string]any{
|
||||
"Int": 1,
|
||||
"Bool": true,
|
||||
"String": "test",
|
||||
"Float": 1,
|
||||
},
|
||||
map[string]any{
|
||||
"Int": 2,
|
||||
"Bool": false,
|
||||
"String": "test2",
|
||||
"Float": 2,
|
||||
},
|
||||
},
|
||||
"ArrArr": []any{
|
||||
[]any{1, 2, 3},
|
||||
[]any{},
|
||||
[]int(nil),
|
||||
},
|
||||
"Nil": []Base(nil),
|
||||
},
|
||||
"MapSt": map[string]any{
|
||||
"StrMap": map[string]any{
|
||||
"a": "1",
|
||||
"b": "2",
|
||||
},
|
||||
"StMap": map[string]any{
|
||||
"a": map[string]any{
|
||||
"Int": 1,
|
||||
"Bool": true,
|
||||
"String": "test",
|
||||
"Float": 1,
|
||||
},
|
||||
"b": map[string]any{
|
||||
"Int": 2,
|
||||
"Bool": false,
|
||||
"String": "test2",
|
||||
"Float": 2,
|
||||
},
|
||||
},
|
||||
"MapMap": map[string]any{
|
||||
"a": map[string]any{
|
||||
"a": "1",
|
||||
"b": "2",
|
||||
},
|
||||
"b": map[string]string(nil),
|
||||
},
|
||||
"Nil": map[string]Base(nil),
|
||||
},
|
||||
"BaseIf": map[string]any{
|
||||
"Int": 1,
|
||||
"Bool": true,
|
||||
"String": "test",
|
||||
"Float": 1,
|
||||
},
|
||||
"NilPtr": (*Base)(nil),
|
||||
}
|
||||
|
||||
mpRetJson, err := ObjectToJSON(retMp)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
exceptMapJson, err := ObjectToJSON(exceptMap)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(string(mpRetJson), ShouldEqualJSON, string(exceptMapJson))
|
||||
})
|
||||
|
||||
Convey("包含UnionType", t, func() {
|
||||
type EleType string
|
||||
type UnionType interface{}
|
||||
|
||||
type EleType1 struct {
|
||||
Metadata `union:"1"`
|
||||
Type EleType `json:"type"`
|
||||
Value1 string `json:"value1"`
|
||||
}
|
||||
|
||||
type EleType2 struct {
|
||||
Metadata `union:"2"`
|
||||
Type EleType `json:"type"`
|
||||
Value2 int `json:"value2"`
|
||||
}
|
||||
|
||||
type St struct {
|
||||
Us []UnionType `json:"us"`
|
||||
}
|
||||
|
||||
mp := map[string]any{
|
||||
"us": []map[string]any{
|
||||
{
|
||||
"type": "1",
|
||||
"value1": "1",
|
||||
},
|
||||
{
|
||||
"type": "2",
|
||||
"value2": 2,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var ret St
|
||||
union := types.NewTypeUnion[UnionType](
|
||||
(*EleType1)(nil),
|
||||
(*EleType2)(nil),
|
||||
)
|
||||
UseTypeUnionInternallyTagged(&union, "type")
|
||||
err := MapToObject(mp, &ret)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(ret.Us, ShouldResemble, []UnionType{
|
||||
&EleType1{Type: "1", Value1: "1"},
|
||||
&EleType2{Type: "2", Value2: 2},
|
||||
})
|
||||
})
|
||||
|
||||
Convey("要转换到的结构体就是一个UnionType", t, func() {
|
||||
type UnionType interface{}
|
||||
|
||||
type EleType1 struct {
|
||||
Metadata `union:"1"`
|
||||
Type string `json:"type"`
|
||||
Value1 string `json:"value1"`
|
||||
}
|
||||
|
||||
type EleType2 struct {
|
||||
Metadata `union:"2"`
|
||||
Type string `json:"type"`
|
||||
Value2 int `json:"value2"`
|
||||
}
|
||||
|
||||
mp := map[string]any{
|
||||
"type": "1",
|
||||
"value1": "1",
|
||||
}
|
||||
|
||||
var ret UnionType
|
||||
union := types.NewTypeUnion[UnionType](
|
||||
(*EleType1)(nil),
|
||||
(*EleType2)(nil),
|
||||
)
|
||||
UseTypeUnionInternallyTagged(&union, "type")
|
||||
err := MapToObject(mp, &ret)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(ret, ShouldResemble, &EleType1{Type: "1", Value1: "1"})
|
||||
})
|
||||
|
||||
Convey("NewType", t, func() {
|
||||
type Str string
|
||||
|
||||
type St struct {
|
||||
Str Str
|
||||
}
|
||||
|
||||
mp := map[string]any{
|
||||
"Str": "1",
|
||||
}
|
||||
|
||||
var ret St
|
||||
err := MapToObject(mp, &ret)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(string(ret.Str), ShouldEqual, "1")
|
||||
})
|
||||
}
|
||||
|
||||
type Base interface {
|
||||
Noop()
|
||||
}
|
||||
type St1 struct {
|
||||
Metadata `union:"St1"`
|
||||
Type string
|
||||
Val string
|
||||
}
|
||||
|
||||
func (*St1) Noop() {}
|
||||
|
||||
type St2 struct {
|
||||
Metadata `union:"St2"`
|
||||
Type string
|
||||
Val int
|
||||
}
|
||||
|
||||
func (St2) Noop() {}
|
||||
|
||||
func Test_ObjectToJSON2(t *testing.T) {
|
||||
Convey("NewType", t, func() {
|
||||
type Str string
|
||||
|
||||
type St struct {
|
||||
Str Str `json:"str"`
|
||||
}
|
||||
|
||||
st := St{
|
||||
Str: Str("1"),
|
||||
}
|
||||
|
||||
data, err := ObjectToJSON(st)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
var ret St
|
||||
err = JSONToObject(data, &ret)
|
||||
So(err, ShouldBeNil)
|
||||
So(string(ret.Str), ShouldEqual, "1")
|
||||
})
|
||||
|
||||
Convey("UnionType ExternallyTagged", t, func() {
|
||||
type Base interface{}
|
||||
type St1 struct {
|
||||
Val string
|
||||
}
|
||||
type St2 struct {
|
||||
Val int64
|
||||
}
|
||||
type Outter struct {
|
||||
B []Base
|
||||
}
|
||||
|
||||
union := types.NewTypeUnion[Base](St1{}, &St2{})
|
||||
UseTypeUnionExternallyTagged(&union)
|
||||
|
||||
val := Outter{B: []Base{St1{Val: "asd"}, &St2{Val: 123}}}
|
||||
data, err := ObjectToJSONEx(val)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
ret, err := JSONToObjectEx[Outter](data)
|
||||
So(err, ShouldBeNil)
|
||||
So(ret, ShouldResemble, val)
|
||||
})
|
||||
|
||||
Convey("UnionType InternallyTagged", t, func() {
|
||||
type Base interface{}
|
||||
type St1 struct {
|
||||
Metadata `union:"St1"`
|
||||
Type string
|
||||
Val string
|
||||
}
|
||||
type St2 struct {
|
||||
Metadata `union:"St2"`
|
||||
Type string
|
||||
Val int64
|
||||
}
|
||||
type Outter struct {
|
||||
B []Base
|
||||
}
|
||||
|
||||
union := types.NewTypeUnion[Base](St1{}, &St2{})
|
||||
UseTypeUnionInternallyTagged(&union, "Type")
|
||||
|
||||
val := Outter{B: []Base{St1{Val: "asd", Type: "St1"}, &St2{Val: 123, Type: "St2"}}}
|
||||
data, err := ObjectToJSONEx(val)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
ret, err := JSONToObjectEx[Outter](data)
|
||||
So(err, ShouldBeNil)
|
||||
So(ret, ShouldResemble, val)
|
||||
})
|
||||
|
||||
Convey("实参类型和目标类型本身就是UnionType ExternallyTagged", t, func() {
|
||||
type Base interface{}
|
||||
type St1 struct {
|
||||
Val string
|
||||
}
|
||||
union := types.NewTypeUnion[Base](St1{})
|
||||
UseTypeUnionExternallyTagged(&union)
|
||||
|
||||
var val Base = St1{Val: "asd"}
|
||||
data, err := ObjectToJSONEx(val)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
ret, err := JSONToObjectEx[Base](data)
|
||||
So(err, ShouldBeNil)
|
||||
So(ret, ShouldResemble, val)
|
||||
})
|
||||
|
||||
Convey("实参类型和目标类型本身就是UnionType InternallyTagged", t, func() {
|
||||
type Base interface{}
|
||||
type St1 struct {
|
||||
Metadata `union:"St1"`
|
||||
Type string
|
||||
Val string
|
||||
}
|
||||
union := types.NewTypeUnion[Base](St1{})
|
||||
UseTypeUnionInternallyTagged(&union, "Type")
|
||||
|
||||
var val Base = St1{Val: "asd", Type: "St1"}
|
||||
data, err := ObjectToJSONEx(val)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
ret, err := JSONToObjectEx[Base](data)
|
||||
So(err, ShouldBeNil)
|
||||
So(ret, ShouldResemble, val)
|
||||
})
|
||||
|
||||
Convey("UnionType带有函数 ExternallyTagged", t, func() {
|
||||
union := types.NewTypeUnion[Base](&St1{}, St2{})
|
||||
UseTypeUnionExternallyTagged(&union)
|
||||
|
||||
var val = []Base{
|
||||
&St1{Val: "asd", Type: "St1"},
|
||||
St2{Val: 123, Type: "St2"},
|
||||
}
|
||||
data, err := ObjectToJSONEx(val)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
ret, err := JSONToObjectEx[[]Base](data)
|
||||
So(err, ShouldBeNil)
|
||||
So(ret, ShouldResemble, val)
|
||||
})
|
||||
|
||||
Convey("UnionType带有函数 InternallyTagged", t, func() {
|
||||
union := types.NewTypeUnion[Base](&St1{}, St2{})
|
||||
UseTypeUnionInternallyTagged(&union, "Type")
|
||||
|
||||
var val = []Base{
|
||||
&St1{Val: "asd", Type: "St1"},
|
||||
St2{Val: 123, Type: "St2"},
|
||||
}
|
||||
data, err := ObjectToJSONEx(val)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
ret, err := JSONToObjectEx[[]Base](data)
|
||||
So(err, ShouldBeNil)
|
||||
So(ret, ShouldResemble, val)
|
||||
})
|
||||
|
||||
Convey("UnionType,但实际值为nil ExternallyTagged", t, func() {
|
||||
union := types.NewTypeUnion[Base](&St1{}, St2{})
|
||||
UseTypeUnionExternallyTagged(&union)
|
||||
|
||||
var val = []Base{
|
||||
nil,
|
||||
}
|
||||
data, err := ObjectToJSONEx(val)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
ret, err := JSONToObjectEx[[]Base](data)
|
||||
So(err, ShouldBeNil)
|
||||
So(ret, ShouldResemble, val)
|
||||
})
|
||||
|
||||
Convey("UnionType,但实际值为nil InternallyTagged", t, func() {
|
||||
union := types.NewTypeUnion[Base](&St1{}, St2{})
|
||||
UseTypeUnionInternallyTagged(&union, "Type")
|
||||
|
||||
var val = []Base{
|
||||
nil,
|
||||
}
|
||||
data, err := ObjectToJSONEx(val)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
ret, err := JSONToObjectEx[[]Base](data)
|
||||
So(err, ShouldBeNil)
|
||||
So(ret, ShouldResemble, val)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func Test_ObjectToJSON3(t *testing.T) {
|
||||
Convey("反序列化TypeUnion时,JSON中对应字段类型不对", t, func() {
|
||||
type Base interface{}
|
||||
|
||||
union := types.NewTypeUnion[Base](&St1{}, &St2{})
|
||||
UseTypeUnionInternallyTagged(&union, "Type")
|
||||
|
||||
v, err := JSONToObjectEx[[]Base]([]byte("[{\"Type\":\"St2\", \"Val\":[]}]"))
|
||||
t.Logf("err: %v", err)
|
||||
t.Logf("v: %+v", v[0])
|
||||
So(err, ShouldNotBeNil)
|
||||
// So(ret, ShouldResemble, val)
|
||||
})
|
||||
}
|
||||
|
||||
type BaseCallback interface{}
|
||||
type StCallback struct {
|
||||
Metadata `union:"StCallback"`
|
||||
Type string
|
||||
Value string
|
||||
}
|
||||
|
||||
func (st *StCallback) OnUnionSerializing() {
|
||||
st.Value = "called"
|
||||
st.Type = "StCallback"
|
||||
}
|
||||
|
||||
func Test_ObjectToJSONEx4(t *testing.T) {
|
||||
Convey("序列化Callback", t, func() {
|
||||
union := types.NewTypeUnion[BaseCallback](&StCallback{})
|
||||
UseTypeUnionInternallyTagged(&union, "Type")
|
||||
|
||||
val := []BaseCallback{&StCallback{}}
|
||||
data, err := ObjectToJSONEx(val)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
ret, err := JSONToObjectEx[[]BaseCallback](data)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(len(ret), ShouldEqual, 1)
|
||||
So(ret[0].(*StCallback).Value, ShouldEqual, "called")
|
||||
})
|
||||
}
|
||||
|
||||
type StStringer struct {
|
||||
}
|
||||
|
||||
func (s StStringer) String() string {
|
||||
return "StStringer"
|
||||
}
|
||||
|
||||
func Test_ObjectToMapString(t *testing.T) {
|
||||
Convey("结构体", t, func() {
|
||||
type StEmb struct {
|
||||
IntRef *int `json:"intRef,omitempty"`
|
||||
BoolRef **bool `json:"boolRef"`
|
||||
StrRef *string `json:"strRef"`
|
||||
}
|
||||
|
||||
type St struct {
|
||||
StEmb
|
||||
Int int
|
||||
Bool bool
|
||||
Str string
|
||||
StStringer *StStringer
|
||||
}
|
||||
|
||||
st := St{
|
||||
StEmb: StEmb{
|
||||
IntRef: types.Ref(123),
|
||||
BoolRef: types.Ref(types.Ref(true)),
|
||||
},
|
||||
Int: 456,
|
||||
Bool: false,
|
||||
Str: "test",
|
||||
StStringer: &StStringer{},
|
||||
}
|
||||
|
||||
mp, err := ObjectToMapString(st)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(mp["intRef"], ShouldEqual, "123")
|
||||
So(mp["boolRef"], ShouldEqual, "true")
|
||||
So(mp["strRef"], ShouldEqual, "")
|
||||
So(mp["Int"], ShouldEqual, "456")
|
||||
So(mp["Bool"], ShouldEqual, "false")
|
||||
So(mp["Str"], ShouldEqual, "test")
|
||||
So(mp["StStringer"], ShouldEqual, "StStringer")
|
||||
})
|
||||
|
||||
Convey("结构体引用", t, func() {
|
||||
type StEmb struct {
|
||||
IntRef *int `json:"intRef,omitempty"`
|
||||
BoolRef **bool `json:"boolRef"`
|
||||
StrRef *string `json:"strRef"`
|
||||
}
|
||||
|
||||
type St struct {
|
||||
StEmb
|
||||
Int int
|
||||
Bool bool
|
||||
Str string
|
||||
StStringer *StStringer
|
||||
}
|
||||
|
||||
st := St{
|
||||
StEmb: StEmb{
|
||||
IntRef: types.Ref(123),
|
||||
BoolRef: types.Ref(types.Ref(true)),
|
||||
},
|
||||
Int: 456,
|
||||
Bool: false,
|
||||
Str: "test",
|
||||
StStringer: &StStringer{},
|
||||
}
|
||||
|
||||
mp, err := ObjectToMapString(&st)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(mp["intRef"], ShouldEqual, "123")
|
||||
So(mp["boolRef"], ShouldEqual, "true")
|
||||
So(mp["strRef"], ShouldEqual, "")
|
||||
So(mp["Int"], ShouldEqual, "456")
|
||||
So(mp["Bool"], ShouldEqual, "false")
|
||||
So(mp["Str"], ShouldEqual, "test")
|
||||
So(mp["StStringer"], ShouldEqual, "StStringer")
|
||||
})
|
||||
|
||||
Convey("nil", t, func() {
|
||||
mp, err := ObjectToMapString(nil)
|
||||
So(err, ShouldBeNil)
|
||||
So(mp, ShouldResemble, map[string]string{})
|
||||
})
|
||||
|
||||
Convey("nil指针", t, func() {
|
||||
type St struct{}
|
||||
var st *St
|
||||
mp, err := ObjectToMapString(st)
|
||||
So(err, ShouldBeNil)
|
||||
So(mp, ShouldResemble, map[string]string{})
|
||||
})
|
||||
|
||||
Convey("非结构体", t, func() {
|
||||
mp, err := ObjectToMapString(123)
|
||||
So(err, ShouldNotBeNil)
|
||||
So(mp, ShouldBeNil)
|
||||
})
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package serder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type StringTypeResolver struct {
|
||||
strToType map[string]reflect.Type
|
||||
typeToStr map[reflect.Type]string
|
||||
}
|
||||
|
||||
func NewStringTypeResolver() *StringTypeResolver {
|
||||
return &StringTypeResolver{
|
||||
strToType: make(map[string]reflect.Type),
|
||||
typeToStr: make(map[reflect.Type]string),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *StringTypeResolver) Add(str string, typ reflect.Type) *StringTypeResolver {
|
||||
r.strToType[str] = typ
|
||||
r.typeToStr[typ] = str
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *StringTypeResolver) TypeToString(typ reflect.Type) (string, error) {
|
||||
var typeStr string
|
||||
var ok bool
|
||||
if typeStr, ok = r.typeToStr[typ]; !ok {
|
||||
return "", fmt.Errorf("type %s is not registered before", typ)
|
||||
}
|
||||
|
||||
return typeStr, nil
|
||||
}
|
||||
|
||||
func (r *StringTypeResolver) StringToType(typeStr string) (reflect.Type, error) {
|
||||
typ, ok := r.strToType[typeStr]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknow type string %s", typeStr)
|
||||
}
|
||||
|
||||
return typ, nil
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package serder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type TypeNameResolver struct {
|
||||
includePackagePath bool
|
||||
types map[string]reflect.Type
|
||||
}
|
||||
|
||||
func NewTypeNameResolver(includePackagePath bool) *TypeNameResolver {
|
||||
return &TypeNameResolver{
|
||||
includePackagePath: includePackagePath,
|
||||
types: make(map[string]reflect.Type),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *TypeNameResolver) Register(typ reflect.Type) {
|
||||
r.types[makeTypeString(typ, r.includePackagePath)] = typ
|
||||
}
|
||||
|
||||
func (r *TypeNameResolver) TypeToString(typ reflect.Type) (string, error) {
|
||||
typeStr := makeTypeString(typ, r.includePackagePath)
|
||||
if _, ok := r.types[typeStr]; !ok {
|
||||
return "", fmt.Errorf("type %s is not registered before", typeStr)
|
||||
}
|
||||
|
||||
return typeStr, nil
|
||||
}
|
||||
|
||||
func (r *TypeNameResolver) StringToType(typeStr string) (reflect.Type, error) {
|
||||
typ, ok := r.types[typeStr]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknow type name %s", typeStr)
|
||||
}
|
||||
|
||||
return typ, nil
|
||||
}
|
||||
|
||||
func makeTypeString(typ reflect.Type, includePkgPath bool) string {
|
||||
if includePkgPath {
|
||||
return fmt.Sprintf("%s.%s", typ.PkgPath(), typ.Name())
|
||||
}
|
||||
|
||||
return typ.Name()
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package serder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"gitlink.org.cn/JointCloud/pcm-participant-common/utils/serder/types"
|
||||
)
|
||||
|
||||
type Metadata = types.Metadata
|
||||
|
||||
type TimestampSecond time.Time
|
||||
|
||||
func (t *TimestampSecond) MarshalJSON() ([]byte, error) {
|
||||
raw := time.Time(*t)
|
||||
return []byte(fmt.Sprintf("%d", raw.Unix())), nil
|
||||
}
|
||||
|
||||
func (t *TimestampSecond) UnmarshalJSON(data []byte) error {
|
||||
var timestamp int64
|
||||
var err error
|
||||
if timestamp, err = strconv.ParseInt(string(data), 10, 64); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*t = TimestampSecond(time.Unix(timestamp, 0))
|
||||
return nil
|
||||
}
|
||||
|
||||
type TimestampMilliSecond time.Time
|
||||
|
||||
func (t *TimestampMilliSecond) MarshalJSON() ([]byte, error) {
|
||||
raw := time.Time(*t)
|
||||
return []byte(fmt.Sprintf("%d", raw.UnixMilli())), nil
|
||||
}
|
||||
|
||||
func (t *TimestampMilliSecond) UnmarshalJSON(data []byte) error {
|
||||
var timestamp int64
|
||||
var err error
|
||||
if timestamp, err = strconv.ParseInt(string(data), 10, 64); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*t = TimestampMilliSecond(time.UnixMilli(timestamp))
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package types
|
||||
|
||||
type Metadata struct{}
|
||||
|
||||
type OnUnionSerializing interface {
|
||||
OnUnionSerializing()
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package serder
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func Test_Timestamp(t *testing.T) {
|
||||
Convey("秒级时间戳", t, func() {
|
||||
str := "1698894747"
|
||||
|
||||
var ts TimestampSecond
|
||||
|
||||
err := json.Unmarshal([]byte(str), &ts)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
t := time.Time(ts)
|
||||
So(t.Unix(), ShouldEqual, 1698894747)
|
||||
})
|
||||
|
||||
Convey("毫秒级时间戳", t, func() {
|
||||
str := "1698895130651"
|
||||
|
||||
var ts TimestampMilliSecond
|
||||
|
||||
err := json.Unmarshal([]byte(str), &ts)
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
t := time.Time(ts)
|
||||
So(t.UnixMilli(), ShouldEqual, 1698895130651)
|
||||
})
|
||||
}
|
|
@ -0,0 +1,373 @@
|
|||
package serder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/modern-go/reflect2"
|
||||
"gitlink.org.cn/JointCloud/pcm-participant-common/pkgs/types"
|
||||
sertypes "gitlink.org.cn/JointCloud/pcm-participant-common/utils/serder/types"
|
||||
|
||||
ref2 "gitlink.org.cn/JointCloud/pcm-participant-common/utils/reflect2"
|
||||
)
|
||||
|
||||
type anyTypeUnionExternallyTagged struct {
|
||||
Union *types.AnyTypeUnion
|
||||
TypeNameToType map[string]reflect.Type
|
||||
}
|
||||
|
||||
type TypeUnionExternallyTagged[T any] struct {
|
||||
anyTypeUnionExternallyTagged
|
||||
TUnion *types.TypeUnion[T]
|
||||
}
|
||||
|
||||
// 遇到TypeUnion的基类(UnionType)的字段时,将其实际值的类型信息也编码到JSON中,反序列化时也会根据解析出类型信息,还原出真实的类型。
|
||||
// Externally Tagged的格式是:{ "类型名": {...对象内容...} }
|
||||
//
|
||||
// 可以通过内嵌Metadata结构体,并在它身上增加"union"Tag来指定类型名称,如果没有指定,则默认使用系统类型名(包括包路径)。
|
||||
func UseTypeUnionExternallyTagged[T any](union *types.TypeUnion[T]) *TypeUnionExternallyTagged[T] {
|
||||
eu := &TypeUnionExternallyTagged[T]{
|
||||
anyTypeUnionExternallyTagged: anyTypeUnionExternallyTagged{
|
||||
Union: union.ToAny(),
|
||||
TypeNameToType: make(map[string]reflect.Type),
|
||||
},
|
||||
TUnion: union,
|
||||
}
|
||||
|
||||
for _, eleType := range union.ElementTypes {
|
||||
eu.Add(eleType)
|
||||
}
|
||||
|
||||
unionHandler.externallyTagged[union.UnionType] = &eu.anyTypeUnionExternallyTagged
|
||||
|
||||
return eu
|
||||
}
|
||||
|
||||
func (u *TypeUnionExternallyTagged[T]) Add(typ reflect.Type) error {
|
||||
err := u.TUnion.Add(typ)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
u.TypeNameToType[makeDerefFullTypeName(typ)] = typ
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *TypeUnionExternallyTagged[T]) AddT(nilValue T) error {
|
||||
u.Add(reflect.TypeOf(nilValue))
|
||||
return nil
|
||||
}
|
||||
|
||||
type anyTypeUnionInternallyTagged struct {
|
||||
Union *types.AnyTypeUnion
|
||||
TagField string
|
||||
TagToType map[string]reflect.Type
|
||||
}
|
||||
|
||||
type TypeUnionInternallyTagged[T any] struct {
|
||||
anyTypeUnionInternallyTagged
|
||||
TUnion *types.TypeUnion[T]
|
||||
}
|
||||
|
||||
// 遇到TypeUnion的基类(UnionType)的字段时,将其实际值的类型信息也编码到JSON中,反序列化时也会解析出类型信息,还原出真实的类型。
|
||||
// Internally Tagged的格式是:{ "类型字段": "类型名", ...对象内容...},JSON中的类型字段名需要指定。
|
||||
// 注:对象定义需要包含类型字段,而且在序列化之前需要手动赋值,目前不支持自动设置。
|
||||
//
|
||||
// 可以通过内嵌Metadata结构体,并在它身上增加"union"Tag来指定类型名称,如果没有指定,则默认使用系统类型名(包括包路径)。
|
||||
func UseTypeUnionInternallyTagged[T any](union *types.TypeUnion[T], tagField string) *TypeUnionInternallyTagged[T] {
|
||||
iu := &TypeUnionInternallyTagged[T]{
|
||||
anyTypeUnionInternallyTagged: anyTypeUnionInternallyTagged{
|
||||
Union: union.ToAny(),
|
||||
TagField: tagField,
|
||||
TagToType: make(map[string]reflect.Type),
|
||||
},
|
||||
TUnion: union,
|
||||
}
|
||||
|
||||
for _, eleType := range union.ElementTypes {
|
||||
iu.Add(eleType)
|
||||
}
|
||||
|
||||
unionHandler.internallyTagged[union.UnionType] = &iu.anyTypeUnionInternallyTagged
|
||||
return iu
|
||||
}
|
||||
|
||||
func (u *TypeUnionInternallyTagged[T]) Add(typ reflect.Type) error {
|
||||
err := u.Union.Add(typ)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 解引用直到得到结构体类型
|
||||
structType := typ
|
||||
for structType.Kind() == reflect.Pointer {
|
||||
structType = structType.Elem()
|
||||
}
|
||||
|
||||
// 要求内嵌Metadata结构体,那么结构体中的字段名就会是Metadata,
|
||||
field, ok := structType.FieldByName(ref2.TypeNameOf[Metadata]())
|
||||
if !ok {
|
||||
u.TagToType[makeDerefFullTypeName(structType)] = typ
|
||||
return nil
|
||||
}
|
||||
|
||||
// 为防同名,检查类型是不是也是Metadata
|
||||
if field.Type != ref2.TypeOf[Metadata]() {
|
||||
u.TagToType[makeDerefFullTypeName(structType)] = typ
|
||||
return nil
|
||||
}
|
||||
|
||||
tag := field.Tag.Get("union")
|
||||
if tag == "" {
|
||||
u.TagToType[makeDerefFullTypeName(structType)] = typ
|
||||
return nil
|
||||
}
|
||||
|
||||
u.TagToType[tag] = typ
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *TypeUnionInternallyTagged[T]) AddT(nilValue T) error {
|
||||
u.Add(reflect.TypeOf(nilValue))
|
||||
return nil
|
||||
}
|
||||
|
||||
type UnionHandler struct {
|
||||
internallyTagged map[reflect.Type]*anyTypeUnionInternallyTagged
|
||||
externallyTagged map[reflect.Type]*anyTypeUnionExternallyTagged
|
||||
}
|
||||
|
||||
func (h *UnionHandler) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) {
|
||||
|
||||
}
|
||||
|
||||
func (h *UnionHandler) CreateMapKeyDecoder(typ reflect2.Type) jsoniter.ValDecoder {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *UnionHandler) CreateMapKeyEncoder(typ reflect2.Type) jsoniter.ValEncoder {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *UnionHandler) CreateDecoder(typ reflect2.Type) jsoniter.ValDecoder {
|
||||
typ1 := typ.Type1()
|
||||
if it, ok := h.internallyTagged[typ1]; ok {
|
||||
return &InternallyTaggedDecoder{
|
||||
union: it,
|
||||
}
|
||||
}
|
||||
|
||||
if et, ok := h.externallyTagged[typ1]; ok {
|
||||
return &ExternallyTaggedDecoder{
|
||||
union: et,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *UnionHandler) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder {
|
||||
typ1 := typ.Type1()
|
||||
if it, ok := h.internallyTagged[typ1]; ok {
|
||||
return &InternallyTaggedEncoder{
|
||||
union: it,
|
||||
}
|
||||
}
|
||||
|
||||
if et, ok := h.externallyTagged[typ1]; ok {
|
||||
return &ExternallyTaggedEncoder{
|
||||
union: et,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *UnionHandler) DecorateDecoder(typ reflect2.Type, decoder jsoniter.ValDecoder) jsoniter.ValDecoder {
|
||||
return decoder
|
||||
}
|
||||
|
||||
func (h *UnionHandler) DecorateEncoder(typ reflect2.Type, encoder jsoniter.ValEncoder) jsoniter.ValEncoder {
|
||||
return encoder
|
||||
}
|
||||
|
||||
// 以下Encoder/Decoder都是在传入类型/目标类型是TypeUnion的基类(UnionType)时使用
|
||||
type InternallyTaggedEncoder struct {
|
||||
union *anyTypeUnionInternallyTagged
|
||||
}
|
||||
|
||||
func (e *InternallyTaggedEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *InternallyTaggedEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
var val any
|
||||
|
||||
if e.union.Union.UnionType.NumMethod() == 0 {
|
||||
// 无方法的interface底层都是eface结构体,所以可以直接转*any
|
||||
val = *(*any)(ptr)
|
||||
} else {
|
||||
// 有方法的interface底层都是iface结构体,可以将其转成eface,转换后不损失类型信息
|
||||
val = reflect2.IFaceToEFace(ptr)
|
||||
}
|
||||
|
||||
if val != nil {
|
||||
if on, ok := val.(sertypes.OnUnionSerializing); ok {
|
||||
on.OnUnionSerializing()
|
||||
}
|
||||
}
|
||||
|
||||
// 可以考虑检查一下Type字段有没有赋值,没有赋值则将其赋值为union Tag指定的值
|
||||
stream.WriteVal(val)
|
||||
}
|
||||
|
||||
type InternallyTaggedDecoder struct {
|
||||
union *anyTypeUnionInternallyTagged
|
||||
}
|
||||
|
||||
func (e *InternallyTaggedDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
nextTokenKind := iter.WhatIsNext()
|
||||
if nextTokenKind == jsoniter.NilValue {
|
||||
iter.Skip()
|
||||
return
|
||||
}
|
||||
|
||||
raw := iter.ReadAny()
|
||||
if raw.LastError() != nil {
|
||||
iter.ReportError("decode TaggedUnionType", "getting object raw:"+raw.LastError().Error())
|
||||
return
|
||||
}
|
||||
|
||||
tagField := raw.Get(e.union.TagField)
|
||||
if tagField.LastError() != nil {
|
||||
iter.ReportError("decode TaggedUnionType", "getting type tag field:"+tagField.LastError().Error())
|
||||
return
|
||||
}
|
||||
|
||||
typeTag := tagField.ToString()
|
||||
if typeTag == "" {
|
||||
iter.ReportError("decode TaggedUnionType", "type tag is empty")
|
||||
return
|
||||
}
|
||||
|
||||
typ, ok := e.union.TagToType[typeTag]
|
||||
if !ok {
|
||||
iter.ReportError("decode TaggedUnionType", fmt.Sprintf("unknow type tag %s in union %s", typeTag, e.union.Union.UnionType.Name()))
|
||||
return
|
||||
}
|
||||
|
||||
// 如果目标类型已经是个指针类型*T,那么在New的时候就需要使用T,
|
||||
// 否则New出来的是会是**T,这将导致后续的反序列化出问题
|
||||
if typ.Kind() == reflect.Pointer {
|
||||
val := reflect.New(typ.Elem())
|
||||
raw.ToVal(val.Interface()) // TODO 使用的库丢失了ToVal期间的错误信息,考虑换个库
|
||||
|
||||
retVal := reflect.NewAt(e.union.Union.UnionType, ptr)
|
||||
retVal.Elem().Set(val)
|
||||
|
||||
} else {
|
||||
val := reflect.New(typ)
|
||||
raw.ToVal(val.Interface())
|
||||
|
||||
retVal := reflect.NewAt(e.union.Union.UnionType, ptr)
|
||||
retVal.Elem().Set(val.Elem())
|
||||
}
|
||||
}
|
||||
|
||||
type ExternallyTaggedEncoder struct {
|
||||
union *anyTypeUnionExternallyTagged
|
||||
}
|
||||
|
||||
func (e *ExternallyTaggedEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *ExternallyTaggedEncoder) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
var val any
|
||||
|
||||
if e.union.Union.UnionType.NumMethod() == 0 {
|
||||
// 无方法的interface底层都是eface结构体,所以可以直接转*any
|
||||
val = *(*any)(ptr)
|
||||
} else {
|
||||
// 有方法的interface底层都是iface结构体,可以将其转成eface,转换后不损失类型信息
|
||||
val = reflect2.IFaceToEFace(ptr)
|
||||
}
|
||||
|
||||
if val == nil {
|
||||
stream.WriteNil()
|
||||
return
|
||||
}
|
||||
|
||||
if on, ok := val.(sertypes.OnUnionSerializing); ok {
|
||||
on.OnUnionSerializing()
|
||||
}
|
||||
|
||||
stream.WriteObjectStart()
|
||||
valType := ref2.TypeOfValue(val)
|
||||
if !e.union.Union.Include(valType) {
|
||||
stream.Error = fmt.Errorf("type %v is not in union %v", valType, e.union.Union.UnionType)
|
||||
return
|
||||
}
|
||||
stream.WriteObjectField(makeDerefFullTypeName(valType))
|
||||
stream.WriteVal(val)
|
||||
stream.WriteObjectEnd()
|
||||
}
|
||||
|
||||
type ExternallyTaggedDecoder struct {
|
||||
union *anyTypeUnionExternallyTagged
|
||||
}
|
||||
|
||||
func (e *ExternallyTaggedDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
nextTkType := iter.WhatIsNext()
|
||||
|
||||
if nextTkType == jsoniter.NilValue {
|
||||
iter.Skip()
|
||||
return
|
||||
}
|
||||
|
||||
if nextTkType != jsoniter.ObjectValue {
|
||||
iter.ReportError("decode UnionType", fmt.Sprintf("unknow next token type %v", nextTkType))
|
||||
return
|
||||
}
|
||||
|
||||
typeStr := iter.ReadObject()
|
||||
if typeStr == "" {
|
||||
iter.ReportError("decode UnionType", "type string is empty")
|
||||
}
|
||||
|
||||
typ, ok := e.union.TypeNameToType[typeStr]
|
||||
if !ok {
|
||||
iter.ReportError("decode UnionType", fmt.Sprintf("unknow type string %s in union %v", typeStr, e.union.Union.UnionType))
|
||||
return
|
||||
}
|
||||
|
||||
// 如果目标类型已经是个指针类型*T,那么在New的时候就需要使用T,
|
||||
// 否则New出来的是会是**T,这将导致后续的反序列化出问题
|
||||
if typ.Kind() == reflect.Pointer {
|
||||
val := reflect.New(typ.Elem())
|
||||
iter.ReadVal(val.Interface())
|
||||
|
||||
retVal := reflect.NewAt(e.union.Union.UnionType, ptr)
|
||||
retVal.Elem().Set(val)
|
||||
|
||||
} else {
|
||||
val := reflect.New(typ)
|
||||
iter.ReadVal(val.Interface())
|
||||
|
||||
retVal := reflect.NewAt(e.union.Union.UnionType, ptr)
|
||||
retVal.Elem().Set(val.Elem())
|
||||
}
|
||||
|
||||
if iter.ReadObject() != "" {
|
||||
iter.ReportError("decode UnionType", "there should be only one fields in the json object")
|
||||
}
|
||||
}
|
||||
|
||||
func makeDerefFullTypeName(typ reflect.Type) string {
|
||||
realType := typ
|
||||
for realType.Kind() == reflect.Pointer {
|
||||
realType = realType.Elem()
|
||||
}
|
||||
return fmt.Sprintf("%s.%s", realType.PkgPath(), realType.Name())
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
package serder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"gitlink.org.cn/JointCloud/pcm-participant-common/utils/reflect2"
|
||||
)
|
||||
|
||||
func Test_WalkValue(t *testing.T) {
|
||||
type Base struct {
|
||||
Int int
|
||||
Bool bool
|
||||
String string
|
||||
Float float32
|
||||
}
|
||||
type ArraryStruct struct {
|
||||
IntArr []int
|
||||
StArr []Base
|
||||
ArrArr [][]int
|
||||
Nil []Base
|
||||
}
|
||||
type MapStruct struct {
|
||||
StrMap map[string]string
|
||||
StMap map[string]Base
|
||||
MapMap map[string]map[string]string
|
||||
Nil map[string]Base
|
||||
}
|
||||
|
||||
type Top struct {
|
||||
ArrSt ArraryStruct
|
||||
MapSt *MapStruct
|
||||
BaseIf any
|
||||
NilPtr *Base
|
||||
}
|
||||
|
||||
isBaseDataType := func(val reflect.Value) bool {
|
||||
typ := val.Type()
|
||||
return typ == reflect2.TypeOf[int]() || typ == reflect2.TypeOf[bool]() ||
|
||||
typ == reflect2.TypeOf[string]() || typ == reflect2.TypeOf[float32]() || val.IsZero()
|
||||
}
|
||||
|
||||
toString := func(val any) string {
|
||||
return fmt.Sprintf("%v", val)
|
||||
}
|
||||
|
||||
Convey("遍历", t, func() {
|
||||
val := Top{
|
||||
ArrSt: ArraryStruct{
|
||||
IntArr: []int{1, 2, 3},
|
||||
StArr: []Base{
|
||||
{
|
||||
Int: 1,
|
||||
Bool: true,
|
||||
String: "test",
|
||||
Float: 1,
|
||||
},
|
||||
{
|
||||
Int: 2,
|
||||
Bool: false,
|
||||
String: "test2",
|
||||
Float: 2,
|
||||
},
|
||||
},
|
||||
ArrArr: [][]int{
|
||||
{1, 2, 3},
|
||||
{},
|
||||
nil,
|
||||
},
|
||||
Nil: nil,
|
||||
},
|
||||
MapSt: &MapStruct{
|
||||
StrMap: map[string]string{
|
||||
"a": "1",
|
||||
"b": "2",
|
||||
},
|
||||
StMap: map[string]Base{
|
||||
"a": {
|
||||
Int: 1,
|
||||
Bool: true,
|
||||
String: "test",
|
||||
Float: 1,
|
||||
},
|
||||
"b": {
|
||||
Int: 2,
|
||||
Bool: false,
|
||||
String: "test2",
|
||||
Float: 2,
|
||||
},
|
||||
},
|
||||
MapMap: map[string]map[string]string{
|
||||
"a": {
|
||||
"a": "1",
|
||||
"b": "2",
|
||||
},
|
||||
"b": nil,
|
||||
},
|
||||
Nil: nil,
|
||||
},
|
||||
BaseIf: Base{
|
||||
Int: 1,
|
||||
Bool: true,
|
||||
String: "test",
|
||||
Float: 1,
|
||||
},
|
||||
NilPtr: nil,
|
||||
}
|
||||
|
||||
var trace []string
|
||||
WalkValue(val, func(ctx *WalkContext, event WalkEvent) WalkingOp {
|
||||
switch e := event.(type) {
|
||||
case StructBeginEvent:
|
||||
trace = append(trace, "StructBeginEvent")
|
||||
case StructArriveFieldEvent:
|
||||
trace = append(trace, "StructFieldEvent", e.Info.Name)
|
||||
if isBaseDataType(e.Value) {
|
||||
trace = append(trace, toString(e.Value.Interface()))
|
||||
}
|
||||
case StructEndEvent:
|
||||
trace = append(trace, "StructEndEvent")
|
||||
|
||||
case MapBeginEvent:
|
||||
trace = append(trace, "MapBeginEvent")
|
||||
case MapArriveEntryEvent:
|
||||
trace = append(trace, "MapEntryEvent", e.Key.String())
|
||||
if isBaseDataType(e.Value) {
|
||||
trace = append(trace, toString(e.Value.Interface()))
|
||||
}
|
||||
case MapEndEvent:
|
||||
trace = append(trace, "MapEndEvent")
|
||||
|
||||
case ArrayBeginEvent:
|
||||
trace = append(trace, "ArrayBeginEvent")
|
||||
case ArrayArriveElementEvent:
|
||||
trace = append(trace, "ArrayElementEvent", fmt.Sprintf("%d", e.Index))
|
||||
if isBaseDataType(e.Value) {
|
||||
trace = append(trace, toString(e.Value.Interface()))
|
||||
}
|
||||
case ArrayEndEvent:
|
||||
trace = append(trace, "ArrayEndEvent")
|
||||
}
|
||||
|
||||
return Next
|
||||
})
|
||||
|
||||
So(trace, ShouldResemble, []string{})
|
||||
})
|
||||
}
|
|
@ -0,0 +1,278 @@
|
|||
package serder
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/zyedidia/generic/stack"
|
||||
)
|
||||
|
||||
type WalkEvent interface{}
|
||||
|
||||
type StructBeginEvent struct {
|
||||
Value reflect.Value
|
||||
}
|
||||
|
||||
type StructArriveFieldEvent struct {
|
||||
Info reflect.StructField
|
||||
Value reflect.Value
|
||||
}
|
||||
|
||||
type StructLeaveFieldEvent struct {
|
||||
Info reflect.StructField
|
||||
Value reflect.Value
|
||||
}
|
||||
|
||||
type StructEndEvent struct {
|
||||
Value reflect.Value
|
||||
}
|
||||
|
||||
type ArrayBeginEvent struct {
|
||||
Value reflect.Value
|
||||
}
|
||||
|
||||
type ArrayArriveElementEvent struct {
|
||||
Index int
|
||||
Value reflect.Value
|
||||
}
|
||||
|
||||
type ArrayLeaveElementEvent struct {
|
||||
Index int
|
||||
Value reflect.Value
|
||||
}
|
||||
|
||||
type ArrayEndEvent struct {
|
||||
Value reflect.Value
|
||||
}
|
||||
|
||||
type MapBeginEvent struct {
|
||||
Value reflect.Value
|
||||
}
|
||||
|
||||
type MapArriveEntryEvent struct {
|
||||
Key reflect.Value
|
||||
Value reflect.Value
|
||||
}
|
||||
|
||||
type MapLeaveEntryEvent struct {
|
||||
Key reflect.Value
|
||||
Value reflect.Value
|
||||
}
|
||||
|
||||
type MapEndEvent struct {
|
||||
Value reflect.Value
|
||||
}
|
||||
|
||||
type WalkingOp int
|
||||
|
||||
const (
|
||||
Next WalkingOp = iota
|
||||
Skip
|
||||
Stop
|
||||
)
|
||||
|
||||
type Walker func(ctx *WalkContext, event WalkEvent) WalkingOp
|
||||
|
||||
type WalkContext struct {
|
||||
stack *stack.Stack[any]
|
||||
}
|
||||
|
||||
func (c *WalkContext) StackPush(val any) {
|
||||
c.stack.Push(val)
|
||||
}
|
||||
|
||||
func (c *WalkContext) StackPop() any {
|
||||
return c.stack.Pop()
|
||||
}
|
||||
|
||||
func (c *WalkContext) StackPeek() any {
|
||||
return c.stack.Peek()
|
||||
}
|
||||
|
||||
type WalkOption struct {
|
||||
StackValues []any
|
||||
}
|
||||
|
||||
func WalkValue(value any, walker Walker, opts ...WalkOption) *WalkContext {
|
||||
var opt WalkOption
|
||||
if len(opts) > 0 {
|
||||
opt = opts[0]
|
||||
}
|
||||
|
||||
ctx := &WalkContext{
|
||||
stack: stack.New[any](),
|
||||
}
|
||||
|
||||
for _, v := range opt.StackValues {
|
||||
ctx.StackPush(v)
|
||||
}
|
||||
|
||||
doWalking(ctx, reflect.ValueOf(value), walker)
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
func doWalking(ctx *WalkContext, val reflect.Value, walker Walker) WalkingOp {
|
||||
if !WillWalkInto(val) {
|
||||
return Next
|
||||
}
|
||||
|
||||
switch val.Kind() {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if walker(ctx, ArrayBeginEvent{Value: val}) == Stop {
|
||||
return Stop
|
||||
}
|
||||
|
||||
for i := 0; i < val.Len(); i++ {
|
||||
eleVal := val.Index(i)
|
||||
|
||||
op := walker(ctx, ArrayArriveElementEvent{
|
||||
Index: i,
|
||||
Value: eleVal,
|
||||
})
|
||||
|
||||
if op == Skip {
|
||||
if walker(ctx, ArrayLeaveElementEvent{
|
||||
Index: i,
|
||||
Value: eleVal,
|
||||
}) == Stop {
|
||||
return Stop
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if op == Stop {
|
||||
return Stop
|
||||
}
|
||||
|
||||
if doWalking(ctx, eleVal, walker) == Stop {
|
||||
return Stop
|
||||
}
|
||||
|
||||
if walker(ctx, ArrayLeaveElementEvent{
|
||||
Index: i,
|
||||
Value: eleVal,
|
||||
}) == Stop {
|
||||
return Stop
|
||||
}
|
||||
}
|
||||
|
||||
if walker(ctx, ArrayEndEvent{Value: val}) == Stop {
|
||||
return Stop
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
if walker(ctx, MapBeginEvent{Value: val}) == Stop {
|
||||
return Stop
|
||||
}
|
||||
|
||||
keys := val.MapKeys()
|
||||
for _, key := range keys {
|
||||
val := val.MapIndex(key)
|
||||
|
||||
op := walker(ctx, MapArriveEntryEvent{
|
||||
Key: key,
|
||||
Value: val,
|
||||
})
|
||||
|
||||
if op == Skip {
|
||||
if walker(ctx, MapLeaveEntryEvent{
|
||||
Key: key,
|
||||
Value: val,
|
||||
}) == Stop {
|
||||
return Stop
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if op == Stop {
|
||||
return Stop
|
||||
}
|
||||
|
||||
if doWalking(ctx, val, walker) == Stop {
|
||||
return Stop
|
||||
}
|
||||
|
||||
if walker(ctx, MapLeaveEntryEvent{
|
||||
Key: key,
|
||||
Value: val,
|
||||
}) == Stop {
|
||||
return Stop
|
||||
}
|
||||
}
|
||||
|
||||
if walker(ctx, MapEndEvent{Value: val}) == Stop {
|
||||
return Stop
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
if walker(ctx, StructBeginEvent{Value: val}) == Stop {
|
||||
return Stop
|
||||
}
|
||||
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
field := val.Field(i)
|
||||
|
||||
op := walker(ctx, StructArriveFieldEvent{
|
||||
Info: val.Type().Field(i),
|
||||
Value: field,
|
||||
})
|
||||
|
||||
if op == Skip {
|
||||
if walker(ctx, StructLeaveFieldEvent{
|
||||
Info: val.Type().Field(i),
|
||||
Value: field,
|
||||
}) == Stop {
|
||||
return Stop
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if op == Stop {
|
||||
return Stop
|
||||
}
|
||||
|
||||
if doWalking(ctx, field, walker) == Stop {
|
||||
return Stop
|
||||
}
|
||||
|
||||
if walker(ctx, StructLeaveFieldEvent{
|
||||
Info: val.Type().Field(i),
|
||||
Value: field,
|
||||
}) == Stop {
|
||||
return Stop
|
||||
}
|
||||
}
|
||||
|
||||
if walker(ctx, StructEndEvent{Value: val}) == Stop {
|
||||
return Stop
|
||||
}
|
||||
|
||||
case reflect.Interface:
|
||||
fallthrough
|
||||
case reflect.Pointer:
|
||||
eleVal := val.Elem()
|
||||
return doWalking(ctx, eleVal, walker)
|
||||
}
|
||||
|
||||
return Next
|
||||
}
|
||||
|
||||
const (
|
||||
WillWalkIntoTypeKinds = (1 << reflect.Array) | (1 << reflect.Map) | (1 << reflect.Slice) | (1 << reflect.Struct)
|
||||
)
|
||||
|
||||
func WillWalkInto(val reflect.Value) bool {
|
||||
if val.IsZero() {
|
||||
return false
|
||||
}
|
||||
|
||||
typ := val.Type()
|
||||
typeKind := typ.Kind()
|
||||
if typeKind == reflect.Interface || typeKind == reflect.Pointer {
|
||||
return WillWalkInto(val.Elem())
|
||||
}
|
||||
|
||||
return ((1 << typeKind) & WillWalkIntoTypeKinds) != 0
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
go 1.23.0
|
||||
|
||||
toolchain go1.24.0
|
||||
|
||||
use (
|
||||
./common
|
||||
./client
|
||||
./platform
|
||||
)
|
||||
|
||||
replace gitlink.org.cn/JointCloud/pcm-participant-common v0.0.0 => ./common
|
|
@ -0,0 +1,46 @@
|
|||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/knz/go-libedit v1.10.1 h1:0pHpWtx9vcvC0xGZqEQlQdfSQs7WRlAjuPvk3fOZDCo=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86 h1:D6paGObi5Wud7xg83MaEFyjxQB1W5bz5d0IFppr+ymk=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c h1:bY6ktFuJkt+ZXkX0RChQch2FtHpWQLVS8Qo1YasiIVk=
|
||||
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM=
|
||||
github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY=
|
||||
github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636 h1:aSISeOcal5irEhJd1M+IrApc0PdcN7e7Aj4yuEnOrfQ=
|
||||
github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk=
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 h1:pXY9qYc/MP5zdvqWEUH6SjNiu7VhSjuVFTFiTcphaLU=
|
||||
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
|
||||
github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw=
|
||||
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp v0.0.0-20220218215828-6cf2b201936e h1:iWVPgObh6F4UDtjBLK51zsy5UHTPLQwCmsNjCsbKhQ0=
|
||||
golang.org/x/exp v0.0.0-20220218215828-6cf2b201936e/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
|
||||
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
|
||||
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
|
||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
nullprogram.com/x/optparse v1.0.0 h1:xGFgVi5ZaWOnYdac2foDT3vg0ZZC9ErXFV57mr4OHrI=
|
||||
rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=
|
|
@ -0,0 +1,60 @@
|
|||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
|
@ -0,0 +1,3 @@
|
|||
module gitlink.org.cn/JointCloud/pcm-participant-platform
|
||||
|
||||
go 1.23.0
|
Loading…
Reference in New Issue