diff --git a/go.mod b/go.mod index 296a0eb2..ed55be59 100644 --- a/go.mod +++ b/go.mod @@ -39,8 +39,8 @@ require ( github.com/forgoer/openssl v1.2.1 // indirect github.com/go-flac/go-flac v0.3.1 // indirect github.com/gomodule/redigo v1.8.8 // indirect - github.com/gookit/color v1.5.0 // indirect - github.com/gookit/goutil v0.5.2 // indirect + github.com/gookit/color v1.5.2 // indirect + github.com/gookit/goutil v0.6.0 // indirect github.com/hajimehoshi/go-mp3 v0.3.0 // indirect github.com/hajimehoshi/oto v1.0.1 // indirect github.com/icza/bitio v1.1.0 // indirect @@ -50,18 +50,18 @@ require ( github.com/mattn/go-isatty v0.0.14 // indirect github.com/mewkiz/flac v1.0.7 // indirect github.com/mewkiz/pkg v0.0.0-20211102230744-16a6ce8f1b77 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/rivo/uniseg v0.4.2 // indirect github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect - golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b // indirect + golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 // indirect golang.org/x/image v0.5.0 // indirect golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 // indirect - golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect + golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect golang.org/x/text v0.7.0 // indirect ) diff --git a/go.sum b/go.sum index 70a6ed4e..967c11bb 100644 --- a/go.sum +++ b/go.sum @@ -144,14 +144,15 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gookit/color v1.2.9/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg= github.com/gookit/color v1.3.0/go.mod h1:R3ogXq2B9rTbXoSHJ1HyUVAZ3poOJHpd9nQmyGZsfvQ= -github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw= github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= +github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI= +github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg= github.com/gookit/gcli/v2 v2.3.4 h1:g/o//Hcx0/cHEjG4bPypDFQtmAcKHzgBuOwXhadHovk= github.com/gookit/gcli/v2 v2.3.4/go.mod h1:HXk2Bon7HwODU2HGRQguIkG03jF6YrEokruony+Aj/c= github.com/gookit/goutil v0.3.5/go.mod h1:OHs5W5Xmfj4pCMXHnMxsDPrCc0SRbHLgJ2qs6wr5fxM= github.com/gookit/goutil v0.5.0/go.mod h1:pq1eTibwb2wN96jrci0xy7xogWzzo9CihOQJEAvz4yQ= -github.com/gookit/goutil v0.5.2 h1:1IbfIUiRV+Y+5IdgBeb/O7hWvq8OnnP1+sB/Ua2Q6jE= -github.com/gookit/goutil v0.5.2/go.mod h1:pq1eTibwb2wN96jrci0xy7xogWzzo9CihOQJEAvz4yQ= +github.com/gookit/goutil v0.6.0 h1:uGne/hUNe2xiJZB77QkeIsKsdPRaPyXFv9mUdDqq/Bw= +github.com/gookit/goutil v0.6.0/go.mod h1:DI6e4Waos7Yzjhoz75YFMpGl08m92cxNu0Tep36D/d0= github.com/gookit/ini/v2 v2.1.0 h1:L1qn8CfP1KYlbogKuMsJ3FiDdKDwvABCKeeuMWDlQzQ= github.com/gookit/ini/v2 v2.1.0/go.mod h1:r06awbwBtIHxjA7ndqWJkRgCAvSG+5FdSGrrbGfigtY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -239,7 +240,6 @@ github.com/mewkiz/pkg v0.0.0-20211102230744-16a6ce8f1b77/go.mod h1:J/rDzvIiwiVpv github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= @@ -311,13 +311,15 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/telanflow/cookiejar v0.0.0-20190719062046-114449e86aa5 h1:gTQl5nPlc9B53vFOKM8aJHwxB2BW2kM49PVR5526GBg= github.com/telanflow/cookiejar v0.0.0-20190719062046-114449e86aa5/go.mod h1:qNgA5MKwTh103SxGTooqZMiKxZTaV9UV3KjN7I7Drig= @@ -342,10 +344,8 @@ golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211202192323-5770296d904e h1:MUP6MR3rJ7Gk9LEia0LP2ytiH6MuCfs7qYz+47jGdD8= -golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b h1:Qwe1rC8PSniVfAFPFJeyUkB+zcysC3RgJBAGk7eqBEU= -golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -354,7 +354,6 @@ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8 golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/image v0.0.0-20190220214146-31aff87c08e9/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI= golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4= @@ -397,6 +396,8 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -429,21 +430,20 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM= golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc= +golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -500,8 +500,9 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -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= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/vendor/github.com/frolovo22/tag/.DS_Store b/vendor/github.com/frolovo22/tag/.DS_Store new file mode 100644 index 00000000..51227d71 Binary files /dev/null and b/vendor/github.com/frolovo22/tag/.DS_Store differ diff --git a/vendor/github.com/gookit/color/.nojekyll b/vendor/github.com/gookit/color/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/vendor/github.com/gookit/color/README.md b/vendor/github.com/gookit/color/README.md index 9e562d7c..718b11b5 100644 --- a/vendor/github.com/gookit/color/README.md +++ b/vendor/github.com/gookit/color/README.md @@ -2,14 +2,14 @@ ![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/gookit/color?style=flat-square) [![Actions Status](https://github.com/gookit/color/workflows/action-tests/badge.svg)](https://github.com/gookit/color/actions) -[![Codacy Badge](https://api.codacy.com/project/badge/Grade/51b28c5f7ffe4cc2b0f12ecf25ed247f)](https://app.codacy.com/app/inhere/color) +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/7fef8d74c1d64afc99ce0f2c6d3f8af1)](https://www.codacy.com/gh/gookit/color/dashboard?utm_source=github.com&utm_medium=referral&utm_content=gookit/color&utm_campaign=Badge_Grade) [![GoDoc](https://godoc.org/github.com/gookit/color?status.svg)](https://pkg.go.dev/github.com/gookit/color?tab=overview) [![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/gookit/color)](https://github.com/gookit/color) [![Build Status](https://travis-ci.org/gookit/color.svg?branch=master)](https://travis-ci.org/gookit/color) [![Coverage Status](https://coveralls.io/repos/github/gookit/color/badge.svg?branch=master)](https://coveralls.io/github/gookit/color?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/gookit/color)](https://goreportcard.com/report/github.com/gookit/color) -A command-line color library with true color support, universal API methods and Windows support. +A command-line color library with 16/256/True color support, universal API methods and Windows support. > **[中文说明](README.zh-CN.md)** @@ -30,7 +30,7 @@ Now, 256 colors and RGB colors have also been supported to work in Windows CMD a - See [this gist](https://gist.github.com/XVilka/8346728) for information on true color support - Support converts `HEX` `HSL` value to RGB color - Generic API methods: `Print`, `Printf`, `Println`, `Sprint`, `Sprintf` - - Supports HTML tag-style color rendering, such as `message`. + - Supports HTML tag-style color rendering, such as `message text`. - In addition to using built-in tags, it also supports custom color attributes - Custom color attributes support the use of 16 color names, 256 color values, rgb color values and hex color values - Support working on Windows `cmd` and `powerShell` terminal @@ -121,11 +121,6 @@ Supported on any Windows version. Provide generic API methods: `Print`, `Printf` ```go color.Bold.Println("bold message") -color.Black.Println("bold message") -color.White.Println("bold message") -color.Gray.Println("bold message") -color.Red.Println("yellow message") -color.Blue.Println("yellow message") color.Cyan.Println("yellow message") color.Yellow.Println("yellow message") color.Magenta.Println("yellow message") @@ -172,15 +167,9 @@ print message use defined style: ```go color.Info.Println("Info message") -color.Note.Println("Note message") color.Notice.Println("Notice message") color.Error.Println("Error message") -color.Danger.Println("Danger message") -color.Warn.Println("Warn message") -color.Debug.Println("Debug message") -color.Primary.Println("Primary message") -color.Question.Println("Question message") -color.Secondary.Println("Secondary message") +// ... ``` Run demo: `go run ./_examples/theme_basic.go` @@ -191,14 +180,8 @@ Run demo: `go run ./_examples/theme_basic.go` ```go color.Info.Tips("Info tips message") -color.Note.Tips("Note tips message") color.Notice.Tips("Notice tips message") color.Error.Tips("Error tips message") -color.Danger.Tips("Danger tips message") -color.Warn.Tips("Warn tips message") -color.Debug.Tips("Debug tips message") -color.Primary.Tips("Primary tips message") -color.Question.Tips("Question tips message") color.Secondary.Tips("Secondary tips message") ``` @@ -210,15 +193,9 @@ Run demo: `go run ./_examples/theme_tips.go` ```go color.Info.Prompt("Info prompt message") -color.Note.Prompt("Note prompt message") color.Notice.Prompt("Notice prompt message") color.Error.Prompt("Error prompt message") -color.Danger.Prompt("Danger prompt message") -color.Warn.Prompt("Warn prompt message") -color.Debug.Prompt("Debug prompt message") -color.Primary.Prompt("Primary prompt message") -color.Question.Prompt("Question prompt message") -color.Secondary.Prompt("Secondary prompt message") +// ... ``` Run demo: `go run ./_examples/theme_prompt.go` @@ -228,16 +205,9 @@ Run demo: `go run ./_examples/theme_prompt.go` **Block Style** ```go -color.Info.Block("Info block message") -color.Note.Block("Note block message") -color.Notice.Block("Notice block message") -color.Error.Block("Error block message") color.Danger.Block("Danger block message") color.Warn.Block("Warn block message") -color.Debug.Block("Debug block message") -color.Primary.Block("Primary block message") -color.Question.Block("Question block message") -color.Secondary.Block("Secondary block message") +// ... ``` Run demo: `go run ./_examples/theme_block.go` @@ -372,7 +342,32 @@ s.Printf("style with %s\n", "options") ## HTML-like tag usage -**Supported** on Windows `cmd.exe` `PowerShell` . +`Print,Printf,Println` functions support auto parse and render color tags. + +```go + text := ` + gookit/color: + A command-line + color library with 256-color + and True-color support, + universal API methods + and Windows support. +` + color.Print(text) +``` + +Preview, code please see [_examples/demo_tag.go](_examples/demo_tag.go): + +![demo_tag](_examples/images/demo_tag.png) + +**Tag formats:** + +- Use built in tags: `CONTENT` e.g: `message` +- Custom tag attributes: `CONTENT` e.g: `wel` + +> **Supported** on Windows `cmd.exe` `PowerShell`. + +Examples: ```go // use style tag @@ -387,20 +382,63 @@ color.Print("hello, welcome\n") // Custom label attr: Supports the use of 16 color names, 256 color values, rgb color values and hex color values color.Println("hello, welcome") ``` + +### Tag attributes + +tag attributes format: + +```text +attr format: + // VALUE please see var: FgColors, BgColors, AllOptions + "fg=VALUE;bg=VALUE;op=VALUE" + +16 color: + "fg=yellow" + "bg=red" + "op=bold,underscore" // option is allow multi value + "fg=white;bg=blue;op=bold" + "fg=white;op=bold,underscore" + +256 color: + "fg=167" + "fg=167;bg=23" + "fg=167;bg=23;op=bold" + +True color: + // hex + "fg=fc1cac" + "fg=fc1cac;bg=c2c3c4" + // r,g,b + "fg=23,45,214" + "fg=23,45,214;bg=109,99,88" +``` + +> tag attributes parse please see `func ParseCodeFromAttr()` -- `color.Tag` +### Built-in tags + +Built-in tags please see var `colorTags` in [color_tag.go](color_tag.go) ```go -// set a style tag -color.Tag("info").Print("info style text") -color.Tag("info").Printf("%s style text", "info") -color.Tag("info").Println("info style text") +// use style tag +color.Print("hello, welcome") +color.Println("hello") +color.Println("hello") ``` Run demo: `go run ./_examples/color_tag.go` ![color-tags](_examples/images/color-tags.png) +**Use `color.Tag` build message**: + +```go +// set a style tag +color.Tag("info").Print("info style text") +color.Tag("info").Printf("%s style text", "info") +color.Tag("info").Println("info style text") +``` + ## Color convert Supports conversion between Rgb, 256, 16 colors, `Rgb <=> 256 <=> 16` @@ -418,7 +456,41 @@ rgb.Println("rgb color") rgb.C256().Println("256 color") ``` -**More functions for convert to `RGBColor`**: +### Convert utils + +`color` has many built-in color conversion utility functions. + +```go +func Basic2hex(val uint8) string + +func Bg2Fg(val uint8) uint8 +func Fg2Bg(val uint8) uint8 + +func C256ToRgb(val uint8) (rgb []uint8) +func C256ToRgbV1(val uint8) (rgb []uint8) + +func Hex2basic(hex string, asBg ...bool) uint8 +func Hex2rgb(hex string) []int +func HexToRGB(hex string) []int +func HexToRgb(hex string) (rgb []int) + +func HslIntToRgb(h, s, l int) (rgb []uint8) +func HslToRgb(h, s, l float64) (rgb []uint8) +func HsvToRgb(h, s, v int) (rgb []uint8) + +func Rgb2ansi(r, g, b uint8, isBg bool) uint8 +func Rgb2basic(r, g, b uint8, isBg bool) uint8 +func Rgb2hex(rgb []int) string +func Rgb2short(r, g, b uint8) uint8 +func RgbTo256(r, g, b uint8) uint8 +func RgbTo256Table() map[string]uint8 +func RgbToAnsi(r, g, b uint8, isBg bool) uint8 +func RgbToHex(rgb []int) string +func RgbToHsl(r, g, b uint8) []float64 +func RgbToHslInt(r, g, b uint8) []int +``` + +**Convert to `RGBColor`**: - `func RGBFromSlice(rgb []uint8, isBg ...bool) RGBColor` - `func RGBFromString(rgb string, isBg ...bool) RGBColor` @@ -426,7 +498,7 @@ rgb.C256().Println("256 color") - `func HSL(h, s, l float64, isBg ...bool) RGBColor` - `func HSLInt(h, s, l int, isBg ...bool) RGBColor` -## Func refer +## Util functions There are some useful functions reference @@ -437,9 +509,31 @@ There are some useful functions reference - `ClearCode(str string) string` Use for clear color codes - `ClearTag(s string) string` clear all color html-tag for a string - `IsConsole(w io.Writer)` Determine whether w is one of stderr, stdout, stdin -- `HexToRgb(hex string) (rgb []int)` Convert hex color string to RGB numbers -- `RgbToHex(rgb []int) string` Convert RGB to hex code -- More useful func please see https://pkg.go.dev/github.com/gookit/color + +> More useful func please see https://pkg.go.dev/github.com/gookit/color + +### Detect color level + +`color` automatically checks the color levels supported by the current environment. + +```go +// Level is the color level supported by a terminal. +type Level = terminfo.ColorLevel + +// terminal color available level alias of the terminfo.ColorLevel* +const ( + LevelNo = terminfo.ColorLevelNone // not support color. + Level16 = terminfo.ColorLevelBasic // basic - 3/4 bit color supported + Level256 = terminfo.ColorLevelHundreds // hundreds - 8-bit color supported + LevelRgb = terminfo.ColorLevelMillions // millions - (24 bit)true color supported +) +``` + +- `func SupportColor() bool` Whether the current environment supports color output +- `func Support256Color() bool` Whether the current environment supports 256-color output +- `func SupportTrueColor() bool` Whether the current environment supports (RGB)True-color output +- `func TermColorLevel() Level` Get the currently supported color level + ## Projects using color diff --git a/vendor/github.com/gookit/color/README.zh-CN.md b/vendor/github.com/gookit/color/README.zh-CN.md index 0fe7d4a7..1b144058 100644 --- a/vendor/github.com/gookit/color/README.zh-CN.md +++ b/vendor/github.com/gookit/color/README.zh-CN.md @@ -2,14 +2,14 @@ ![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/gookit/color?style=flat-square) [![Actions Status](https://github.com/gookit/color/workflows/action-tests/badge.svg)](https://github.com/gookit/color/actions) -[![Codacy Badge](https://api.codacy.com/project/badge/Grade/51b28c5f7ffe4cc2b0f12ecf25ed247f)](https://app.codacy.com/app/inhere/color) +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/7fef8d74c1d64afc99ce0f2c6d3f8af1)](https://www.codacy.com/gh/gookit/color/dashboard?utm_source=github.com&utm_medium=referral&utm_content=gookit/color&utm_campaign=Badge_Grade) [![GoDoc](https://godoc.org/github.com/gookit/color?status.svg)](https://pkg.go.dev/github.com/gookit/color?tab=overview) [![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/gookit/color)](https://github.com/gookit/color) [![Build Status](https://travis-ci.org/gookit/color.svg?branch=master)](https://travis-ci.org/gookit/color) [![Coverage Status](https://coveralls.io/repos/github/gookit/color/badge.svg?branch=master)](https://coveralls.io/github/gookit/color?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/gookit/color)](https://goreportcard.com/report/github.com/gookit/color) -Golang下的命令行色彩使用库, 拥有丰富的色彩渲染输出,通用的API方法,兼容Windows系统 +Golang下的命令行色彩使用库, 拥有丰富的色彩(16/256/True)渲染输出,通用的API方法,兼容Windows系统 > **[EN README](README.md)** @@ -31,7 +31,7 @@ Golang下的命令行色彩使用库, 拥有丰富的色彩渲染输出,通用 - 支持转换 `HEX` `HSL` 等为RGB色彩 - 提供通用的API方法:`Print` `Printf` `Println` `Sprint` `Sprintf` - 同时支持html标签式的颜色渲染,除了使用内置标签,同时支持自定义颜色属性 - - 例如: `this an message` 标签内部的文本将会渲染为绿色字体 + - 例如: `this an message text` 标签内部文本将会渲染对应色彩 - 自定义颜色属性: 支持使用16色彩名称,256色彩值,rgb色彩值以及hex色彩值 - 基础色彩: `Bold` `Black` `White` `Gray` `Red` `Green` `Yellow` `Blue` `Magenta` `Cyan` - 扩展风格: `Info` `Note` `Light` `Error` `Danger` `Notice` `Success` `Comment` `Primary` `Warning` `Question` `Secondary` @@ -125,12 +125,7 @@ func main() { color.Bold.Println("bold message") color.Black.Println("bold message") color.White.Println("bold message") -color.Gray.Println("bold message") -color.Red.Println("yellow message") -color.Blue.Println("yellow message") -color.Cyan.Println("yellow message") -color.Yellow.Println("yellow message") -color.Magenta.Println("yellow message") +// ... // Only use foreground color color.FgCyan.Printf("Simple to use %s\n", "color") @@ -186,13 +181,7 @@ color.Reset() color.Info.Println("Info message") color.Note.Println("Note message") color.Notice.Println("Notice message") -color.Error.Println("Error message") -color.Danger.Println("Danger message") -color.Warn.Println("Warn message") -color.Debug.Println("Debug message") -color.Primary.Println("Primary message") -color.Question.Println("Question message") -color.Secondary.Println("Secondary message") +// ... ``` Run demo: `go run ./_examples/theme_basic.go` @@ -203,15 +192,9 @@ Run demo: `go run ./_examples/theme_basic.go` ```go color.Info.Tips("Info tips message") -color.Note.Tips("Note tips message") color.Notice.Tips("Notice tips message") color.Error.Tips("Error tips message") -color.Danger.Tips("Danger tips message") -color.Warn.Tips("Warn tips message") -color.Debug.Tips("Debug tips message") -color.Primary.Tips("Primary tips message") -color.Question.Tips("Question tips message") -color.Secondary.Tips("Secondary tips message") +// ... ``` Run demo: `go run ./_examples/theme_tips.go` @@ -222,8 +205,6 @@ Run demo: `go run ./_examples/theme_tips.go` ```go color.Info.Prompt("Info prompt message") -color.Note.Prompt("Note prompt message") -color.Notice.Prompt("Notice prompt message") color.Error.Prompt("Error prompt message") color.Danger.Prompt("Danger prompt message") ``` @@ -237,9 +218,7 @@ Run demo: `go run ./_examples/theme_prompt.go` ```go color.Warn.Block("Warn block message") color.Debug.Block("Debug block message") -color.Primary.Block("Primary block message") color.Question.Block("Question block message") -color.Secondary.Block("Secondary block message") ``` Run demo: `go run ./_examples/theme_block.go` @@ -252,7 +231,7 @@ Run demo: `go run ./_examples/theme_block.go` ### 使用前景或后景色 - - `color.C256(val uint8, isBg ...bool) Color256` +- `color.C256(val uint8, isBg ...bool) Color256` ```go c := color.C256(132) // fg color @@ -340,7 +319,7 @@ c.Printf("format %s", "message") ### 使用RGB风格 -> 可同时设置前景和背景色 +> TIP: 可同时设置前景和背景色 - `color.NewRGBStyle(fg RGBColor, bg ...RGBColor) *RGBStyle` @@ -370,8 +349,33 @@ s.Printf("style with %s\n", "options") ## 使用颜色标签 +`Print,Printf,Println` 等方法支持自动解析并渲染 HTML 风格的颜色标签 + > **支持** 在windows `cmd.exe` `PowerShell` 使用 +简单示例: + +```go + text := ` + gookit/color: + A command-line + color library with 256-color + and True-color support, + universal API methods + and Windows support. +` + color.Print(text) +``` + +输出效果, 示例代码请看 [_examples/demo_tag.go](_examples/demo_tag.go): + +![demo_tag](_examples/images/demo_tag.png) + +**颜色标签格式:** + +- 直接使用内置风格标签: `CONTENT` e.g: `message` +- 自定义标签属性: `CONTENT` e.g: `wel` + 使用内置的颜色标签,可以非常方便简单的构建自己需要的任何格式 > 同时支持自定义颜色属性: 支持使用16色彩名称,256色彩值,rgb色彩值以及hex色彩值 @@ -390,21 +394,64 @@ color.Print("hello, welcome\n") color.Println("hello, welcome") ``` -- 使用 `color.Tag` +### 自定义标签属性 + +标签属性格式: + +```text +attr format: + // VALUE please see var: FgColors, BgColors, AllOptions + "fg=VALUE;bg=VALUE;op=VALUE" -给后面输出的文本信息加上给定的颜色风格标签 +16 color: + "fg=yellow" + "bg=red" + "op=bold,underscore" // option is allow multi value + "fg=white;bg=blue;op=bold" + "fg=white;op=bold,underscore" + +256 color: + "fg=167" + "fg=167;bg=23" + "fg=167;bg=23;op=bold" + +True color: + // hex + "fg=fc1cac" + "fg=fc1cac;bg=c2c3c4" + // r,g,b + "fg=23,45,214" + "fg=23,45,214;bg=109,99,88" +``` + +> tag attributes parse please see `func ParseCodeFromAttr()` + +### 内置标签 + +内置标签请参见变量 `colorTags` 定义, 源文件 [color_tag.go](color_tag.go) ```go -// set a style tag -color.Tag("info").Print("info style text") -color.Tag("info").Printf("%s style text", "info") -color.Tag("info").Println("info style text") +// use style tag +color.Print("hello, welcome") +color.Println("hello") +color.Println("hello") ``` > 运行 demo: `go run ./_examples/color_tag.go` ![color-tags](_examples/images/color-tags.png) +**使用 `color.Tag` 包装标签**: + +可以使用通用的输出API方法,给后面输出的文本信息加上给定的颜色风格标签 + +```go +// set a style tag +color.Tag("info").Print("info style text") +color.Tag("info").Printf("%s style text", "info") +color.Tag("info").Println("info style text") +``` + ## 颜色转换 支持 Rgb, 256, 16 色彩之间的互相转换 `Rgb <=> 256 <=> 16` @@ -422,7 +469,41 @@ rgb.Println("rgb color") rgb.C256().Println("256 color") ``` -**更多转换方法转换为 `RGBColor`**: +### 颜色转换方法 + +`color` 内置了许多颜色转换工具方法 + +```go +func Basic2hex(val uint8) string + +func Bg2Fg(val uint8) uint8 +func Fg2Bg(val uint8) uint8 + +func C256ToRgb(val uint8) (rgb []uint8) +func C256ToRgbV1(val uint8) (rgb []uint8) + +func Hex2basic(hex string, asBg ...bool) uint8 +func Hex2rgb(hex string) []int +func HexToRGB(hex string) []int +func HexToRgb(hex string) (rgb []int) + +func HslIntToRgb(h, s, l int) (rgb []uint8) +func HslToRgb(h, s, l float64) (rgb []uint8) +func HsvToRgb(h, s, v int) (rgb []uint8) + +func Rgb2ansi(r, g, b uint8, isBg bool) uint8 +func Rgb2basic(r, g, b uint8, isBg bool) uint8 +func Rgb2hex(rgb []int) string +func Rgb2short(r, g, b uint8) uint8 +func RgbTo256(r, g, b uint8) uint8 +func RgbTo256Table() map[string]uint8 +func RgbToAnsi(r, g, b uint8, isBg bool) uint8 +func RgbToHex(rgb []int) string +func RgbToHsl(r, g, b uint8) []float64 +func RgbToHslInt(r, g, b uint8) []int +``` + +**转换为 `RGBColor`**: - `func RGBFromSlice(rgb []uint8, isBg ...bool) RGBColor` - `func RGBFromString(rgb string, isBg ...bool) RGBColor` @@ -430,21 +511,41 @@ rgb.C256().Println("256 color") - `func HSL(h, s, l float64, isBg ...bool) RGBColor` - `func HSLInt(h, s, l int, isBg ...bool) RGBColor` -## 方法参考 +## 工具方法参考 一些有用的工具方法参考 -- `Disable()` disable color render -- `SetOutput(io.Writer)` custom set the colored text output writer -- `ForceOpenColor()` force open color render +- `Disable()` 禁用颜色渲染输出 +- `SetOutput(io.Writer)` 自定义设置渲染后的彩色文本输出位置 +- `ForceOpenColor()` 强制开启颜色渲染 - `ClearCode(str string) string` Use for clear color codes - `Colors2code(colors ...Color) string` Convert colors to code. return like "32;45;3" - `ClearTag(s string) string` clear all color html-tag for a string - `IsConsole(w io.Writer)` Determine whether w is one of stderr, stdout, stdin -- `HexToRgb(hex string) (rgb []int)` Convert hex color string to RGB numbers -- `RgbToHex(rgb []int) string` Convert RGB to hex code - 更多请查看文档 https://pkg.go.dev/github.com/gookit/color +### 检测支持的颜色级别 + +`color` 会自动检查当前环境支持的颜色级别 + +```go +// Level is the color level supported by a terminal. +type Level = terminfo.ColorLevel + +// terminal color available level alias of the terminfo.ColorLevel* +const ( + LevelNo = terminfo.ColorLevelNone // not support color. + Level16 = terminfo.ColorLevelBasic // basic - 3/4 bit color supported + Level256 = terminfo.ColorLevelHundreds // hundreds - 8-bit color supported + LevelRgb = terminfo.ColorLevelMillions // millions - (24 bit)true color supported +) +``` + +- `func SupportColor() bool` 当前环境是否支持色彩输出 +- `func Support256Color() bool` 当前环境是否支持256色彩输出 +- `func SupportTrueColor() bool` 当前环境是否支持(RGB)True色彩输出 +- `func TermColorLevel() Level` 获取当前支持的颜色级别 + ## 使用Color的项目 看看这些使用了 https://github.com/gookit/color 的项目: diff --git a/vendor/github.com/gookit/color/color.go b/vendor/github.com/gookit/color/color.go index edb2a5d7..59e0b0b6 100644 --- a/vendor/github.com/gookit/color/color.go +++ b/vendor/github.com/gookit/color/color.go @@ -1,5 +1,5 @@ /* -Package color is Command line color library. +Package color is command line color library. Support rich color rendering output, universal API method, compatible with Windows system Source code and other details for the project are available at GitHub: @@ -15,29 +15,29 @@ import ( "io" "os" "regexp" + "strings" "github.com/xo/terminfo" ) -// terminal color available level alias of the terminfo.ColorLevel* -const ( - LevelNo = terminfo.ColorLevelNone // not support color. - Level16 = terminfo.ColorLevelBasic // 3/4 bit color supported - Level256 = terminfo.ColorLevelHundreds // 8 bit color supported - LevelRgb = terminfo.ColorLevelMillions // (24 bit)true color supported -) - // color render templates +// // ESC 操作的表示: -// "\033"(Octal 8进制) = "\x1b"(Hexadecimal 16进制) = 27 (10进制) +// +// "\033"(Octal 8进制) = "\x1b"(Hexadecimal 16进制) = 27 (10进制) const ( - SettingTpl = "\x1b[%sm" + // StartSet chars + StartSet = "\x1b[" + // ResetSet close all properties. + ResetSet = "\x1b[0m" + // SettingTpl string. + SettingTpl = "\x1b[%sm" + // FullColorTpl for build color code FullColorTpl = "\x1b[%sm%s\x1b[0m" + // CodeSuffix string for color code. + CodeSuffix = "[0m" ) -// ResetSet Close all properties. -const ResetSet = "\x1b[0m" - // CodeExpr regex to clear color codes eg "\033[1;36mText\x1b[0m" const CodeExpr = `\033\[[\d;?]+m` @@ -61,10 +61,10 @@ var ( // output the default io.Writer message print output io.Writer = os.Stdout // mark current env, It's like in `cmd.exe` - // if not in windows, it's always is False. + // if not in windows, it's always False. isLikeInCmd bool // the color support level for current terminal - // needVTP - need enable VTP, only for windows OS + // needVTP - need enable VTP, only for Windows OS colorLevel, needVTP = detectTermColorLevel() // match color codes codeRegex = regexp.MustCompile(CodeExpr) @@ -73,12 +73,12 @@ var ( // supportColor = IsSupportColor() ) -// TermColorLevel value on current ENV -func TermColorLevel() terminfo.ColorLevel { +// TermColorLevel Get the currently supported color level +func TermColorLevel() Level { return colorLevel } -// SupportColor on the current ENV +// SupportColor Whether the current environment supports color output func SupportColor() bool { return colorLevel > terminfo.ColorLevelNone } @@ -88,12 +88,12 @@ func SupportColor() bool { // return colorLevel > terminfo.ColorLevelNone // } -// Support256Color on the current ENV +// Support256Color Whether the current environment supports 256-color output func Support256Color() bool { return colorLevel > terminfo.ColorLevelBasic } -// SupportTrueColor on the current ENV +// SupportTrueColor Whether the current environment supports (RGB)True-color output func SupportTrueColor() bool { return colorLevel > terminfo.ColorLevelHundreds } @@ -102,7 +102,7 @@ func SupportTrueColor() bool { * global settings *************************************************************/ -// Set set console color attributes +// Set console color attributes func Set(colors ...Color) (int, error) { code := Colors2code(colors...) err := SetTerminal(code) @@ -144,7 +144,7 @@ func ResetOptions() { output = os.Stdout } -// ForceColor force open color render +// ForceSetColorLevel force open color render func ForceSetColorLevel(level terminfo.ColorLevel) terminfo.ColorLevel { oldLevelVal := colorLevel colorLevel = level @@ -163,7 +163,8 @@ func ForceOpenColor() terminfo.ColorLevel { } // IsLikeInCmd check result -// Deprecated +// +// Deprecated: please don't use func IsLikeInCmd() bool { return isLikeInCmd } @@ -178,8 +179,10 @@ func InnerErrs() []error { *************************************************************/ // RenderCode render message by color code. +// // Usage: -// msg := RenderCode("3;32;45", "some", "message") +// +// msg := RenderCode("3;32;45", "some", "message") func RenderCode(code string, args ...interface{}) string { var message string if ln := len(args); ln == 0 { @@ -196,28 +199,31 @@ func RenderCode(code string, args ...interface{}) string { return ClearCode(message) } - return fmt.Sprintf(FullColorTpl, code, message) + // return fmt.Sprintf(FullColorTpl, code, message) + return StartSet + code + "m" + message + ResetSet } // RenderWithSpaces Render code with spaces. // If the number of args is > 1, a space will be added between the args func RenderWithSpaces(code string, args ...interface{}) string { - message := formatArgsForPrintln(args) + msg := formatArgsForPrintln(args) if len(code) == 0 { - return message + return msg } // disabled OR not support color if !Enable || !SupportColor() { - return ClearCode(message) + return ClearCode(msg) } - return fmt.Sprintf(FullColorTpl, code, message) + return StartSet + code + "m" + msg + ResetSet } // RenderString render a string with color code. +// // Usage: -// msg := RenderString("3;32;45", "a message") +// +// msg := RenderString("3;32;45", "a message") func RenderString(code string, str string) string { if len(code) == 0 || str == "" { return str @@ -228,11 +234,18 @@ func RenderString(code string, str string) string { return ClearCode(str) } - return fmt.Sprintf(FullColorTpl, code, str) + // return fmt.Sprintf(FullColorTpl, code, str) + return StartSet + code + "m" + str + ResetSet } // ClearCode clear color codes. -// eg: "\033[36;1mText\x1b[0m" -> "Text" +// +// eg: +// +// "\033[36;1mText\x1b[0m" -> "Text" func ClearCode(str string) string { + if !strings.Contains(str, CodeSuffix) { + return str + } return codeRegex.ReplaceAllString(str, "") } diff --git a/vendor/github.com/gookit/color/color_16.go b/vendor/github.com/gookit/color/color_16.go index f2a211a2..3551521c 100644 --- a/vendor/github.com/gookit/color/color_16.go +++ b/vendor/github.com/gookit/color/color_16.go @@ -116,7 +116,7 @@ const ( OpItalic // 3 斜体(不是所有的终端仿真器都支持) OpUnderscore // 4 下划线 OpBlink // 5 闪烁 - OpFastBlink // 5 快速闪烁(未广泛支持) + OpFastBlink // 6 快速闪烁(未广泛支持) OpReverse // 7 颠倒的 交换背景色与前景色 OpConcealed // 8 隐匿的 OpStrikethrough // 9 删除的,删除线(未广泛支持) @@ -166,10 +166,8 @@ const ( BgHiMagenta = BgLightMagenta ) -// Bit4 an method for create Color -func Bit4(code uint8) Color { - return Color(code) -} +// Bit4 a method for create Color +func Bit4(code uint8) Color { return Color(code) } /************************************************************* * Color render methods @@ -185,33 +183,28 @@ func (c Color) Name() string { } // Text render a text message -func (c Color) Text(message string) string { - return RenderString(c.String(), message) -} +func (c Color) Text(message string) string { return RenderString(c.String(), message) } // Render messages by color setting +// // Usage: // green := color.FgGreen.Render // fmt.Println(green("message")) -func (c Color) Render(a ...interface{}) string { - return RenderCode(c.String(), a...) -} +func (c Color) Render(a ...interface{}) string { return RenderCode(c.String(), a...) } // Renderln messages by color setting. // like Println, will add spaces for each argument +// // Usage: // green := color.FgGreen.Renderln // fmt.Println(green("message")) -func (c Color) Renderln(a ...interface{}) string { - return RenderWithSpaces(c.String(), a...) -} +func (c Color) Renderln(a ...interface{}) string { return RenderWithSpaces(c.String(), a...) } // Sprint render messages by color setting. is alias of the Render() -func (c Color) Sprint(a ...interface{}) string { - return RenderCode(c.String(), a...) -} +func (c Color) Sprint(a ...interface{}) string { return RenderCode(c.String(), a...) } // Sprintf format and render message. +// // Usage: // green := color.Green.Sprintf // colored := green("message") @@ -220,6 +213,7 @@ func (c Color) Sprintf(format string, args ...interface{}) string { } // Print messages. +// // Usage: // color.Green.Print("message") // OR: @@ -230,6 +224,7 @@ func (c Color) Print(args ...interface{}) { } // Printf format and print messages. +// // Usage: // color.Cyan.Printf("string %s", "arg0") func (c Color) Printf(format string, a ...interface{}) { @@ -237,9 +232,7 @@ func (c Color) Printf(format string, a ...interface{}) { } // Println messages with new line -func (c Color) Println(a ...interface{}) { - doPrintlnV2(c.String(), a) -} +func (c Color) Println(a ...interface{}) { doPrintlnV2(c.String(), a) } // Light current color. eg: 36(FgCyan) -> 96(FgLightCyan). // @@ -298,7 +291,7 @@ func (c Color) C256() Color256 { // ToFg always convert fg func (c Color) ToFg() Color { val := uint8(c) - // is option code, don't change + // option code, don't change if val < 10 { return c } @@ -308,7 +301,7 @@ func (c Color) ToFg() Color { // ToBg always convert bg func (c Color) ToBg() Color { val := uint8(c) - // is option code, don't change + // option code, don't change if val < 10 { return c } @@ -327,20 +320,16 @@ func (c Color) RGB() RGBColor { // Code convert to code string. eg "35" func (c Color) Code() string { - // return fmt.Sprintf("%d", c) - return strconv.Itoa(int(c)) + return strconv.FormatInt(int64(c), 10) } // String convert to code string. eg "35" func (c Color) String() string { - // return fmt.Sprintf("%d", c) - return strconv.Itoa(int(c)) + return strconv.FormatInt(int64(c), 10) } // IsValid color value -func (c Color) IsValid() bool { - return c < 107 -} +func (c Color) IsValid() bool { return c < 107 } /************************************************************* * basic color maps @@ -397,8 +386,8 @@ var ExBgColors = map[string]Color{ } // Options color options map -// Deprecated -// NOTICE: please use AllOptions instead. +// +// Deprecated: please use AllOptions instead. var Options = AllOptions // AllOptions color options map @@ -416,9 +405,10 @@ var AllOptions = map[string]Color{ var ( // TODO basic name alias // basicNameAlias = map[string]string{} - + // optionWithAlias = buildOpWithAlias() // basic color name to code - name2basicMap = initName2basicMap() + // name2basicMap = initName2basicMap() + // basic2nameMap basic color code to name basic2nameMap = map[uint8]string{ 30: "black", @@ -475,10 +465,13 @@ func Basic2nameMap() map[uint8]string { return basic2nameMap } -func initName2basicMap() map[string]uint8 { - n2b := make(map[string]uint8, len(basic2nameMap)) - for u, s := range basic2nameMap { - n2b[s] = u - } - return n2b -} +// func initName2basicMap() map[string]uint8 { +// n2b := make(map[string]uint8, len(basic2nameMap)) +// for u, s := range basic2nameMap { +// n2b[s] = u +// } +// return n2b +// } + +// func buildOpWithAlias() map[string]uint8 { +// } diff --git a/vendor/github.com/gookit/color/color_256.go b/vendor/github.com/gookit/color/color_256.go index efd6dca3..c95c0f7b 100644 --- a/vendor/github.com/gookit/color/color_256.go +++ b/vendor/github.com/gookit/color/color_256.go @@ -149,15 +149,12 @@ func (c Color256) FullCode() string { // String convert to color code string with prefix. eg: "38;5;12" func (c Color256) String() string { if c[1] == AsFg { // 0 is Fg - // return fmt.Sprintf(TplFg256, c[0]) return Fg256Pfx + strconv.Itoa(int(c[0])) } if c[1] == AsBg { // 1 is Bg - // return fmt.Sprintf(TplBg256, c[0]) return Bg256Pfx + strconv.Itoa(int(c[0])) } - return "" // empty } @@ -198,8 +195,6 @@ func (c Color256) IsEmpty() bool { // 都是由两位uint8组成, 第一位是色彩值; // 第二位与 Bit8Color 不一样的是,在这里表示是否设置了值 0 未设置 !=0 已设置 type Style256 struct { - // p Printer - // Name of the style Name string // color options of the style @@ -209,6 +204,7 @@ type Style256 struct { } // S256 create a color256 style +// // Usage: // s := color.S256() // s := color.S256(132) // fg @@ -293,16 +289,15 @@ func (s *Style256) Code() string { func (s *Style256) String() string { var ss []string if s.fg[1] > 0 { - ss = append(ss, fmt.Sprintf(TplFg256, s.fg[0])) + ss = append(ss, Fg256Pfx+strconv.FormatInt(int64(s.fg[0]), 10)) } if s.bg[1] > 0 { - ss = append(ss, fmt.Sprintf(TplBg256, s.bg[0])) + ss = append(ss, Bg256Pfx+strconv.FormatInt(int64(s.bg[0]), 10)) } if s.opts.IsValid() { ss = append(ss, s.opts.String()) } - return strings.Join(ss, ";") } diff --git a/vendor/github.com/gookit/color/color_rgb.go b/vendor/github.com/gookit/color/color_rgb.go index 5f16d7f9..ff3c1bb0 100644 --- a/vendor/github.com/gookit/color/color_rgb.go +++ b/vendor/github.com/gookit/color/color_rgb.go @@ -35,27 +35,6 @@ const ( AsBg ) -// values from https://github.com/go-terminfo/terminfo -// var ( -// RgbaBlack = image_color.RGBA{0, 0, 0, 255} -// Red = color.RGBA{205, 0, 0, 255} -// Green = color.RGBA{0, 205, 0, 255} -// Orange = color.RGBA{205, 205, 0, 255} -// Blue = color.RGBA{0, 0, 238, 255} -// Magenta = color.RGBA{205, 0, 205, 255} -// Cyan = color.RGBA{0, 205, 205, 255} -// LightGrey = color.RGBA{229, 229, 229, 255} -// -// DarkGrey = color.RGBA{127, 127, 127, 255} -// LightRed = color.RGBA{255, 0, 0, 255} -// LightGreen = color.RGBA{0, 255, 0, 255} -// Yellow = color.RGBA{255, 255, 0, 255} -// LightBlue = color.RGBA{92, 92, 255, 255} -// LightMagenta = color.RGBA{255, 0, 255, 255} -// LightCyan = color.RGBA{0, 255, 255, 255} -// White = color.RGBA{255, 255, 255, 255} -// ) - /************************************************************* * RGB Color(Bit24Color, TrueColor) *************************************************************/ @@ -78,6 +57,7 @@ type RGBColor [4]uint8 var emptyRGBColor = RGBColor{3: 99} // RGB color create. +// // Usage: // c := RGB(30,144,255) // c := RGB(30,144,255, true) @@ -127,15 +107,14 @@ func HEX(hex string, isBg ...bool) RGBColor { // Hex alias of the HEX() func Hex(hex string, isBg ...bool) RGBColor { return HEX(hex, isBg...) } +// RGBFromHEX quick RGBColor from hex string, alias of HEX() +func RGBFromHEX(hex string, isBg ...bool) RGBColor { return HEX(hex, isBg...) } + // HSL create RGB color from a hsl value. // more see HslToRgb() func HSL(h, s, l float64, isBg ...bool) RGBColor { - if rgb := HslToRgb(h, s, l); len(rgb) > 0 { - return RGB(rgb[0], rgb[1], rgb[2], isBg...) - } - - // mark is empty - return emptyRGBColor + rgb := HslToRgb(h, s, l) + return RGB(rgb[0], rgb[1], rgb[2], isBg...) } // Hsl alias of the HSL() @@ -144,12 +123,8 @@ func Hsl(h, s, l float64, isBg ...bool) RGBColor { return HSL(h, s, l, isBg...) // HSLInt create RGB color from a hsl int value. // more see HslIntToRgb() func HSLInt(h, s, l int, isBg ...bool) RGBColor { - if rgb := HslIntToRgb(h, s, l); len(rgb) > 0 { - return RGB(rgb[0], rgb[1], rgb[2], isBg...) - } - - // mark is empty - return emptyRGBColor + rgb := HslIntToRgb(h, s, l) + return RGB(rgb[0], rgb[1], rgb[2], isBg...) } // HslInt alias of the HSLInt() @@ -161,7 +136,7 @@ func RGBFromSlice(rgb []uint8, isBg ...bool) RGBColor { } // RGBFromString create RGB color from a string. -// support use color name in the {namedRgbMap} +// Support use color name in the {namedRgbMap} // // Usage: // c := RGBFromString("170,187,204") @@ -181,17 +156,17 @@ func RGBFromString(rgb string, isBg ...bool) RGBColor { return emptyRGBColor } - var ar [3]int + var ar [3]uint8 for i, val := range ss { iv, err := strconv.Atoi(val) - if err != nil { + if err != nil || !isValidUint8(iv) { return emptyRGBColor } - ar[i] = iv + ar[i] = uint8(iv) } - return RGB(uint8(ar[0]), uint8(ar[1]), uint8(ar[2]), isBg...) + return RGB(ar[0], ar[1], ar[2], isBg...) } // Set terminal by rgb/true color code @@ -299,13 +274,13 @@ func (c RGBColor) C16() Color { return c.Basic() } * RGB Style *************************************************************/ -// RGBStyle definition. +// RGBStyle supports set foreground and background color // -// Foreground/Background color // All are composed of 4 digits uint8, the first three digits are the color value; // The last bit is different from RGBColor, here it indicates whether the value is set. -// - 1 Has been set -// - ^1 Not set +// +// 1 Has been set +// ^1 Not set type RGBStyle struct { // Name of the style Name string @@ -326,6 +301,7 @@ func NewRGBStyle(fg RGBColor, bg ...RGBColor) *RGBStyle { } // HEXStyle create a RGBStyle from HEX color string. +// // Usage: // s := HEXStyle("aabbcc", "eee") // s.Print("message") @@ -338,11 +314,11 @@ func HEXStyle(fg string, bg ...string) *RGBStyle { if len(fg) > 0 { s.SetFg(HEX(fg)) } - return s } // RGBStyleFromString create a RGBStyle from color value string. +// // Usage: // s := RGBStyleFromString("170,187,204", "70,87,4") // s.Print("message") diff --git a/vendor/github.com/gookit/color/color_tag.go b/vendor/github.com/gookit/color/color_tag.go index 051ba84f..4f6fed93 100644 --- a/vendor/github.com/gookit/color/color_tag.go +++ b/vendor/github.com/gookit/color/color_tag.go @@ -38,8 +38,11 @@ var ( * internal defined color tags *************************************************************/ -// There are internal defined color tags -// Usage: content text +// There are internal defined fg color tags +// +// Usage: +// content text +// // @notice 加 0 在前面是为了防止之前的影响到现在的设置 var colorTags = map[string]string{ // basic tags @@ -72,7 +75,9 @@ var colorTags = map[string]string{ "magenta": "0;35", "mga": "0;35", // short name "magentaB": "1;35", // with bold + "magenta1": "1;35", "mgb": "1;35", + "mga1": "1;35", "mgaB": "1;35", // light/hi tags @@ -90,7 +95,7 @@ var colorTags = map[string]string{ "light_magenta": "0;95", "hiMagenta": "0;95", "hi_magenta": "0;95", - "lightMagentaB": "1;95", // with bold + "lightMagenta1": "1;95", // with bold "hiMagentaB": "1;95", // with bold "hi_magenta_b": "1;95", "lightRed": "0;91", @@ -127,9 +132,14 @@ var colorTags = map[string]string{ // option "bold": "1", "b": "1", + "italic": "3", + "i": "3", // italic "underscore": "4", "us": "4", // short name for 'underscore' + "blink": "5", + "fb": "6", // fast blink "reverse": "7", + "st": "9", // strikethrough // alert tags, like bootstrap's alert "suc": "1;32", // same "green" and "bold" @@ -146,12 +156,141 @@ var colorTags = map[string]string{ "error": "97;41", // fg light white; bg red } +/************************************************************* + * internal defined tag attributes + *************************************************************/ + +// built-in attributes for fg,bg 16-colors and op codes. +var ( + attrFgs = map[string]string{ + // basic colors + + "black": FgBlack.Code(), + "red": "31", + "green": "32", + "brown": "33", // #A52A2A + "yellow": "33", + "ylw": "33", + "blue": "34", + "cyan": "36", + "magenta": "35", + "mga": "35", + "white": FgWhite.Code(), + "default": "39", // no color + "normal": "39", // no color + + // light/hi colors + + "darkGray": FgDarkGray.Code(), + "dark_gray": "90", + "gray": "90", + "lightYellow": "93", + "light_yellow": "93", + "hiYellow": "93", + "hi_yellow": "93", + "lightMagenta": "95", + "light_magenta": "95", + "hiMagenta": "95", + "hi_magenta": "95", + "hi_mga": "95", + "lightRed": "91", + "light_red": "91", + "hiRed": "91", + "hi_red": "91", + "lightGreen": "92", + "light_green": "92", + "hiGreen": "92", + "hi_green": "92", + "lightBlue": "94", + "light_blue": "94", + "hiBlue": "94", + "hi_blue": "94", + "lightCyan": "96", + "light_cyan": "96", + "hiCyan": "96", + "hi_cyan": "96", + "lightWhite": "97", + "light_white": "97", + } + + attrBgs = map[string]string{ + // basic colors + + "black": BgBlack.Code(), + "red": "41", + "green": "42", + "brown": "43", // #A52A2A + "yellow": "43", + "ylw": "43", + "blue": "44", + "cyan": "46", + "magenta": "45", + "mga": "45", + "white": FgWhite.Code(), + "default": "49", // no color + "normal": "49", // no color + + // light/hi colors + + "darkGray": BgDarkGray.Code(), + "dark_gray": "100", + "gray": "100", + "lightYellow": "103", + "light_yellow": "103", + "hiYellow": "103", + "hi_yellow": "103", + "lightMagenta": "105", + "light_magenta": "105", + "hiMagenta": "105", + "hi_magenta": "105", + "hi_mga": "105", + "lightRed": "101", + "light_red": "101", + "hiRed": "101", + "hi_red": "101", + "lightGreen": "102", + "light_green": "102", + "hiGreen": "102", + "hi_green": "102", + "lightBlue": "104", + "light_blue": "104", + "hiBlue": "104", + "hi_blue": "104", + "lightCyan": "106", + "light_cyan": "106", + "hiCyan": "106", + "hi_cyan": "106", + "lightWhite": BgLightWhite.Code(), + "light_white": "107", + } + + attrOpts = map[string]string{ + "reset": OpReset.Code(), + "bold": OpBold.Code(), + "b": OpBold.Code(), + "fuzzy": OpFuzzy.Code(), + "italic": OpItalic.Code(), + "i": OpItalic.Code(), + "underscore": OpUnderscore.Code(), + "us": OpUnderscore.Code(), + "u": OpUnderscore.Code(), + "blink": OpBlink.Code(), + "fastblink": OpFastBlink.Code(), + "fb": OpFastBlink.Code(), + "reverse": OpReverse.Code(), + "concealed": OpConcealed.Code(), + "strikethrough": OpStrikethrough.Code(), + "st": OpStrikethrough.Code(), + } +) + /************************************************************* * parse color tags *************************************************************/ var ( tagParser = TagParser{} + // regex for match color 256 code rxNumStr = regexp.MustCompile("^[0-9]{1,3}$") rxHexCode = regexp.MustCompile("^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$") ) @@ -182,11 +321,18 @@ func (tp *TagParser) ParseByEnv(str string) string { if !Enable || !SupportColor() { return ClearTag(str) } - return tp.Parse(str) } // Parse parse given string, replace color tag and return rendered string +// +// Use built in tags: +// CONTENT +// // e.g: `message` +// +// Custom tag attributes: +// `CONTENT` +// // e.g: `wel` func (tp *TagParser) Parse(str string) string { // not contains color tag if !strings.Contains(str, "") { @@ -198,14 +344,15 @@ func (tp *TagParser) Parse(str string) string { // item: 0 full text 1 tag name 2 tag content for _, item := range matched { - full, tag, content := item[0], item[1], item[2] + full, tag, body := item[0], item[1], item[2] - // use defined tag name: "content" -> tag: "info" + // use defined color tag name: "content" -> tag: "info" if !strings.ContainsRune(tag, '=') { - code := colorTags[tag] - if len(code) > 0 { - now := RenderString(code, content) - // old := WrapTag(content, tag) is equals to var 'full' + if code := colorTags[tag]; len(code) > 0 { + str = strings.Replace(str, full, RenderString(code, body), 1) + } else if code, ok := namedRgbMap[tag]; ok { + code = strings.Replace(code, ",", ";", -1) + now := RenderString(FgRGBPfx+code, body) str = strings.Replace(str, full, now, 1) } continue @@ -214,18 +361,13 @@ func (tp *TagParser) Parse(str string) string { // custom color in tag // - basic: "fg=white;bg=blue;op=bold" if code := ParseCodeFromAttr(tag); len(code) > 0 { - now := RenderString(code, content) - str = strings.Replace(str, full, now, 1) + str = strings.Replace(str, full, RenderString(code, body), 1) } } return str } -// func (tp *TagParser) ParseAttr(attr string) (code string) { -// return -// } - // ReplaceTag parse string, replace color tag and return rendered string func ReplaceTag(str string) string { return tagParser.ParseByEnv(str) @@ -236,17 +378,20 @@ func ReplaceTag(str string) string { // attr format: // // VALUE please see var: FgColors, BgColors, AllOptions // "fg=VALUE;bg=VALUE;op=VALUE" +// // 16 color: // "fg=yellow" // "bg=red" -// "op=bold,underscore" option is allow multi value +// "op=bold,underscore" // option is allow multi value // "fg=white;bg=blue;op=bold" // "fg=white;op=bold,underscore" +// // 256 color: // "fg=167" // "fg=167;bg=23" // "fg=167;bg=23;op=bold" -// true color: +// +// True color: // // hex // "fg=fc1cac" // "fg=fc1cac;bg=c2c3c4" @@ -270,18 +415,14 @@ func ParseCodeFromAttr(attr string) (code string) { pos, val := item[1], item[2] switch pos { case "fg": - if c, ok := FgColors[val]; ok { // basic - codes = append(codes, c.String()) - } else if c, ok := ExFgColors[val]; ok { // extra - codes = append(codes, c.String()) + if code, ok := attrFgs[val]; ok { // attr fg + codes = append(codes, code) } else if code := rgbHex256toCode(val, false); code != "" { codes = append(codes, code) } case "bg": - if c, ok := BgColors[val]; ok { // basic bg - codes = append(codes, c.String()) - } else if c, ok := ExBgColors[val]; ok { // extra bg - codes = append(codes, c.String()) + if code, ok := attrBgs[val]; ok { // attr bg + codes = append(codes, code) } else if code := rgbHex256toCode(val, true); code != "" { codes = append(codes, code) } @@ -289,12 +430,12 @@ func ParseCodeFromAttr(attr string) (code string) { if strings.Contains(val, ",") { ns := strings.Split(val, ",") for _, n := range ns { - if c, ok := AllOptions[n]; ok { - codes = append(codes, c.String()) + if code, ok := attrOpts[n]; ok { // attr ops + codes = append(codes, code) } } - } else if c, ok := AllOptions[val]; ok { - codes = append(codes, c.String()) + } else if code, ok := attrOpts[val]; ok { + codes = append(codes, code) } } } @@ -327,7 +468,6 @@ func ClearTag(s string) string { if !strings.Contains(s, "") { return s } - return stripRegex.ReplaceAllString(s, "") } @@ -350,7 +490,6 @@ func WrapTag(s string, tag string) string { if s == "" || tag == "" { return s } - return fmt.Sprintf("<%s>%s", tag, s) } @@ -410,12 +549,7 @@ func (tg Tag) Println(a ...interface{}) { // Sprint render messages func (tg Tag) Sprint(a ...interface{}) string { - name := string(tg) - // if stl := GetStyle(name); !stl.IsEmpty() { - // return stl.Render(args...) - // } - - return RenderCode(GetTagCode(name), a...) + return RenderCode(GetTagCode(string(tg)), a...) } // Sprintf format and render messages diff --git a/vendor/github.com/gookit/color/convert.go b/vendor/github.com/gookit/color/convert.go index 6e3326df..39aac7d2 100644 --- a/vendor/github.com/gookit/color/convert.go +++ b/vendor/github.com/gookit/color/convert.go @@ -8,6 +8,27 @@ import ( "strings" ) +// values from https://github.com/go-terminfo/terminfo +// var ( +// RgbaBlack = image_color.RGBA{0, 0, 0, 255} +// Red = color.RGBA{205, 0, 0, 255} +// Green = color.RGBA{0, 205, 0, 255} +// Orange = color.RGBA{205, 205, 0, 255} +// Blue = color.RGBA{0, 0, 238, 255} +// Magenta = color.RGBA{205, 0, 205, 255} +// Cyan = color.RGBA{0, 205, 205, 255} +// LightGrey = color.RGBA{229, 229, 229, 255} +// +// DarkGrey = color.RGBA{127, 127, 127, 255} +// LightRed = color.RGBA{255, 0, 0, 255} +// LightGreen = color.RGBA{0, 255, 0, 255} +// Yellow = color.RGBA{255, 255, 0, 255} +// LightBlue = color.RGBA{92, 92, 255, 255} +// LightMagenta = color.RGBA{255, 0, 255, 255} +// LightCyan = color.RGBA{0, 255, 255, 255} +// White = color.RGBA{255, 255, 255, 255} +// ) + var ( // ---------- basic(16) <=> 256 color convert ---------- basicTo256Map = map[uint8]uint8{ @@ -32,14 +53,14 @@ var ( // ---------- basic(16) <=> RGB color convert ---------- // refer from Hyper app basic2hexMap = map[uint8]string{ - 30: "000000", // black - 31: "c51e14", // red - 32: "1dc121", // green - 33: "c7c329", // yellow - 34: "0a2fc4", // blue - 35: "c839c5", // magenta - 36: "20c5c6", // cyan - 37: "c7c7c7", // white + 30: "000000", // black + 31: "c51e14", // red + 32: "1dc121", // green + 33: "c7c329", // yellow + 34: "0a2fc4", // blue + 35: "c839c5", // magenta + 36: "20c5c6", // cyan + 37: "c7c7c7", // white // - don't add bg color // 40: "000000", // black // 41: "c51e14", // red @@ -49,14 +70,14 @@ var ( // 45: "c839c5", // magenta // 46: "20c5c6", // cyan // 47: "c7c7c7", // white - 90: "686868", // lightBlack/darkGray - 91: "fd6f6b", // lightRed - 92: "67f86f", // lightGreen - 93: "fffa72", // lightYellow - 94: "6a76fb", // lightBlue - 95: "fd7cfc", // lightMagenta - 96: "68fdfe", // lightCyan - 97: "ffffff", // lightWhite + 90: "686868", // lightBlack/darkGray + 91: "fd6f6b", // lightRed + 92: "67f86f", // lightGreen + 93: "fffa72", // lightYellow + 94: "6a76fb", // lightBlue + 95: "fd7cfc", // lightMagenta + 96: "68fdfe", // lightCyan + 97: "ffffff", // lightWhite // - don't add bg color // 100: "686868", // lightBlack/darkGray // 101: "fd6f6b", // lightRed @@ -497,7 +518,7 @@ func Rgb2basic(r, g, b uint8, isBg bool) uint8 { return RgbToAnsi(r, g, b, isBg) } -// Rgb2ansi alias of the RgbToAnsi() +// Rgb2ansi convert RGB-code to 16-code, alias of the RgbToAnsi() func Rgb2ansi(r, g, b uint8, isBg bool) uint8 { return RgbToAnsi(r, g, b, isBg) } @@ -718,7 +739,9 @@ func RgbToHslInt(r, g, b uint8) []int { } // RgbToHsl Converts an RGB color value to HSL. Conversion formula +// // adapted from http://en.wikipedia.org/wiki/HSL_color_space. +// // Assumes r, g, and b are contained in the set [0, 255] and // returns h, s, and l in the set [0, 1]. func RgbToHsl(r, g, b uint8) []float64 { @@ -733,11 +756,9 @@ func RgbToHsl(r, g, b uint8) []float64 { min, max := ps[0], ps[2] // max := math.Max(math.Max(pr, pg), pb) // min := math.Min(math.Min(pr, pg), pb) - mid := (max + min) / 2 h, s, l := mid, mid, mid - if max == min { h, s = 0, 0 // achromatic } else { diff --git a/vendor/github.com/gookit/color/detect_env.go b/vendor/github.com/gookit/color/detect_env.go index 3b2300db..7fd8d81e 100644 --- a/vendor/github.com/gookit/color/detect_env.go +++ b/vendor/github.com/gookit/color/detect_env.go @@ -2,7 +2,6 @@ package color import ( "io" - "io/ioutil" "os" "runtime" "strconv" @@ -12,6 +11,17 @@ import ( "github.com/xo/terminfo" ) +// Level is the color level supported by a terminal. +type Level = terminfo.ColorLevel + +// terminal color available level alias of the terminfo.ColorLevel* +const ( + LevelNo = terminfo.ColorLevelNone // not support color. + Level16 = terminfo.ColorLevelBasic // basic - 3/4 bit color supported + Level256 = terminfo.ColorLevelHundreds // hundreds - 8-bit color supported + LevelRgb = terminfo.ColorLevelMillions // millions - (24 bit)true color supported +) + /************************************************************* * helper methods for detect color supports *************************************************************/ @@ -19,8 +29,8 @@ import ( // DetectColorLevel for current env // // NOTICE: The method will detect terminal info each times, -// if only want get current color level, please direct call SupportColor() or TermColorLevel() -func DetectColorLevel() terminfo.ColorLevel { +// if only want to get current color level, please direct call SupportColor() or TermColorLevel() +func DetectColorLevel() Level { level, _ := detectTermColorLevel() return level } @@ -28,7 +38,7 @@ func DetectColorLevel() terminfo.ColorLevel { // detect terminal color support level // // refer https://github.com/Delta456/box-cli-maker -func detectTermColorLevel() (level terminfo.ColorLevel, needVTP bool) { +func detectTermColorLevel() (level Level, needVTP bool) { // on windows WSL: // - runtime.GOOS == "Linux" // - support true-color @@ -76,7 +86,7 @@ func detectTermColorLevel() (level terminfo.ColorLevel, needVTP bool) { // // refer the terminfo.ColorLevelFromEnv() // https://en.wikipedia.org/wiki/Terminfo -func detectColorLevelFromEnv(termVal string, isWin bool) terminfo.ColorLevel { +func detectColorLevelFromEnv(termVal string, isWin bool) Level { // check for overriding environment variables colorTerm, termProg, forceColor := os.Getenv("COLORTERM"), os.Getenv("TERM_PROGRAM"), os.Getenv("FORCE_COLOR") switch { @@ -172,6 +182,7 @@ func detectWSL() bool { return false } +/* // refer // https://github.com/Delta456/box-cli-maker/blob/7b5a1ad8a016ce181e7d8b05e24b54ff60b4b38a/detect_unix.go#L27-L45 // detect WSL as it has True Color support @@ -198,10 +209,11 @@ func isWSL() bool { } // it gives "Microsoft" for WSL and "microsoft" for WSL 2 - // it support True-color + // it supports True-color content := strings.ToLower(string(wsl)) return strings.Contains(content, "microsoft") } +*/ /************************************************************* * helper methods for check env @@ -228,11 +240,7 @@ func IsConsole(w io.Writer) bool { // IsMSys msys(MINGW64) environment, does not necessarily support color func IsMSys() bool { // like "MSYSTEM=MINGW64" - if len(os.Getenv("MSYSTEM")) > 0 { - return true - } - - return false + return len(os.Getenv("MSYSTEM")) > 0 } // IsSupportColor check current console is support color. @@ -243,7 +251,7 @@ func IsSupportColor() bool { return IsSupport16Color() } -// IsSupportColor check current console is support color. +// IsSupport16Color check current console is support color. // // NOTICE: The method will detect terminal info each times, // if only want get current color level, please direct call SupportColor() or TermColorLevel() @@ -255,7 +263,7 @@ func IsSupport16Color() bool { // IsSupport256Color render check // // NOTICE: The method will detect terminal info each times, -// if only want get current color level, please direct call SupportColor() or TermColorLevel() +// if only want to get current color level, please direct call SupportColor() or TermColorLevel() func IsSupport256Color() bool { level, _ := detectTermColorLevel() return level > terminfo.ColorLevelBasic diff --git a/vendor/github.com/gookit/color/detect_nonwin.go b/vendor/github.com/gookit/color/detect_nonwin.go index 75c7202f..26f10908 100644 --- a/vendor/github.com/gookit/color/detect_nonwin.go +++ b/vendor/github.com/gookit/color/detect_nonwin.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows // The method in the file has no effect @@ -13,7 +14,7 @@ import ( ) // detect special term color support -func detectSpecialTermColor(termVal string) (terminfo.ColorLevel, bool) { +func detectSpecialTermColor(termVal string) (Level, bool) { if termVal == "" { return terminfo.ColorLevelNone, false } diff --git a/vendor/github.com/gookit/color/detect_windows.go b/vendor/github.com/gookit/color/detect_windows.go index 7707d9ca..df538b21 100644 --- a/vendor/github.com/gookit/color/detect_windows.go +++ b/vendor/github.com/gookit/color/detect_windows.go @@ -1,10 +1,13 @@ +//go:build windows // +build windows -// Display color on windows +// Display color on Windows +// // refer: -// golang.org/x/sys/windows -// golang.org/x/crypto/ssh/terminal -// https://docs.microsoft.com/en-us/windows/console +// +// golang.org/x/sys/windows +// golang.org/x/crypto/ssh/terminal +// https://docs.microsoft.com/en-us/windows/console package color import ( @@ -38,7 +41,7 @@ func init() { return } - // if at windows's ConEmu, Cmder, putty ... terminals not need VTP + // if at Windows's ConEmu, Cmder, putty ... terminals not need VTP // -------- try force enable colors on windows terminal ------- tryEnableVTP(needVTP) @@ -47,7 +50,7 @@ func init() { // err := getConsoleScreenBufferInfo(uintptr(syscall.Stdout), &defScreenInfo) } -// try force enable colors on windows terminal +// try force enable colors on Windows terminal func tryEnableVTP(enable bool) bool { if !enable { return false @@ -57,7 +60,7 @@ func tryEnableVTP(enable bool) bool { initKernel32Proc() - // enable colors on windows terminal + // enable colors on Windows terminal if tryEnableOnCONOUT() { return true } @@ -70,7 +73,7 @@ func initKernel32Proc() { return } - // load related windows dll + // load related Windows dll // https://docs.microsoft.com/en-us/windows/console/setconsolemode kernel32 = syscall.NewLazyDLL("kernel32.dll") @@ -111,10 +114,12 @@ var ( ) // refer -// https://github.com/Delta456/box-cli-maker/blob/7b5a1ad8a016ce181e7d8b05e24b54ff60b4b38a/detect_windows.go#L30-L57 -// https://github.com/gookit/color/issues/25#issuecomment-738727917 -// detects the Color Level Supported on windows: cmd, powerShell -func detectSpecialTermColor(termVal string) (tl terminfo.ColorLevel, needVTP bool) { +// +// https://github.com/Delta456/box-cli-maker/blob/7b5a1ad8a016ce181e7d8b05e24b54ff60b4b38a/detect_windows.go#L30-L57 +// https://github.com/gookit/color/issues/25#issuecomment-738727917 +// +// detects the color level supported on Windows: cmd, powerShell +func detectSpecialTermColor(termVal string) (tl Level, needVTP bool) { if os.Getenv("ConEmuANSI") == "ON" { debugf("support True Color by ConEmuANSI=ON") // ConEmuANSI is "ON" for generic ANSI support @@ -130,7 +135,7 @@ func detectSpecialTermColor(termVal string) (tl terminfo.ColorLevel, needVTP boo // Detect if using ANSICON on older systems if os.Getenv("ANSICON") != "" { conVersion := os.Getenv("ANSICON_VER") - // 8 bit Colors were only supported after v1.81 release + // 8-bit Colors were only supported after v1.81 release if conVersion >= "181" { return terminfo.ColorLevelHundreds, false } @@ -140,7 +145,7 @@ func detectSpecialTermColor(termVal string) (tl terminfo.ColorLevel, needVTP boo return terminfo.ColorLevelNone, false } - // True Color is not available before build 14931 so fallback to 8 bit color. + // True Color is not available before build 14931 so fallback to 8-bit color. if buildNumber < 14931 { return terminfo.ColorLevelHundreds, true } @@ -151,7 +156,7 @@ func detectSpecialTermColor(termVal string) (tl terminfo.ColorLevel, needVTP boo } /************************************************************* - * render full color code on windows(8,16,24bit color) + * render full color code on Windows(8,16,24bit color) *************************************************************/ // docs https://docs.microsoft.com/zh-cn/windows/console/getconsolemode#parameters @@ -166,9 +171,10 @@ const ( // doc https://docs.microsoft.com/zh-cn/windows/console/console-virtual-terminal-sequences#samples // // Usage: -// err := EnableVirtualTerminalProcessing(syscall.Stdout, true) -// // support print color text -// err = EnableVirtualTerminalProcessing(syscall.Stdout, false) +// +// err := EnableVirtualTerminalProcessing(syscall.Stdout, true) +// // support print color text +// err = EnableVirtualTerminalProcessing(syscall.Stdout, false) func EnableVirtualTerminalProcessing(stream syscall.Handle, enable bool) error { var mode uint32 // Check if it is currently in the terminal @@ -216,7 +222,7 @@ func EnableVirtualTerminalProcessing(stream syscall.Handle, enable bool) error { // } /************************************************************* - * render simple color code on windows + * render simple color code on Windows *************************************************************/ // IsTty returns true if the given file descriptor is a terminal. @@ -231,9 +237,10 @@ func IsTty(fd uintptr) bool { // IsTerminal returns true if the given file descriptor is a terminal. // // Usage: -// fd := os.Stdout.Fd() -// fd := uintptr(syscall.Stdout) // for windows -// IsTerminal(fd) +// +// fd := os.Stdout.Fd() +// fd := uintptr(syscall.Stdout) // for Windows +// IsTerminal(fd) func IsTerminal(fd uintptr) bool { initKernel32Proc() diff --git a/vendor/github.com/gookit/color/index.html b/vendor/github.com/gookit/color/index.html new file mode 100644 index 00000000..19e8e9a5 --- /dev/null +++ b/vendor/github.com/gookit/color/index.html @@ -0,0 +1,25 @@ + + + + + + + + Color - A command-line color library with true color support, universal API methods and Windows support. + + +
+ + + + diff --git a/vendor/github.com/gookit/color/quickstart.go b/vendor/github.com/gookit/color/quickstart.go index 3cc3b77f..4dbd1a43 100644 --- a/vendor/github.com/gookit/color/quickstart.go +++ b/vendor/github.com/gookit/color/quickstart.go @@ -5,105 +5,104 @@ package color *************************************************************/ // Redp print message with Red color -func Redp(a ...interface{}) { - Red.Print(a...) -} +func Redp(a ...interface{}) { Red.Print(a...) } + +// Redf print message with Red color +func Redf(format string, a ...interface{}) { Red.Printf(format, a...) } // Redln print message line with Red color -func Redln(a ...interface{}) { - Red.Println(a...) -} +func Redln(a ...interface{}) { Red.Println(a...) } // Bluep print message with Blue color -func Bluep(a ...interface{}) { - Blue.Print(a...) -} +func Bluep(a ...interface{}) { Blue.Print(a...) } + +// Bluef print message with Blue color +func Bluef(format string, a ...interface{}) { Blue.Printf(format, a...) } // Blueln print message line with Blue color -func Blueln(a ...interface{}) { - Blue.Println(a...) -} +func Blueln(a ...interface{}) { Blue.Println(a...) } // Cyanp print message with Cyan color -func Cyanp(a ...interface{}) { - Cyan.Print(a...) -} +func Cyanp(a ...interface{}) { Cyan.Print(a...) } + +// Cyanf print message with Cyan color +func Cyanf(format string, a ...interface{}) { Cyan.Printf(format, a...) } // Cyanln print message line with Cyan color -func Cyanln(a ...interface{}) { - Cyan.Println(a...) -} +func Cyanln(a ...interface{}) { Cyan.Println(a...) } // Grayp print message with Gray color -func Grayp(a ...interface{}) { - Gray.Print(a...) -} +func Grayp(a ...interface{}) { Gray.Print(a...) } + +// Grayf print message with Gray color +func Grayf(format string, a ...interface{}) { Gray.Printf(format, a...) } // Grayln print message line with Gray color -func Grayln(a ...interface{}) { - Gray.Println(a...) -} +func Grayln(a ...interface{}) { Gray.Println(a...) } // Greenp print message with Green color -func Greenp(a ...interface{}) { - Green.Print(a...) -} +func Greenp(a ...interface{}) { Green.Print(a...) } + +// Greenf print message with Green color +func Greenf(format string, a ...interface{}) { Green.Printf(format, a...) } // Greenln print message line with Green color -func Greenln(a ...interface{}) { - Green.Println(a...) -} +func Greenln(a ...interface{}) { Green.Println(a...) } // Yellowp print message with Yellow color -func Yellowp(a ...interface{}) { - Yellow.Print(a...) -} +func Yellowp(a ...interface{}) { Yellow.Print(a...) } + +// Yellowf print message with Yellow color +func Yellowf(format string, a ...interface{}) { Yellow.Printf(format, a...) } // Yellowln print message line with Yellow color -func Yellowln(a ...interface{}) { - Yellow.Println(a...) -} +func Yellowln(a ...interface{}) { Yellow.Println(a...) } // Magentap print message with Magenta color -func Magentap(a ...interface{}) { - Magenta.Print(a...) -} +func Magentap(a ...interface{}) { Magenta.Print(a...) } + +// Magentaf print message with Magenta color +func Magentaf(format string, a ...interface{}) { Magenta.Printf(format, a...) } // Magentaln print message line with Magenta color -func Magentaln(a ...interface{}) { - Magenta.Println(a...) -} +func Magentaln(a ...interface{}) { Magenta.Println(a...) } /************************************************************* * quick use style print message *************************************************************/ +// Infop print message with Info color +func Infop(a ...interface{}) { Info.Print(a...) } + // Infof print message with Info style -func Infof(format string, a ...interface{}) { - Info.Printf(format, a...) -} +func Infof(format string, a ...interface{}) { Info.Printf(format, a...) } // Infoln print message with Info style -func Infoln(a ...interface{}) { - Info.Println(a...) -} +func Infoln(a ...interface{}) { Info.Println(a...) } + +// Successp print message with success color +func Successp(a ...interface{}) { Success.Print(a...) } + +// Successf print message with success style +func Successf(format string, a ...interface{}) { Success.Printf(format, a...) } + +// Successln print message with success style +func Successln(a ...interface{}) { Success.Println(a...) } + +// Errorp print message with Error color +func Errorp(a ...interface{}) { Error.Print(a...) } // Errorf print message with Error style -func Errorf(format string, a ...interface{}) { - Error.Printf(format, a...) -} +func Errorf(format string, a ...interface{}) { Error.Printf(format, a...) } // Errorln print message with Error style -func Errorln(a ...interface{}) { - Error.Println(a...) -} +func Errorln(a ...interface{}) { Error.Println(a...) } + +// Warnp print message with Warn color +func Warnp(a ...interface{}) { Warn.Print(a...) } // Warnf print message with Warn style -func Warnf(format string, a ...interface{}) { - Warn.Printf(format, a...) -} +func Warnf(format string, a ...interface{}) { Warn.Printf(format, a...) } // Warnln print message with Warn style -func Warnln(a ...interface{}) { - Warn.Println(a...) -} +func Warnln(a ...interface{}) { Warn.Println(a...) } diff --git a/vendor/github.com/gookit/color/utils.go b/vendor/github.com/gookit/color/utils.go index bfd13539..4554b27e 100644 --- a/vendor/github.com/gookit/color/utils.go +++ b/vendor/github.com/gookit/color/utils.go @@ -47,6 +47,7 @@ func Println(a ...interface{}) { } // Fprint print rendered messages to writer +// // Notice: will ignore print error func Fprint(w io.Writer, a ...interface{}) { _, err := fmt.Fprint(w, Render(a...)) @@ -84,14 +85,15 @@ func Lprint(l *log.Logger, a ...interface{}) { } // Render parse color tags, return rendered string. +// // Usage: +// // text := Render("hello world!") // fmt.Println(text) func Render(a ...interface{}) string { if len(a) == 0 { return "" } - return ReplaceTag(fmt.Sprint(a...)) } @@ -184,6 +186,11 @@ func debugf(f string, v ...interface{}) { } } +// equals: return ok ? val1 : val2 +func isValidUint8(val int) bool { + return val >= 0 && val < 256 +} + // equals: return ok ? val1 : val2 func compareVal(ok bool, val1, val2 uint8) uint8 { if ok { diff --git a/vendor/github.com/gookit/goutil/.gitignore b/vendor/github.com/gookit/goutil/.gitignore index 1bbab25d..596754e8 100644 --- a/vendor/github.com/gookit/goutil/.gitignore +++ b/vendor/github.com/gookit/goutil/.gitignore @@ -1,6 +1,7 @@ *.log *.swp .idea +.vscode/ *.patch ### Go template # Binaries for programs and plugins @@ -17,3 +18,5 @@ *.out *.cov .DS_Store + +testdata/ diff --git a/vendor/github.com/gookit/goutil/Makefile b/vendor/github.com/gookit/goutil/Makefile index 6d04a25c..f902a5f1 100644 --- a/vendor/github.com/gookit/goutil/Makefile +++ b/vendor/github.com/gookit/goutil/Makefile @@ -3,7 +3,7 @@ .DEFAULT_GOAL := help # 每行命令之前必须有一个tab键。如果想用其他键,可以用内置变量.RECIPEPREFIX 声明 # mac 下这条声明 没起作用 !! -.RECIPEPREFIX = > +#.RECIPEPREFIX = > .PHONY: all usage help clean # 需要注意的是,每行命令在一个单独的shell中执行。这些Shell之间没有继承关系。 @@ -26,6 +26,11 @@ readme: go run ./internal/gendoc -o README.md go run ./internal/gendoc -o README.zh-CN.md -l zh-CN + readme-c: ## Generate or update README file and commit change to git +readme-c: readme + git add README.* internal + git commit -m "doc: update and re-generate README docs" + csfix: ## Fix code style for all files by go fmt csfix: go fmt ./... diff --git a/vendor/github.com/gookit/goutil/README.md b/vendor/github.com/gookit/goutil/README.md index 71ef04f1..565f799e 100644 --- a/vendor/github.com/gookit/goutil/README.md +++ b/vendor/github.com/gookit/goutil/README.md @@ -2,36 +2,83 @@ ![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/gookit/goutil?style=flat-square) [![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/gookit/goutil)](https://github.com/gookit/goutil) -[![GoDoc](https://godoc.org/github.com/gookit/goutil?status.svg)](https://pkg.go.dev/github.com/gookit/goutil) [![Go Report Card](https://goreportcard.com/badge/github.com/gookit/goutil)](https://goreportcard.com/report/github.com/gookit/goutil) [![Unit-Tests](https://github.com/gookit/goutil/workflows/Unit-Tests/badge.svg)](https://github.com/gookit/goutil/actions) [![Coverage Status](https://coveralls.io/repos/github/gookit/goutil/badge.svg?branch=master)](https://coveralls.io/github/gookit/goutil?branch=master) +[![Go Reference](https://pkg.go.dev/badge/github.com/gookit/goutil.svg)](https://pkg.go.dev/github.com/gookit/goutil) -💪 Useful utils for the Go: int, string, array/slice, map, error, time, format, CLI, ENV, filesystem, system, testing and more. +💪 Useful utils package for the Go: int, string, array/slice, map, error, time, format, CLI, ENV, filesystem, system, testing and more. -- [`arrutil`](./arrutil): Array/Slice util functions. eg: check, convert -- [`dump`](./dump): Simple variable printing tool, printing slice, map will automatically wrap each element and display the call location -- `cliutil` Command-line util functions. eg: read input, exec command, cmdline parse/build -- [`errorx`](./errorx) Provide an enhanced error implements for go, allow with stacktraces and wrap another error. -- `envutil` ENV util for current runtime env information. eg: get one, get info, parse var -- `fmtutil` Format data util functions +> **[中文说明](README.zh-CN.md)** + +**Basic packages:** + +- [`arrutil`](./arrutil): Array/Slice util functions. eg: check, convert, formatting, enum, collections +- [`cliutil`](./cliutil) Command-line util functions. eg: colored print, read input, exec command +- [`envutil`](./envutil) ENV util for current runtime env information. eg: get one, get info, parse var +- [`fmtutil`](./fmtutil) Format data util functions. eg: data, size, time - [`fsutil`](./fsutil) Filesystem util functions, quick create, read and write file. eg: file and dir check, operate -- `jsonutil` some util functions for quick read, write, encode, decode JSON data. +- [`jsonutil`](./jsonutil) some util functions for quick read, write, encode, decode JSON data. - [`maputil`](./maputil) Map data util functions. eg: convert, sub-value get, simple merge -- `mathutil`, `numutil` Math(int, number) util functions. eg: convert, math calc, random +- [`mathutil`](./mathutil) Math(int, number) util functions. eg: convert, math calc, random - `netutil` Network util functions - - `netutil/httpreq` An easier-to-use HTTP client that wraps http.Client -- `strutil` String util functions. eg: bytes, check, convert, encode, format and more -- `sysutil` System util functions. eg: sysenv, exec, user, process -- `testutil` Test help util functions. eg: http test, mock ENV value +- [`reflects`](./reflects) Provide extends reflect util functions. +- [`stdutil`](./stdutil) Provide some commonly std util functions. +- [`structs`](./structs) Provide some extends util functions for struct. eg: tag parse, struct data init +- [`strutil`](./strutil) String util functions. eg: bytes, check, convert, encode, format and more +- [`sysutil`](./sysutil) System util functions. eg: sysenv, exec, user, process + - [process](./sysutil/process) Provide some process handle util functions. + +**Advance packages:** + +- [`cflag`](./cflag): Wraps and extends go `flag.FlagSet` to build simple command line applications +- cli util: + - [cmdline](./cliutil/cmdline) Provide cmdline parse, args build to cmdline +- [`dump`](./dump): GO value printing tool. print slice, map will auto wrap each element and display the call location +- [`errorx`](./errorx) Provide an enhanced error implements for go, allow with stacktrace and wrap another error. +- net util: + - [httpreq](netutil/httpreq) An easier-to-use HTTP client that wraps http.Client +- string util: + - [textscan](strutil/textscan) Implemented a parser that quickly scans and analyzes text content. It can be used to parse INI, Properties and other formats +- sys util: + - [clipboard](./sysutil/clipboard) Provide a simple clipboard read and write operations. + - [cmdr](./sysutil/cmdr) Provide for quick build and run a cmd, batch run multi cmd tasks +- [`testutil`](./testutil) Test help util functions. eg: http test, mock ENV value + - [assert](./testutil/assert) Asserts functions for help testing - [`timex`](./timex) Provides an enhanced time.Time implementation. Add more commonly used functional methods - such as: DayStart(), DayAfter(), DayAgo(), DateFormat() and more. -> **[中文说明](README.zh-CN.md)** +## Go Doc -## GoDoc +Please see [Go doc](https://pkg.go.dev/github.com/gookit/goutil) + +## Install + +```shell +go get github.com/gookit/goutil +``` -- [Godoc for github](https://pkg.go.dev/github.com/gookit/goutil) +## Usage + +```go +// github.com/gookit/goutil +is.True(goutil.IsEmpty(nil)) +is.False(goutil.IsEmpty("abc")) + +is.True(goutil.IsEqual("a", "a")) +is.True(goutil.IsEqual([]string{"a"}, []string{"a"})) +is.True(goutil.IsEqual(23, 23)) + +is.True(goutil.Contains("abc", "a")) +is.True(goutil.Contains([]string{"abc", "def"}, "abc")) +is.True(goutil.Contains(map[int]string{2: "abc", 4: "def"}, 4)) + +// convert type +str = goutil.String(23) // "23" +iVal = goutil.Int("-2") // 2 +i64Val = goutil.Int64("-2") // -2 +u64Val = goutil.Uint("2") // 2 +``` ## Packages @@ -43,31 +90,46 @@ // source at arrutil/arrutil.go func Reverse(ss []string) func StringsRemove(ss []string, s string) []string -func TrimStrings(ss []string, cutSet ...string) (ns []string) -func GetRandomOne(arr interface{}) interface{} +func StringsFilter(ss []string, filter ...func(s string) bool) []string +func StringsMap(ss []string, mapFn func(s string) string) []string +func TrimStrings(ss []string, cutSet ...string) []string // source at arrutil/check.go func IntsHas(ints []int, val int) bool func Int64sHas(ints []int64, val int64) bool func InStrings(elem string, ss []string) bool { return StringsHas(ss, elem) } func StringsHas(ss []string, val string) bool -func HasValue(arr, val interface{}) bool -func Contains(arr, val interface{}) bool -func NotContains(arr, val interface{}) bool +func HasValue(arr, val any) bool +func Contains(arr, val any) bool +func NotContains(arr, val any) bool +// source at arrutil/collection.go +func TwowaySearch(data any, item any, fn Comparer) (int, error) +func MakeEmptySlice(itemType reflect.Type) any +func CloneSlice(data any) any +func Excepts(first, second any, fn Comparer) any +func Intersects(first any, second any, fn Comparer) any +func Union(first, second any, fn Comparer) any +func Find(source any, fn Predicate) (any, error) +func FindOrDefault(source any, fn Predicate, defaultValue any) any +func TakeWhile(data any, fn Predicate) any +func ExceptWhile(data any, fn Predicate) any // source at arrutil/convert.go func JoinStrings(sep string, ss ...string) string func StringsJoin(sep string, ss ...string) string func StringsToInts(ss []string) (ints []int, err error) -func MustToStrings(arr interface{}) []string -func StringsToSlice(ss []string) []interface{} -func ToInt64s(arr interface{}) (ret []int64, err error) -func MustToInt64s(arr interface{}) []int64 -func SliceToInt64s(arr []interface{}) []int64 -func ToStrings(arr interface{}) (ret []string, err error) -func SliceToStrings(arr []interface{}) []string -func AnyToString(arr interface{}) string -func SliceToString(arr ...interface{}) string { return ToString(arr) } -func ToString(arr []interface{}) string -func JoinSlice(sep string, arr ...interface{}) string +func MustToStrings(arr any) []string +func StringsToSlice(ss []string) []any +func ToInt64s(arr any) (ret []int64, err error) +func MustToInt64s(arr any) []int64 +func SliceToInt64s(arr []any) []int64 +func ToStrings(arr any) (ret []string, err error) +func SliceToStrings(arr []any) []string +func AnyToString(arr any) string +func SliceToString(arr ...any) string { return ToString(arr) } +func ToString(arr []any) string +func JoinSlice(sep string, arr ...any) string +// source at arrutil/format.go +func NewFormatter(arr any) *ArrFormatter +func FormatIndent(arr any, indent string) string ``` #### ArrUtil Usage @@ -90,6 +152,39 @@ ints, err := arrutil.ToInt64s([]string{"1", "2"}) // ints: []int64{1, 2} ss, err := arrutil.ToStrings([]int{1, 2}) // ss: []string{"1", "2"} ``` + +### Cflag + +> Package `github.com/gookit/goutil/cflag` + +```go +// source at cflag/app.go +func NewApp(fns ...func(app *App)) *App +func NewCmd(name, desc string) *Cmd +// source at cflag/cflag.go +func SetDebug(open bool) +func New(fns ...func(c *CFlags)) *CFlags +func NewEmpty(fns ...func(c *CFlags)) *CFlags +func WithDesc(desc string) func(c *CFlags) +func WithVersion(version string) func(c *CFlags) +// source at cflag/optarg.go +func NewArg(name, desc string, required bool) *FlagArg +// source at cflag/util.go +func IsZeroValue(opt *flag.Flag, value string) (bool, bool) +func AddPrefix(name string) string +func AddPrefixes(name string, shorts []string) string +func AddPrefixes2(name string, shorts []string, nameAtEnd bool) string +func SplitShortcut(shortcut string) []string +func FilterNames(names []string) []string +func IsFlagHelpErr(err error) bool +func WrapColorForCode(s string) string +func ReplaceShorts(args []string, shortsMap map[string]string) []string +``` +#### `cflag` Usage + +`cflag` usage please see [cflag/README.md](cflag/README.md) + + ### CLI/Console > Package `github.com/gookit/goutil/cliutil` @@ -108,17 +203,59 @@ func ExecCommand(binName string, args []string, workDir ...string) (string, erro func ShellExec(cmdLine string, shells ...string) (string, error) func CurrentShell(onlyName bool) (path string) func HasShellEnv(shell string) bool +func BuildOptionHelpName(names []string) string +func ShellQuote(s string) string +func OutputLines(output string) []string +func FirstLine(output string) string +// source at cliutil/color_print.go +func Redp(a ...any) { color.Red.Print(a...) } +func Redf(format string, a ...any) { color.Red.Printf(format, a...) } +func Redln(a ...any) { color.Red.Println(a...) } +func Bluep(a ...any) { color.Blue.Print(a...) } +func Bluef(format string, a ...any) { color.Blue.Printf(format, a...) } +func Blueln(a ...any) { color.Blue.Println(a...) } +func Cyanp(a ...any) { color.Cyan.Print(a...) } +func Cyanf(format string, a ...any) { color.Cyan.Printf(format, a...) } +func Cyanln(a ...any) { color.Cyan.Println(a...) } +func Grayp(a ...any) { color.Gray.Print(a...) } +func Grayf(format string, a ...any) { color.Gray.Printf(format, a...) } +func Grayln(a ...any) { color.Gray.Println(a...) } +func Greenp(a ...any) { color.Green.Print(a...) } +func Greenf(format string, a ...any) { color.Green.Printf(format, a...) } +func Greenln(a ...any) { color.Green.Println(a...) } +func Yellowp(a ...any) { color.Yellow.Print(a...) } +func Yellowf(format string, a ...any) { color.Yellow.Printf(format, a...) } +func Yellowln(a ...any) { color.Yellow.Println(a...) } +func Magentap(a ...any) { color.Magenta.Print(a...) } +func Magentaf(format string, a ...any) { color.Magenta.Printf(format, a...) } +func Magentaln(a ...any) { color.Magenta.Println(a...) } +func Infop(a ...any) { color.Info.Print(a...) } +func Infof(format string, a ...any) { color.Info.Printf(format, a...) } +func Infoln(a ...any) { color.Info.Println(a...) } +func Successp(a ...any) { color.Success.Print(a...) } +func Successf(format string, a ...any) { color.Success.Printf(format, a...) } +func Successln(a ...any) { color.Success.Println(a...) } +func Errorp(a ...any) { color.Error.Print(a...) } +func Errorf(format string, a ...any) { color.Error.Printf(format, a...) } +func Errorln(a ...any) { color.Error.Println(a...) } +func Warnp(a ...any) { color.Warn.Print(a...) } +func Warnf(format string, a ...any) { color.Warn.Printf(format, a...) } +func Warnln(a ...any) { color.Warn.Println(a...) } +// source at cliutil/info.go func Workdir() string func BinDir() string func BinFile() string +func BinName() string +func GetTermSize(refresh ...bool) (w int, h int) // source at cliutil/read.go func ReadInput(question string) (string, error) func ReadLine(question string) (string, error) func ReadFirst(question string) (string, error) func ReadFirstByte(question string) (byte, error) func ReadFirstRune(question string) (rune, error) -// source at cliutil/read_nonwin.go func ReadPassword(question ...string) string +func InputIsYes(ans string) bool +func ByteIsYes(ans byte) bool ``` #### CLI Util Usage @@ -161,7 +298,7 @@ func main() { **output**: -```text +```shell PRINT AT github.com/gookit/goutil/cliutil_test.TestParseLine(line_parser_test.go:30) []string [ #len=5 string("./app"), #len=5 @@ -174,7 +311,9 @@ PRINT AT github.com/gookit/goutil/cliutil_test.TestParseLine(line_parser_test.go Build line: ./myapp -a val0 -m "this is message" arg0 ``` -### Dump +> More, please see [./cliutil/README](cliutil/README.md) + +### Dumper > Package `github.com/gookit/goutil/dump` @@ -183,12 +322,12 @@ Build line: ./myapp -a val0 -m "this is message" arg0 func Std() *Dumper func Reset() func Config(fn func(opts *Options)) -func Print(vs ...interface{}) -func Println(vs ...interface{}) -func Fprint(w io.Writer, vs ...interface{}) -func Format(vs ...interface{}) string -func NoLoc(vs ...interface{}) -func Clear(vs ...interface{}) +func Print(vs ...any) +func Println(vs ...any) +func Fprint(w io.Writer, vs ...any) +func Format(vs ...any) string +func NoLoc(vs ...any) +func Clear(vs ...any) // source at dump/dumper.go func NewDumper(out io.Writer, skip int) *Dumper func NewWithOptions(fn func(opts *Options)) *Dumper @@ -237,16 +376,18 @@ Preview: ![](dump/_examples/preview-nested-struct.png) + ### ENV/Environment > Package `github.com/gookit/goutil/envutil` ```go // source at envutil/envutil.go -func VarReplace(s string) string -func VarParse(str string) string -func ParseEnvValue(str string) string +func VarReplace(s string) string { return os.ExpandEnv(s) } +func VarParse(val string) string +func ParseEnvValue(val string) string func ParseValue(val string) (newVal string) +func SetEnvs(mp map[string]string) // source at envutil/get.go func Getenv(name string, def ...string) string func GetInt(name string, def ...int) int @@ -266,6 +407,7 @@ func HasShellEnv(shell string) bool func IsSupportColor() bool func IsSupport256Color() bool func IsSupportTrueColor() bool +func IsGithubActions() bool ``` #### ENV Util Usage @@ -285,46 +427,50 @@ envutil.GetBool("APP_DEBUG", true) envutil.ParseValue("${ENV_NAME | defValue}") ``` + ### Errorx > Package `github.com/gookit/goutil/errorx` Package errorx provide a enhanced error implements, allow with call stack and wrap another error. + ```go +// source at errorx/errors.go +func NewR(code int, msg string) ErrorR +func Fail(code int, msg string) ErrorR +func Suc(msg string) ErrorR // source at errorx/errorx.go func New(msg string) error -func Newf(tpl string, vars ...interface{}) error -func Errorf(tpl string, vars ...interface{}) error +func Newf(tpl string, vars ...any) error +func Errorf(tpl string, vars ...any) error func With(err error, msg string) error -func Withf(err error, tpl string, vars ...interface{}) error +func Withf(err error, tpl string, vars ...any) error func WithPrev(err error, msg string) error -func WithPrevf(err error, tpl string, vars ...interface{}) error +func WithPrevf(err error, tpl string, vars ...any) error func WithStack(err error) error func Traced(err error) error func Stacked(err error) error func WithOptions(msg string, fns ...func(opt *ErrStackOpt)) error func Wrap(err error, msg string) error -func Wrapf(err error, tpl string, vars ...interface{}) error -// source at errorx/reply.go -func NewR(code int, msg string) ErrorR -func Fail(code int, msg string) ErrorR -func Suc(msg string) ErrorR +func Wrapf(err error, tpl string, vars ...any) error // source at errorx/stack.go func FuncForPC(pc uintptr) *Func +func ResetStdOpt() func Config(fns ...func(opt *ErrStackOpt)) func SkipDepth(skipDepth int) func(opt *ErrStackOpt) func TraceDepth(traceDepth int) func(opt *ErrStackOpt) // source at errorx/util.go func Raw(msg string) error -func Rawf(tpl string, vars ...interface{}) error +func Rawf(tpl string, vars ...any) error func Cause(err error) error func Unwrap(err error) error func Previous(err error) error { return Unwrap(err) } +func ToErrorX(err error) (ex *ErrorX, ok bool) func Has(err, target error) bool func Is(err, target error) bool -func To(err error, target interface{}) bool -func As(err error, target interface{}) bool +func To(err error, target any) bool +func As(err error, target any) bool ``` #### Errorx Usage @@ -402,21 +548,25 @@ runtime.goexit() /usr/local/Cellar/go/1.18/libexec/src/runtime/asm_amd64.s:1571 ``` + ### Formatting > Package `github.com/gookit/goutil/fmtutil` ```go // source at fmtutil/format.go -func DataSize(bytes uint64) string -func PrettyJSON(v interface{}) (string, error) +func DataSize(size uint64) string +func SizeToString(size uint64) string { return DataSize(size) } +func StringToByte(sizeStr string) uint64 { return ParseByte(sizeStr) } +func ParseByte(sizeStr string) uint64 +func PrettyJSON(v any) (string, error) func StringsToInts(ss []string) (ints []int, err error) -func ArgsWithSpaces(args []interface{}) (message string) +func ArgsWithSpaces(args []any) (message string) // source at fmtutil/time.go func HowLongAgo(sec int64) string ``` -### FileSystem +### File System > Package `github.com/gookit/goutil/fsutil` @@ -429,52 +579,49 @@ func IsFile(path string) bool func IsAbsPath(aPath string) bool func IsImageFile(path string) bool func IsZipFile(filepath string) bool -// source at fsutil/finder.go -func EmptyFinder() *FileFinder -func NewFinder(dirPaths []string, filePaths ...string) *FileFinder -func ExtFilterFunc(exts []string, include bool) FileFilterFunc -func SuffixFilterFunc(suffixes []string, include bool) FileFilterFunc -func PathNameFilterFunc(names []string, include bool) FileFilterFunc -func DotFileFilterFunc(include bool) FileFilterFunc -func ModTimeFilterFunc(limitSec int, op rune, include bool) FileFilterFunc -func GlobFilterFunc(patterns []string, include bool) FileFilterFunc -func RegexFilterFunc(pattern string, include bool) FileFilterFunc -func DotDirFilterFunc(include bool) DirFilterFunc -func DirNameFilterFunc(names []string, include bool) DirFilterFunc // source at fsutil/fsutil.go -func DiscardReader(src io.Reader) func OSTempFile(pattern string) (*os.File, error) func TempFile(dir, pattern string) (*os.File, error) func OSTempDir(pattern string) (string, error) func TempDir(dir, pattern string) (string, error) func MimeType(path string) (mime string) func ReaderMimeType(r io.Reader) (mime string) -func GlobWithFunc(pattern string, fn func(filePath string) error) (err error) // source at fsutil/info.go func Dir(fpath string) string func PathName(fpath string) string func Name(fpath string) string func FileExt(fpath string) string func Suffix(fpath string) string -func ExpandPath(path string) string +func Expand(pathStr string) string +func ExpandPath(pathStr string) string func Realpath(pathStr string) string +func SplitPath(pathStr string) (dir, name string) +func GlobWithFunc(pattern string, fn func(filePath string) error) (err error) +func FindInDir(dir string, handleFn HandleFunc, filters ...FilterFunc) (e error) // source at fsutil/operate.go func Mkdir(dirPath string, perm os.FileMode) error func MkParentDir(fpath string) error +func DiscardReader(src io.Reader) func MustReadFile(filePath string) []byte func MustReadReader(r io.Reader) []byte +func GetContents(in any) []byte func ReadExistFile(filePath string) []byte func OpenFile(filepath string, flag int, perm os.FileMode) (*os.File, error) -func QuickOpenFile(filepath string) (*os.File, error) -func CreateFile(fpath string, filePerm, dirPerm os.FileMode) (*os.File, error) +func QuickOpenFile(filepath string, fileFlag ...int) (*os.File, error) +func OpenReadFile(filepath string) (*os.File, error) +func CreateFile(fpath string, filePerm, dirPerm os.FileMode, fileFlag ...int) (*os.File, error) func MustCreateFile(filePath string, filePerm, dirPerm os.FileMode) *os.File -func CopyFile(src string, dst string) error -func MustCopyFile(src string, dst string) -func Remove(fpath string) error -func MustRemove(fpath string) -func QuietRemove(fpath string) -func DeleteIfExist(fpath string) error -func DeleteIfFileExist(fpath string) error +func PutContents(filePath string, data any, fileFlag ...int) (int, error) +func WriteFile(filePath string, data any, perm os.FileMode, fileFlag ...int) error +func CopyFile(srcPath, dstPath string) error +func MustCopyFile(srcPath, dstPath string) +func Remove(fPath string) error +func MustRemove(fPath string) +func QuietRemove(fPath string) { _ = os.Remove(fPath) } +func RmIfExist(fPath string) error { return DeleteIfExist(fPath) } +func DeleteIfExist(fPath string) error +func RmFileIfExist(fPath string) error { return DeleteIfFileExist(fPath) } +func DeleteIfFileExist(fPath string) error func Unzip(archive, targetDir string) (err error) ``` @@ -489,11 +636,11 @@ import ( "fmt" "os" - "github.com/gookit/goutil/fsutil" + "github.com/gookit/goutil/fsutil/finder" ) func main() { - f := fsutil.EmptyFinder() + f := finder.EmptyFinder() f. AddDir("./testdata"). @@ -505,7 +652,7 @@ func main() { fmt.Println(filePath) }) - fsutil.NewFinder([]string{"./testdata"}). + finder.NewFinder([]string{"./testdata"}). AddFile("finder.go"). NoDotDir(). EachStat(func(fi os.FileInfo, filePath string) { @@ -514,22 +661,24 @@ func main() { } ``` -### JSON + +### JSON Utils > Package `github.com/gookit/goutil/jsonutil` ```go // source at jsonutil/jsonutil.go -func WriteFile(filePath string, data interface{}) error -func ReadFile(filePath string, v interface{}) error -func Pretty(v interface{}) (string, error) -func Encode(v interface{}) ([]byte, error) -func EncodePretty(v interface{}) ([]byte, error) -func EncodeToWriter(v interface{}, w io.Writer) error -func EncodeUnescapeHTML(v interface{}) ([]byte, error) -func Decode(bts []byte, ptr interface{}) error -func DecodeString(str string, ptr interface{}) error -func DecodeReader(r io.Reader, ptr interface{}) error +func WriteFile(filePath string, data any) error +func ReadFile(filePath string, v any) error +func Pretty(v any) (string, error) +func Encode(v any) ([]byte, error) +func EncodePretty(v any) ([]byte, error) +func EncodeToWriter(v any, w io.Writer) error +func EncodeUnescapeHTML(v any) ([]byte, error) +func Decode(bts []byte, ptr any) error +func DecodeString(str string, ptr any) error +func DecodeReader(r io.Reader, ptr any) error +func Mapping(src, dst any) error func StripComments(src string) string ``` @@ -538,16 +687,34 @@ func StripComments(src string) string > Package `github.com/gookit/goutil/maputil` ```go +// source at maputil/check.go +func HasKey(mp, key any) (ok bool) +func HasAllKeys(mp any, keys ...any) (ok bool, noKey any) // source at maputil/convert.go func KeyToLower(src map[string]string) map[string]string -func ToStringMap(src map[string]interface{}) map[string]string -func HttpQueryString(data map[string]interface{}) string -func ToString(mp map[string]interface{}) string +func ToStringMap(src map[string]any) map[string]string +func HttpQueryString(data map[string]any) string +func ToString(mp map[string]any) string +func ToString2(mp any) string +func FormatIndent(mp any, indent string) string +func Flatten(mp map[string]any) map[string]any +func FlatWithFunc(mp map[string]any, fn reflects.FlatFunc) +// source at maputil/format.go +func NewFormatter(mp any) *MapFormatter +// source at maputil/get.go +func DeepGet(mp map[string]any, path string) (val any) +func QuietGet(mp map[string]any, path string) (val any) +func GetByPath(path string, mp map[string]any) (val any, ok bool) +func Keys(mp any) (keys []string) +func Values(mp any) (values []any) // source at maputil/maputil.go +func MergeSMap(src, dst map[string]string, ignoreCase bool) map[string]string func MergeStringMap(src, dst map[string]string, ignoreCase bool) map[string]string -func GetByPath(key string, mp map[string]interface{}) (val interface{}, ok bool) -func Keys(mp interface{}) (keys []string) -func Values(mp interface{}) (values []interface{}) +func MakeByPath(path string, val any) (mp map[string]any) +func MakeByKeys(keys []string, val any) (mp map[string]any) +// source at maputil/setval.go +func SetByPath(mp *map[string]any, path string, val any) error +func SetByKeys(mp *map[string]any, keys []string, val any) (err error) ``` ### Math/Number @@ -555,26 +722,47 @@ func Values(mp interface{}) (values []interface{}) > Package `github.com/gookit/goutil/mathutil` ```go +// source at mathutil/check.go +func Compare(srcVal, dstVal any, op string) (ok bool) +func CompInt64(srcI64, dstI64 int64, op string) (ok bool) +func CompFloat(srcF64, dstF64 float64, op string) (ok bool) // source at mathutil/convert.go -func Int(in interface{}) (int, error) -func MustInt(in interface{}) int -func IntOrPanic(in interface{}) int -func ToInt(in interface{}) (iVal int, err error) -func Uint(in interface{}) (uint64, error) -func MustUint(in interface{}) uint64 -func ToUint(in interface{}) (u64 uint64, err error) -func Int64(in interface{}) (int64, error) -func MustInt64(in interface{}) int64 -func ToInt64(in interface{}) (i64 int64, err error) -func Float(in interface{}) (float64, error) -func ToFloat(in interface{}) (f64 float64, err error) -func FloatOrPanic(in interface{}) float64 -func MustFloat(in interface{}) float64 -func TryToString(val interface{}, defaultAsErr bool) (str string, err error) -func StringOrPanic(val interface{}) string -func MustString(val interface{}) string -func ToString(val interface{}) (string, error) -func String(val interface{}) string +func Int(in any) (int, error) +func QuietInt(in any) int +func MustInt(in any) int +func IntOrPanic(in any) int +func IntOrErr(in any) (iVal int, err error) +func ToInt(in any) (iVal int, err error) +func StrInt(s string) int +func Uint(in any) (uint64, error) +func QuietUint(in any) uint64 +func MustUint(in any) uint64 +func UintOrErr(in any) (uint64, error) +func ToUint(in any) (u64 uint64, err error) +func Int64(in any) (int64, error) +func QuietInt64(in any) int64 +func MustInt64(in any) int64 +func Int64OrErr(in any) (int64, error) +func ToInt64(in any) (i64 int64, err error) +func QuietFloat(in any) float64 +func FloatOrPanic(in any) float64 +func MustFloat(in any) float64 +func Float(in any) (float64, error) +func FloatOrErr(in any) (float64, error) +func ToFloat(in any) (f64 float64, err error) +func StringOrPanic(val any) string +func MustString(val any) string +func ToString(val any) (string, error) +func StringOrErr(val any) (string, error) +func QuietString(val any) string +func String(val any) string +func TryToString(val any, defaultAsErr bool) (str string, err error) +// source at mathutil/mathutil.go +func MaxFloat(x, y float64) float64 +func MaxInt(x, y int) int +func SwapMaxInt(x, y int) (int, int) +func MaxI64(x, y int64) int64 +func SwapMaxI64(x, y int64) (int64, int64) // source at mathutil/number.go func IsNumeric(c byte) bool func Percent(val, total int) float64 @@ -588,6 +776,57 @@ func RandIntWithSeed(min, max int, seed int64) int func RandomIntWithSeed(min, max int, seed int64) int ``` +### Reflects + +> Package `github.com/gookit/goutil/reflects` + +```go +// source at reflects/check.go +func HasChild(v reflect.Value) bool +func IsNil(v reflect.Value) bool +func IsFunc(val any) bool +func IsEqual(src, dst any) bool +func IsEmpty(v reflect.Value) bool +func IsEmptyValue(v reflect.Value) bool +// source at reflects/conv.go +func BaseTypeVal(v reflect.Value) (value any, err error) +func ValueByType(val any, typ reflect.Type) (rv reflect.Value, err error) +func ValueByKind(val any, kind reflect.Kind) (rv reflect.Value, err error) +func String(rv reflect.Value) string +func ToString(rv reflect.Value) (str string, err error) +func ValToString(rv reflect.Value, defaultAsErr bool) (str string, err error) +// source at reflects/type.go +func ToBaseKind(kind reflect.Kind) BKind +func ToBKind(kind reflect.Kind) BKind +func TypeOf(v any) Type +// source at reflects/util.go +func Elem(v reflect.Value) reflect.Value +func Indirect(v reflect.Value) reflect.Value +func Len(v reflect.Value) int +func SliceSubKind(typ reflect.Type) reflect.Kind +func SetValue(rv reflect.Value, val any) error +func FlatMap(rv reflect.Value, fn FlatFunc) +// source at reflects/value.go +func Wrap(rv reflect.Value) Value +func ValueOf(v any) Value +``` + +### Stdio + +> Package `github.com/gookit/goutil/stdio` + +```go +// source at stdio/ioutil.go +func QuietFprint(w io.Writer, ss ...string) +func QuietFprintf(w io.Writer, tpl string, vs ...any) +func QuietFprintln(w io.Writer, ss ...string) +func QuietWriteString(w io.Writer, ss ...string) +func DiscardReader(src io.Reader) +func MustReadReader(r io.Reader) []byte +// source at stdio/writer.go +func NewWriteWrapper(w io.Writer) *WriteWrapper +``` + ### Standard > Package `github.com/gookit/goutil/stdutil` @@ -596,17 +835,25 @@ func RandomIntWithSeed(min, max int, seed int64) int // source at stdutil/chan.go func WaitCloseSignals(closer io.Closer) error func Go(f func() error) error +func SignalHandler(ctx context.Context, signals ...os.Signal) (execute func() error, interrupt func(error)) // source at stdutil/check.go +func IsNil(v any) bool +func IsEmpty(v any) bool +func IsFunc(val any) bool +func IsEqual(src, dst any) bool +func Contains(data, elem any) bool +func IsContains(data, elem any) bool +func CheckContains(data, elem any) (valid, found bool) func ValueIsEmpty(v reflect.Value) bool func ValueLen(v reflect.Value) int -// source at stdutil/convert.go -func ToString(v interface{}) string -func MustString(v interface{}) string -func TryString(v interface{}) (string, error) -func BaseTypeVal2(v reflect.Value) (value interface{}, err error) -func BaseTypeVal(val interface{}) (value interface{}, err error) +// source at stdutil/conv.go +func ToString(v any) string +func MustString(v any) string +func TryString(v any) (string, error) +func BaseTypeVal(val any) (value any, err error) +func BaseTypeVal2(v reflect.Value) (value any, err error) // source at stdutil/gofunc.go -func FuncName(fn interface{}) string +func FuncName(fn any) string func CutFuncName(fullFcName string) (pkgPath, shortFnName string) func PkgName(fullFcName string) string // source at stdutil/stack.go @@ -615,93 +862,136 @@ func GetCallerInfo(skip int) string func SimpleCallersInfo(skip, num int) []string func GetCallersInfo(skip, max int) []string // source at stdutil/stdutil.go +func DiscardE(_ error) {} func PanicIfErr(err error) func PanicIf(err error) -func Panicf(format string, v ...interface{}) +func Panicf(format string, v ...any) +func GoVersion() string ``` -### Struct +### Structs > Package `github.com/gookit/goutil/structs` ```go // source at structs/alias.go func NewAliases(checker func(alias string)) *Aliases +// source at structs/convert.go +func ToMap(st any, optFns ...MapOptFunc) map[string]any +func MustToMap(st any, optFns ...MapOptFunc) map[string]any +func TryToMap(st any, optFns ...MapOptFunc) (map[string]any, error) +func StructToMap(st any, optFns ...MapOptFunc) (map[string]any, error) // source at structs/data.go -func NewMapData() *MapDataStore +func NewData() *Data +// source at structs/setval.go +func InitDefaults(ptr any, optFns ...InitOptFunc) error +func SetValues(ptr any, data map[string]any, optFns ...SetOptFunc) error // source at structs/structs.go -func ToMap(st interface{}) map[string]interface{} -func TryToMap(st interface{}) (map[string]interface{}, error) -func MustToMap(st interface{}) map[string]interface{} +func MapStruct(srcSt, dstSt any) // source at structs/tags.go -func ParseTags(v interface{}) error -func ParseReflectTags(v reflect.Value) error -func ParseTagValue(str string) maputil.SMap -func ParseTagValueINI(field, str string) (mp maputil.SMap, err error) +func ParseTags(st any, tagNames []string) (map[string]maputil.SMap, error) +func ParseReflectTags(rt reflect.Type, tagNames []string) (map[string]maputil.SMap, error) +func NewTagParser(tagNames ...string) *TagParser +func ParseTagValueDefault(field, tagVal string) (mp maputil.SMap, err error) +func ParseTagValueDefine(sep string, defines []string) TagValFunc +func ParseTagValueNamed(field, tagVal string, keys ...string) (mp maputil.SMap, err error) +// source at structs/value.go +func NewValue(val any) *Value ``` -### String +### Strings > Package `github.com/gookit/goutil/strutil` ```go // source at strutil/bytes.go +func NewBuffer() *Buffer func NewByteChanPool(maxSize int, width int, capWidth int) *ByteChanPool // source at strutil/check.go -func IsNumeric(c byte) bool +func NoCaseEq(s, t string) bool +func IsNumChar(c byte) bool +func IsNumeric(s string) bool func IsAlphabet(char uint8) bool func IsAlphaNum(c uint8) bool func StrPos(s, sub string) int func BytePos(s string, bt byte) int -func RunePos(s string, ru rune) int func HasOneSub(s string, subs []string) bool func HasAllSubs(s string, subs []string) bool -func IsStartsOf(s string, subs []string) bool -func HasOnePrefix(s string, subs []string) bool -func IsStartOf(s, sub string) bool -func IsEndOf(s, sub string) bool -func Len(s string) int -func Utf8len(s string) int -func ValidUtf8String(s string) bool +func IsStartsOf(s string, prefixes []string) bool +func HasOnePrefix(s string, prefixes []string) bool +func HasPrefix(s string, prefix string) bool { return strings.HasPrefix(s, prefix) } +func IsStartOf(s, prefix string) bool { return strings.HasPrefix(s, prefix) } +func HasSuffix(s string, suffix string) bool { return strings.HasSuffix(s, suffix) } +func IsEndOf(s, suffix string) bool { return strings.HasSuffix(s, suffix) } +func IsValidUtf8(s string) bool { return utf8.ValidString(s) } func IsSpace(c byte) bool -func IsSpaceRune(r rune) bool -func IsEmpty(s string) bool +func IsEmpty(s string) bool { return len(s) == 0 } func IsBlank(s string) bool func IsNotBlank(s string) bool func IsBlankBytes(bs []byte) bool func IsSymbol(r rune) bool +func IsVersion(s string) bool +func Compare(s1, s2, op string) bool +func VersionCompare(v1, v2, op string) bool // source at strutil/convert.go -func Join(sep string, ss ...string) string -func Implode(sep string, ss ...string) string -func String(val interface{}) (string, error) -func MustString(in interface{}) string -func ToString(val interface{}) (string, error) -func AnyToString(val interface{}, defaultAsErr bool) (str string, err error) +func Quote(s string) string { return strconv.Quote(s) } +func Unquote(s string) string +func Join(sep string, ss ...string) string { return strings.Join(ss, sep) } +func JoinList(sep string, ss []string) string { return strings.Join(ss, sep) } +func Implode(sep string, ss ...string) string { return strings.Join(ss, sep) } +func String(val any) (string, error) +func QuietString(in any) string +func MustString(in any) string +func StringOrErr(val any) (string, error) +func ToString(val any) (string, error) +func AnyToString(val any, defaultAsErr bool) (str string, err error) func Byte2str(b []byte) string func Byte2string(b []byte) string func ToBytes(s string) (b []byte) func ToBool(s string) (bool, error) +func QuietBool(s string) bool func MustBool(s string) bool func Bool(s string) (bool, error) func Int(s string) (int, error) func ToInt(s string) (int, error) +func QuietInt(s string) int func MustInt(s string) int func IntOrPanic(s string) int +func Int64(s string) int64 +func QuietInt64(s string) int64 +func ToInt64(s string) (int64, error) +func Int64OrErr(s string) (int64, error) +func MustInt64(s string) int64 +func Int64OrPanic(s string) int64 func Ints(s string, sep ...string) []int -func ToInts(s string, sep ...string) ([]int, error) +func ToInts(s string, sep ...string) ([]int, error) { return ToIntSlice(s, sep...) } func ToIntSlice(s string, sep ...string) (ints []int, err error) -func ToArray(s string, sep ...string) []string -func Strings(s string, sep ...string) []string -func ToStrings(s string, sep ...string) []string +func ToArray(s string, sep ...string) []string { return ToSlice(s, sep...) } +func Strings(s string, sep ...string) []string { return ToSlice(s, sep...) } +func ToStrings(s string, sep ...string) []string { return ToSlice(s, sep...) } func ToSlice(s string, sep ...string) []string func ToOSArgs(s string) []string func MustToTime(s string, layouts ...string) time.Time func ToTime(s string, layouts ...string) (t time.Time, err error) +func ToDuration(s string) (time.Duration, error) // source at strutil/encode.go -func Base64(str string) string -func B64Encode(str string) string +func EscapeJS(s string) string +func EscapeHTML(s string) string +func AddSlashes(s string) string +func StripSlashes(s string) string +func Md5(src any) string +func MD5(src any) string { return Md5(src) } +func GenMd5(src any) string { return Md5(src) } +func Md5Bytes(src any) []byte func URLEncode(s string) string func URLDecode(s string) string +func B32Encode(str string) string +func B32Decode(str string) string +func B64Encode(str string) string +func B64EncodeBytes(src []byte) []byte +func B64Decode(str string) string +func B64DecodeBytes(str string) []byte +func Encoding(base int, typ BaseType) BaseEncoder // source at strutil/filter.go func Trim(s string, cutSet ...string) string func Ltrim(s string, cutSet ...string) string { return TrimLeft(s, cutSet...) } @@ -712,6 +1002,7 @@ func RTrim(s string, cutSet ...string) string { return TrimRight(s, cutSet...) } func TrimRight(s string, cutSet ...string) string func FilterEmail(s string) string // source at strutil/format.go +func Title(s string) string { return strings.ToTitle(s) } func Lower(s string) string { return strings.ToLower(s) } func Lowercase(s string) string { return strings.ToLower(s) } func Upper(s string) string { return strings.ToUpper(s) } @@ -719,27 +1010,61 @@ func Uppercase(s string) string { return strings.ToUpper(s) } func UpperWord(s string) string func LowerFirst(s string) string func UpperFirst(s string) string -func Snake(s string, sep ...string) string func SnakeCase(s string, sep ...string) string -func Camel(s string, sep ...string) string +func Camel(s string, sep ...string) string { return CamelCase(s, sep...) } func CamelCase(s string, sep ...string) string +func Indent(s, prefix string) string +func IndentBytes(b, prefix []byte) []byte // source at strutil/id.go func MicroTimeID() string func MicroTimeHexID() string +// source at strutil/padding.go +func Padding(s, pad string, length int, pos PosFlag) string +func PadLeft(s, pad string, length int) string +func PadRight(s, pad string, length int) string +func Resize(s string, length int, align PosFlag) string +func PadBytes(bs []byte, pad byte, length int, pos PosFlag) []byte +func PadBytesLeft(bs []byte, pad byte, length int) []byte +func PadBytesRight(bs []byte, pad byte, length int) []byte +func PadRunes(rs []rune, pad rune, length int, pos PosFlag) []rune +func PadRunesLeft(rs []rune, pad rune, length int) []rune +func PadRunesRight(rs []rune, pad rune, length int) []rune +func Repeat(s string, times int) string +func RepeatRune(char rune, times int) []rune +func RepeatBytes(char byte, times int) []byte // source at strutil/random.go -func Md5(src interface{}) string -func GenMd5(src interface{}) string func RandomChars(ln int) string func RandomCharsV2(ln int) string func RandomCharsV3(ln int) string func RandomBytes(length int) ([]byte, error) func RandomString(length int) (string, error) +// source at strutil/runes.go +func RuneIsWord(c rune) bool +func RuneIsLower(c rune) bool +func RuneIsUpper(c rune) bool +func RunePos(s string, ru rune) int { return strings.IndexRune(s, ru) } +func IsSpaceRune(r rune) bool +func Utf8Len(s string) int { return utf8.RuneCountInString(s) } +func Utf8len(s string) int { return utf8.RuneCountInString(s) } +func RuneCount(s string) int { return len([]rune(s)) } +func RuneWidth(r rune) int +func TextWidth(s string) int { return Utf8Width(s) } +func Utf8Width(s string) int { return RunesWidth([]rune(s)) } +func RunesWidth(rs []rune) (w int) +func TextTruncate(s string, w int, tail string) string { return Utf8Truncate(s, w, tail) } +func Utf8Truncate(s string, w int, tail string) string +func TextSplit(s string, w int) []string { return Utf8Split(s, w) } +func Utf8Split(s string, w int) []string +func TextWrap(s string, w int) string { return WidthWrap(s, w) } +func WidthWrap(s string, w int) string +func WordWrap(s string, w int) string // source at strutil/similar_find.go func NewComparator(src, dst string) *SimilarComparator func Similarity(s, t string, rate float32) (float32, bool) // source at strutil/split.go func Cut(s, sep string) (before string, after string, found bool) func MustCut(s, sep string) (before string, after string) +func TrimCut(s, sep string) (string, string) func SplitValid(s, sep string) (ss []string) { return Split(s, sep) } func Split(s, sep string) (ss []string) func SplitNValid(s, sep string, n int) (ss []string) { return SplitN(s, sep, n) } @@ -747,17 +1072,13 @@ func SplitN(s, sep string, n int) (ss []string) func SplitTrimmed(s, sep string) (ss []string) func SplitNTrimmed(s, sep string, n int) (ss []string) func Substr(s string, pos, length int) string +func SplitInlineComment(val string) (string, string) // source at strutil/strutil.go -func Padding(s, pad string, length int, pos uint8) string -func PadLeft(s, pad string, length int) string -func PadRight(s, pad string, length int) string -func Repeat(s string, times int) string -func RepeatRune(char rune, times int) (chars []rune) -func RepeatBytes(char byte, times int) (chars []byte) func Replaces(str string, pairs map[string]string) string -func PrettyJSON(v interface{}) (string, error) -func RenderTemplate(input string, data interface{}, fns template.FuncMap, isFile ...bool) string -func RenderText(input string, data interface{}, fns template.FuncMap, isFile ...bool) string +func PrettyJSON(v any) (string, error) +func RenderTemplate(input string, data any, fns template.FuncMap, isFile ...bool) string +func RenderText(input string, data any, fns template.FuncMap, isFile ...bool) string +func WrapTag(s, tag string) string ``` ### System @@ -766,39 +1087,51 @@ func RenderText(input string, data interface{}, fns template.FuncMap, isFile ... ```go // source at sysutil/exec.go +func NewCmd(bin string, args ...string) *cmdr.Cmd +func FlushExec(bin string, args ...string) error func QuickExec(cmdLine string, workDir ...string) (string, error) func ExecLine(cmdLine string, workDir ...string) (string, error) func ExecCmd(binName string, args []string, workDir ...string) (string, error) func ShellExec(cmdLine string, shells ...string) (string, error) -func FindExecutable(binName string) (string, error) -func Executable(binName string) (string, error) -func HasExecutable(binName string) bool +// source at sysutil/stack.go +func CallersInfos(skip, num int, filters ...func(file string, fc *runtime.Func) bool) []*CallerInfo // source at sysutil/sysenv.go -func Hostname() string -func IsWin() bool -func IsWindows() bool -func IsMac() bool -func IsLinux() bool func IsMSys() bool func IsConsole(out io.Writer) bool func IsTerminal(fd uintptr) bool func StdIsTerminal() bool +func Hostname() string func CurrentShell(onlyName bool) (path string) func HasShellEnv(shell string) bool func IsShellSpecialVar(c uint8) bool +func EnvPaths() []string +func FindExecutable(binName string) (string, error) +func Executable(binName string) (string, error) +func HasExecutable(binName string) bool +func SearchPath(keywords string) []string +// source at sysutil/sysgo.go +func GoVersion() string +func ParseGoVersion(line string) (*GoInfo, error) +func OsGoInfo() (*GoInfo, error) // source at sysutil/sysutil.go func Workdir() string func BinDir() string func BinFile() string // source at sysutil/sysutil_nonwin.go +func IsWin() bool +func IsWindows() bool +func IsMac() bool +func IsDarwin() bool +func IsLinux() bool func Kill(pid int, signal syscall.Signal) error func ProcessExists(pid int) bool +func OpenBrowser(URL string) error // source at sysutil/user.go func MustFindUser(uname string) *user.User func LoginUser() *user.User func CurrentUser() *user.User -func UserHomeDir() string func UHomeDir() string +func UserHomeDir() string func HomeDir() string func UserDir(subPath string) string func UserCacheDir(subPath string) string @@ -814,6 +1147,16 @@ func ChangeUserUidGid(newUid int, newGid int) (err error) > Package `github.com/gookit/goutil/testutil` ```go +// source at testutil/buffer.go +func NewBuffer() *Buffer +// source at testutil/envmock.go +func MockEnvValue(key, val string, fn func(nv string)) +func MockEnvValues(kvMap map[string]string, fn func()) +func MockOsEnvByText(envText string, fn func()) +func MockOsEnv(mp map[string]string, fn func()) +func ClearOSEnv() { os.Clearenv() } +func RevertOSEnv() +func MockCleanOsEnv(mp map[string]string, fn func()) // source at testutil/httpmock.go func NewHttpRequest(method, path string, data *MD) *http.Request func MockRequest(h http.Handler, method, path string, data *MD) *httptest.ResponseRecorder @@ -821,33 +1164,34 @@ func MockRequest(h http.Handler, method, path string, data *MD) *httptest.Respon func DiscardStdout() error func ReadOutput() (s string) func RewriteStdout() -func RestoreStdout() (s string) +func RestoreStdout(printData ...bool) (s string) func RewriteStderr() -func RestoreStderr() (s string) -func MockEnvValue(key, val string, fn func(nv string)) -func MockEnvValues(kvMap map[string]string, fn func()) -func MockOsEnvByText(envText string, fn func()) -func MockOsEnv(mp map[string]string, fn func()) +func RestoreStderr(printData ...bool) (s string) +// source at testutil/writer.go +func NewTestWriter() *TestWriter ``` ### Timex > Package `github.com/gookit/goutil/timex` +Provides an enhanced time.Time implementation, and add more commonly used functional methods. ```go +// source at timex/template.go +func ToLayout(template string) string // source at timex/timex.go -func Now() *TimeX -func New(t time.Time) *TimeX -func Wrap(t time.Time) *TimeX -func FromTime(t time.Time) *TimeX -func Local() *TimeX -func FromUnix(sec int64) *TimeX -func FromDate(s string, template ...string) (*TimeX, error) -func FromString(s string, layouts ...string) (*TimeX, error) -func LocalByName(tzName string) *TimeX -func SetLocalByName(tzName string) error +func Now() *Time +func New(t time.Time) *Time +func Wrap(t time.Time) *Time +func FromTime(t time.Time) *Time +func Local() *Time +func FromUnix(sec int64) *Time +func FromDate(s string, template ...string) (*Time, error) +func FromString(s string, layouts ...string) (*Time, error) +func LocalByName(tzName string) *Time // source at timex/util.go func NowUnix() int64 +func SetLocalByName(tzName string) error func Format(t time.Time) string func FormatBy(t time.Time, layout string) string func Date(t time.Time, template string) string @@ -860,6 +1204,8 @@ func NowAddDay(day int) time.Time func NowAddHour(hour int) time.Time func NowAddMinutes(minutes int) time.Time func NowAddSeconds(seconds int) time.Time +func NowHourStart() time.Time +func NowHourEnd() time.Time func AddDay(t time.Time, day int) time.Time func AddHour(t time.Time, hour int) time.Time func AddMinutes(t time.Time, minutes int) time.Time @@ -868,11 +1214,10 @@ func HourStart(t time.Time) time.Time func HourEnd(t time.Time) time.Time func DayStart(t time.Time) time.Time func DayEnd(t time.Time) time.Time -func NowHourStart() time.Time func TodayStart() time.Time func TodayEnd() time.Time func HowLongAgo(sec int64) string -func ToLayout(template string) string +func ToDuration(s string) (time.Duration, error) ``` #### Timex Usage @@ -897,7 +1242,7 @@ tx, err := timex.FromString("2022-04-20 19:40:34") // custom set the datetime layout tx, err := timex.FromString("2022-04-20 19:40:34", "2006-01-02 15:04:05") // use date template as layout -tx, err := timex.FromDate("2022-04-20 19:40:34", "Y-M-D H:I:S") +tx, err := timex.FromDate("2022-04-20 19:40:34", "Y-m-d H:I:S") ``` **Use timex instance** @@ -912,10 +1257,10 @@ change time: tx.Yesterday() tx.Tomorrow() -tx.DayStart() // get time at Y-M-D 00:00:00 -tx.DayEnd() // get time at Y-M-D 23:59:59 -tx.HourStart() // get time at Y-M-D H:00:00 -tx.HourEnd() // get time at Y-M-D H:59:59 +tx.DayStart() // get time at Y-m-d 00:00:00 +tx.DayEnd() // get time at Y-m-d 23:59:59 +tx.HourStart() // get time at Y-m-d H:00:00 +tx.HourEnd() // get time at Y-m-d H:59:59 tx.AddDay(2) tx.AddHour(1) @@ -947,31 +1292,40 @@ t := NowAddSeconds(180) // from now add 180 seconds **Convert time to date by template** +Template Chars: + ```text -Template Vars: Y,y - year Y - year 2006 y - year 06 - M,m - month 01 - D,d - day 02 - H,h - hour 15 - I,i - minute 04 - S,s - second 05 + m - month 01-12 + d - day 01-31 + H,h - hour + H - hour 00-23 + h - hour 01-12 + I,i - minute + I - minute 00-59 + i - minute 0-59 + S,s - second + S - second 00-59 + s - second 0-59 ``` +> More, please see [char map](./timex/template.go) + Examples, use timex: ```go -now := timex.Now() -date := now.DateFormat("Y-M-D H:i:s") // Output: 2022-04-20 19:40:34 -date = now.DateFormat("y-M-D H:i:s") // Output: 22-04-20 19:40:34 +tx := timex.Now() +date := tx.DateFormat("Y-m-d H:I:S") // Output: 2022-04-20 19:09:03 +date = tx.DateFormat("y-m-d h:i:s") // Output: 22-04-20 07:9:3 ``` Format time.Time: ```go -now := time.Now() -date := timex.DateFormat(now, "Y-M-D H:i:s") // Output: 2022-04-20 19:40:34 +tx := time.Now() +date := timex.DateFormat(tx, "Y-m-d H:I:S") // Output: 2022-04-20 19:40:34 ``` More usage: @@ -980,32 +1334,53 @@ More usage: ts := timex.NowUnix() // current unix timestamp date := FormatUnix(ts, "2006-01-02 15:04:05") // Get: 2022-04-20 19:40:34 -date := FormatUnixByTpl(ts, "Y-M-D H:I:S") // Get: 2022-04-20 19:40:34 +date := FormatUnixByTpl(ts, "Y-m-d H:I:S") // Get: 2022-04-20 19:40:34 ``` + ## Code Check & Testing ```bash gofmt -w -l ./ golint ./... -go test ./... + +# testing +go test -v ./... +go test -v -run ^TestErr$ +go test -v -run ^TestErr$ ./testutil/assert/... ``` +Testing in docker: + +```shell +cd goutil +docker run -ti -v $(pwd):/go/work golang:1.18 +root@xx:/go/work# go test ./... +``` + +## Related + +- https://github.com/duke-git/lancet +- https://github.com/samber/lo +- https://github.com/zyedidia/generic +- https://github.com/thoas/go-funk + ## Gookit packages - - [gookit/ini](https://github.com/gookit/ini) Go config management, use INI files - - [gookit/rux](https://github.com/gookit/rux) Simple and fast request router for golang HTTP - - [gookit/gcli](https://github.com/gookit/gcli) Build CLI application, tool library, running CLI commands - - [gookit/slog](https://github.com/gookit/slog) Lightweight, easy to extend, configurable logging library written in Go - - [gookit/color](https://github.com/gookit/color) A command-line color library with true color support, universal API methods and Windows support - - [gookit/event](https://github.com/gookit/event) Lightweight event manager and dispatcher implements by Go - - [gookit/cache](https://github.com/gookit/cache) Generic cache use and cache manager for golang. support File, Memory, Redis, Memcached. - - [gookit/config](https://github.com/gookit/config) Go config management. support JSON, YAML, TOML, INI, HCL, ENV and Flags - - [gookit/filter](https://github.com/gookit/filter) Provide filtering, sanitizing, and conversion of golang data - - [gookit/validate](https://github.com/gookit/validate) Use for data validation and filtering. support Map, Struct, Form data - - [gookit/goutil](https://github.com/gookit/goutil) Some utils for the Go: string, array/slice, map, format, cli, env, filesystem, test and more - - More, please see https://github.com/gookit +- [gookit/ini](https://github.com/gookit/ini) Go config management, use INI files +- [gookit/rux](https://github.com/gookit/rux) Simple and fast request router for golang HTTP +- [gookit/gcli](https://github.com/gookit/gcli) Build CLI application, tool library, running CLI commands +- [gookit/slog](https://github.com/gookit/slog) Lightweight, easy to extend, configurable logging library written in Go +- [gookit/color](https://github.com/gookit/color) A command-line color library with true color support, universal API methods and Windows support +- [gookit/event](https://github.com/gookit/event) Lightweight event manager and dispatcher implements by Go +- [gookit/cache](https://github.com/gookit/cache) Generic cache use and cache manager for golang. support File, Memory, Redis, Memcached. +- [gookit/config](https://github.com/gookit/config) Go config management. support JSON, YAML, TOML, INI, HCL, ENV and Flags +- [gookit/filter](https://github.com/gookit/filter) Provide filtering, sanitizing, and conversion of golang data +- [gookit/validate](https://github.com/gookit/validate) Use for data validation and filtering. support Map, Struct, Form data +- [gookit/goutil](https://github.com/gookit/goutil) Some utils for the Go: string, array/slice, map, format, cli, env, filesystem, test and more +- More, please see https://github.com/gookit ## License [MIT](LICENSE) + diff --git a/vendor/github.com/gookit/goutil/README.zh-CN.md b/vendor/github.com/gookit/goutil/README.zh-CN.md index 20a3de26..829ffe39 100644 --- a/vendor/github.com/gookit/goutil/README.zh-CN.md +++ b/vendor/github.com/gookit/goutil/README.zh-CN.md @@ -2,35 +2,82 @@ ![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/gookit/goutil?style=flat-square) [![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/gookit/goutil)](https://github.com/gookit/goutil) -[![GoDoc](https://godoc.org/github.com/gookit/goutil?status.svg)](https://pkg.go.dev/github.com/gookit/goutil) [![Go Report Card](https://goreportcard.com/badge/github.com/gookit/goutil)](https://goreportcard.com/report/github.com/gookit/goutil) [![Unit-Tests](https://github.com/gookit/goutil/workflows/Unit-Tests/badge.svg)](https://github.com/gookit/goutil/actions) [![Coverage Status](https://coveralls.io/repos/github/gookit/goutil/badge.svg?branch=master)](https://coveralls.io/github/gookit/goutil?branch=master) +[![Go Reference](https://pkg.go.dev/badge/github.com/gookit/goutil.svg)](https://pkg.go.dev/github.com/gookit/goutil) -Go一些常用的工具函数收集、实现和整理 - -- `arrutil` array/slice 相关操作的函数工具包 -- `dump` 简单的变量打印工具,打印 slice, map 会自动换行显示每个元素,同时会显示打印调用位置 -- `cliutil` CLI 的一些工具函数包. eg: read input, exec command, cmdline parse/build -- `errorx` 为 go 提供增强的错误实现,允许使用堆栈跟踪和包装另一个错误。 -- `envutil` ENV 信息获取判断工具包. eg: get one, get info, parse var -- `fmtutil` format data tool -- `fsutil` 文件系统操作相关的工具函数包. eg: file and dir check, operate -- `maputil` map 相关操作的函数工具包. eg: convert, sub-value get, simple merge -- `mathutil`, `numutil` int/number 相关操作的函数工具包. eg: convert, math calc, random -- `netutil/httpreq` 包装 http.Client 实现的更加易于使用的HTTP客户端 -- `strutil` string 相关操作的函数工具包. eg: bytes, check, convert, encode, format and more -- `sysutil` system 相关操作的函数工具包. eg: sysenv, exec, user, process -- `testutil` test help 相关操作的函数工具包. eg: http test, mock ENV value -- `timex` 提供增强的 time.Time 实现。添加更多常用的功能方法 - - 例如: DayStart(), DayAfter(), DayAgo(), DateFormat() 等等 +Go一些常用的string、number、slice、map、struct、env、system等工具函数实现、增强、收集和整理。 > **[EN README](README.md)** +**基础工具包** + +- [`arrutil`](./arrutil) array/slice 相关操作的函数工具包 如:类型转换,元素检查等等 +- [`cliutil`](./cliutil) CLI 的一些工具函数包. eg: read input, exec command + - [cmdline](./cliutil/cmdline) 提供 cmdline 解析,args 构建到 cmdline +- [`envutil`](./envutil) ENV 信息获取判断工具包. eg: get one, get info, parse var +- [`fmtutil`](./fmtutil) 格式化数据工具函数 eg:数据size +- [`fsutil`](./fsutil) 文件系统操作相关的工具函数包. eg: file and dir check, operate +- [`jsonutil`](./jsonutil) 一些用于快速读取、写入、编码、解码 JSON 数据的实用函数。 +- [`maputil`](./maputil) map 相关操作的函数工具包. eg: convert, sub-value get, simple merge +- [`mathutil`](./mathutil) int/number 相关操作的函数工具包. eg: convert, math calc, random +- [`reflects`](./reflects) 提供一些扩展性的反射使用工具函数. +- [`stdutil`](./stdutil) 提供一些常用的 std util 函数。 +- [`structs`](./structs) 为 struct 提供一些扩展 util 函数。 eg: tag parse, struct data +- [`strutil`](./strutil) string 相关操作的函数工具包. eg: bytes, check, convert, encode, format and more +- [`sysutil`](./sysutil) system 相关操作的函数工具包. eg: sysenv, exec, user, process + - [process](./sysutil/process) 提供一些进程操作相关的实用功能。 + +**扩展工具包** + +- [`cflag`](./cflag): 包装和扩展 go `flag.FlagSet` 以方便快速的构建简单的命令行应用程序 +- [`dump`](./dump) GO变量打印工具,打印 slice, map 会自动换行显示每个元素,同时会显示打印调用位置 +- [`errorx`](./errorx) 为 go 提供增强的错误实现,允许带有堆栈跟踪信息和包装另一个错误。 +- strutil: + - `netutil/httpreq` 包装 http.Client 实现的更加易于使用的HTTP客户端 +- strutil: + - [textscan](strutil/textscan) 实现了一个快速扫描和分析文本内容的解析器. 可用于解析 INI, Properties 等格式内容 +- sysutil: + - [clipboard](sysutil/clipboard) 提供简单的剪贴板读写操作工具库 + - [cmdr](sysutil/cmdr) 提供快速构建和运行一个cmd,批量运行多个cmd任务 +- [`testutil`](testutil) test help 相关操作的函数工具包. eg: http test, mock ENV value + - [assert](testutil/assert) 用于帮助测试的断言函数工具包 +- [`timex`](timex) 提供增强的 time.Time 实现。添加更多常用的功能方法 + - 例如: DayStart(), DayAfter(), DayAgo(), DateFormat() 等等 + ## GoDoc - [Godoc for github](https://pkg.go.dev/github.com/gookit/goutil) +## 获取 + +```shell +go get github.com/gookit/goutil +``` + +## Usage + +```go +// github.com/gookit/goutil +is.True(goutil.IsEmpty(nil)) +is.False(goutil.IsEmpty("abc")) + +is.True(goutil.IsEqual("a", "a")) +is.True(goutil.IsEqual([]string{"a"}, []string{"a"})) +is.True(goutil.IsEqual(23, 23)) + +is.True(goutil.Contains("abc", "a")) +is.True(goutil.Contains([]string{"abc", "def"}, "abc")) +is.True(goutil.Contains(map[int]string{2: "abc", 4: "def"}, 4)) + +// convert type +str = goutil.String(23) // "23" +iVal = goutil.Int("-2") // 2 +i64Val = goutil.Int64("-2") // -2 +u64Val = goutil.Uint("2") // 2 +``` + ## Packages ### Array/Slice @@ -41,31 +88,46 @@ Go一些常用的工具函数收集、实现和整理 // source at arrutil/arrutil.go func Reverse(ss []string) func StringsRemove(ss []string, s string) []string -func TrimStrings(ss []string, cutSet ...string) (ns []string) -func GetRandomOne(arr interface{}) interface{} +func StringsFilter(ss []string, filter ...func(s string) bool) []string +func StringsMap(ss []string, mapFn func(s string) string) []string +func TrimStrings(ss []string, cutSet ...string) []string // source at arrutil/check.go func IntsHas(ints []int, val int) bool func Int64sHas(ints []int64, val int64) bool func InStrings(elem string, ss []string) bool { return StringsHas(ss, elem) } func StringsHas(ss []string, val string) bool -func HasValue(arr, val interface{}) bool -func Contains(arr, val interface{}) bool -func NotContains(arr, val interface{}) bool +func HasValue(arr, val any) bool +func Contains(arr, val any) bool +func NotContains(arr, val any) bool +// source at arrutil/collection.go +func TwowaySearch(data any, item any, fn Comparer) (int, error) +func MakeEmptySlice(itemType reflect.Type) any +func CloneSlice(data any) any +func Excepts(first, second any, fn Comparer) any +func Intersects(first any, second any, fn Comparer) any +func Union(first, second any, fn Comparer) any +func Find(source any, fn Predicate) (any, error) +func FindOrDefault(source any, fn Predicate, defaultValue any) any +func TakeWhile(data any, fn Predicate) any +func ExceptWhile(data any, fn Predicate) any // source at arrutil/convert.go func JoinStrings(sep string, ss ...string) string func StringsJoin(sep string, ss ...string) string func StringsToInts(ss []string) (ints []int, err error) -func MustToStrings(arr interface{}) []string -func StringsToSlice(ss []string) []interface{} -func ToInt64s(arr interface{}) (ret []int64, err error) -func MustToInt64s(arr interface{}) []int64 -func SliceToInt64s(arr []interface{}) []int64 -func ToStrings(arr interface{}) (ret []string, err error) -func SliceToStrings(arr []interface{}) []string -func AnyToString(arr interface{}) string -func SliceToString(arr ...interface{}) string { return ToString(arr) } -func ToString(arr []interface{}) string -func JoinSlice(sep string, arr ...interface{}) string +func MustToStrings(arr any) []string +func StringsToSlice(ss []string) []any +func ToInt64s(arr any) (ret []int64, err error) +func MustToInt64s(arr any) []int64 +func SliceToInt64s(arr []any) []int64 +func ToStrings(arr any) (ret []string, err error) +func SliceToStrings(arr []any) []string +func AnyToString(arr any) string +func SliceToString(arr ...any) string { return ToString(arr) } +func ToString(arr []any) string +func JoinSlice(sep string, arr ...any) string +// source at arrutil/format.go +func NewFormatter(arr any) *ArrFormatter +func FormatIndent(arr any, indent string) string ``` #### ArrUtil Usage @@ -88,6 +150,39 @@ ints, err := arrutil.ToInt64s([]string{"1", "2"}) // ints: []int64{1, 2} ss, err := arrutil.ToStrings([]int{1, 2}) // ss: []string{"1", "2"} ``` + +### Cflag + +> Package `github.com/gookit/goutil/cflag` + +```go +// source at cflag/app.go +func NewApp(fns ...func(app *App)) *App +func NewCmd(name, desc string) *Cmd +// source at cflag/cflag.go +func SetDebug(open bool) +func New(fns ...func(c *CFlags)) *CFlags +func NewEmpty(fns ...func(c *CFlags)) *CFlags +func WithDesc(desc string) func(c *CFlags) +func WithVersion(version string) func(c *CFlags) +// source at cflag/optarg.go +func NewArg(name, desc string, required bool) *FlagArg +// source at cflag/util.go +func IsZeroValue(opt *flag.Flag, value string) (bool, bool) +func AddPrefix(name string) string +func AddPrefixes(name string, shorts []string) string +func AddPrefixes2(name string, shorts []string, nameAtEnd bool) string +func SplitShortcut(shortcut string) []string +func FilterNames(names []string) []string +func IsFlagHelpErr(err error) bool +func WrapColorForCode(s string) string +func ReplaceShorts(args []string, shortsMap map[string]string) []string +``` +#### `cflag` Usage + +`cflag` 使用说明请看 [cflag/README.zh-CN.md](cflag/README.zh-CN.md) + + ### CLI/Console > Package `github.com/gookit/goutil/cliutil` @@ -106,17 +201,59 @@ func ExecCommand(binName string, args []string, workDir ...string) (string, erro func ShellExec(cmdLine string, shells ...string) (string, error) func CurrentShell(onlyName bool) (path string) func HasShellEnv(shell string) bool +func BuildOptionHelpName(names []string) string +func ShellQuote(s string) string +func OutputLines(output string) []string +func FirstLine(output string) string +// source at cliutil/color_print.go +func Redp(a ...any) { color.Red.Print(a...) } +func Redf(format string, a ...any) { color.Red.Printf(format, a...) } +func Redln(a ...any) { color.Red.Println(a...) } +func Bluep(a ...any) { color.Blue.Print(a...) } +func Bluef(format string, a ...any) { color.Blue.Printf(format, a...) } +func Blueln(a ...any) { color.Blue.Println(a...) } +func Cyanp(a ...any) { color.Cyan.Print(a...) } +func Cyanf(format string, a ...any) { color.Cyan.Printf(format, a...) } +func Cyanln(a ...any) { color.Cyan.Println(a...) } +func Grayp(a ...any) { color.Gray.Print(a...) } +func Grayf(format string, a ...any) { color.Gray.Printf(format, a...) } +func Grayln(a ...any) { color.Gray.Println(a...) } +func Greenp(a ...any) { color.Green.Print(a...) } +func Greenf(format string, a ...any) { color.Green.Printf(format, a...) } +func Greenln(a ...any) { color.Green.Println(a...) } +func Yellowp(a ...any) { color.Yellow.Print(a...) } +func Yellowf(format string, a ...any) { color.Yellow.Printf(format, a...) } +func Yellowln(a ...any) { color.Yellow.Println(a...) } +func Magentap(a ...any) { color.Magenta.Print(a...) } +func Magentaf(format string, a ...any) { color.Magenta.Printf(format, a...) } +func Magentaln(a ...any) { color.Magenta.Println(a...) } +func Infop(a ...any) { color.Info.Print(a...) } +func Infof(format string, a ...any) { color.Info.Printf(format, a...) } +func Infoln(a ...any) { color.Info.Println(a...) } +func Successp(a ...any) { color.Success.Print(a...) } +func Successf(format string, a ...any) { color.Success.Printf(format, a...) } +func Successln(a ...any) { color.Success.Println(a...) } +func Errorp(a ...any) { color.Error.Print(a...) } +func Errorf(format string, a ...any) { color.Error.Printf(format, a...) } +func Errorln(a ...any) { color.Error.Println(a...) } +func Warnp(a ...any) { color.Warn.Print(a...) } +func Warnf(format string, a ...any) { color.Warn.Printf(format, a...) } +func Warnln(a ...any) { color.Warn.Println(a...) } +// source at cliutil/info.go func Workdir() string func BinDir() string func BinFile() string +func BinName() string +func GetTermSize(refresh ...bool) (w int, h int) // source at cliutil/read.go func ReadInput(question string) (string, error) func ReadLine(question string) (string, error) func ReadFirst(question string) (string, error) func ReadFirstByte(question string) (byte, error) func ReadFirstRune(question string) (rune, error) -// source at cliutil/read_nonwin.go func ReadPassword(question ...string) string +func InputIsYes(ans string) bool +func ByteIsYes(ans byte) bool ``` #### CLI Util Usage @@ -159,7 +296,7 @@ func main() { **output**: -```text +```shell PRINT AT github.com/gookit/goutil/cliutil_test.TestParseLine(line_parser_test.go:30) []string [ #len=5 string("./app"), #len=5 @@ -172,7 +309,9 @@ PRINT AT github.com/gookit/goutil/cliutil_test.TestParseLine(line_parser_test.go Build line: ./myapp -a val0 -m "this is message" arg0 ``` -### Dump +> More, please see [./cliutil/README](cliutil/README.md) + +### Dumper > Package `github.com/gookit/goutil/dump` @@ -181,12 +320,12 @@ Build line: ./myapp -a val0 -m "this is message" arg0 func Std() *Dumper func Reset() func Config(fn func(opts *Options)) -func Print(vs ...interface{}) -func Println(vs ...interface{}) -func Fprint(w io.Writer, vs ...interface{}) -func Format(vs ...interface{}) string -func NoLoc(vs ...interface{}) -func Clear(vs ...interface{}) +func Print(vs ...any) +func Println(vs ...any) +func Fprint(w io.Writer, vs ...any) +func Format(vs ...any) string +func NoLoc(vs ...any) +func Clear(vs ...any) // source at dump/dumper.go func NewDumper(out io.Writer, skip int) *Dumper func NewWithOptions(fn func(opts *Options)) *Dumper @@ -235,16 +374,18 @@ Preview: ![](dump/_examples/preview-nested-struct.png) + ### ENV/Environment > Package `github.com/gookit/goutil/envutil` ```go // source at envutil/envutil.go -func VarReplace(s string) string -func VarParse(str string) string -func ParseEnvValue(str string) string +func VarReplace(s string) string { return os.ExpandEnv(s) } +func VarParse(val string) string +func ParseEnvValue(val string) string func ParseValue(val string) (newVal string) +func SetEnvs(mp map[string]string) // source at envutil/get.go func Getenv(name string, def ...string) string func GetInt(name string, def ...int) int @@ -264,6 +405,7 @@ func HasShellEnv(shell string) bool func IsSupportColor() bool func IsSupport256Color() bool func IsSupportTrueColor() bool +func IsGithubActions() bool ``` #### ENV Util Usage @@ -283,6 +425,7 @@ envutil.GetBool("APP_DEBUG", true) envutil.ParseValue("${ENV_NAME | defValue}") ``` + ### Errorx > Package `github.com/gookit/goutil/errorx` @@ -291,40 +434,43 @@ envutil.ParseValue("${ENV_NAME | defValue}") > 在打印 error 时会额外附带调用栈信息, 方便记录日志和查找问题。 + ```go +// source at errorx/errors.go +func NewR(code int, msg string) ErrorR +func Fail(code int, msg string) ErrorR +func Suc(msg string) ErrorR // source at errorx/errorx.go func New(msg string) error -func Newf(tpl string, vars ...interface{}) error -func Errorf(tpl string, vars ...interface{}) error +func Newf(tpl string, vars ...any) error +func Errorf(tpl string, vars ...any) error func With(err error, msg string) error -func Withf(err error, tpl string, vars ...interface{}) error +func Withf(err error, tpl string, vars ...any) error func WithPrev(err error, msg string) error -func WithPrevf(err error, tpl string, vars ...interface{}) error +func WithPrevf(err error, tpl string, vars ...any) error func WithStack(err error) error func Traced(err error) error func Stacked(err error) error func WithOptions(msg string, fns ...func(opt *ErrStackOpt)) error func Wrap(err error, msg string) error -func Wrapf(err error, tpl string, vars ...interface{}) error -// source at errorx/reply.go -func NewR(code int, msg string) ErrorR -func Fail(code int, msg string) ErrorR -func Suc(msg string) ErrorR +func Wrapf(err error, tpl string, vars ...any) error // source at errorx/stack.go func FuncForPC(pc uintptr) *Func +func ResetStdOpt() func Config(fns ...func(opt *ErrStackOpt)) func SkipDepth(skipDepth int) func(opt *ErrStackOpt) func TraceDepth(traceDepth int) func(opt *ErrStackOpt) // source at errorx/util.go func Raw(msg string) error -func Rawf(tpl string, vars ...interface{}) error +func Rawf(tpl string, vars ...any) error func Cause(err error) error func Unwrap(err error) error func Previous(err error) error { return Unwrap(err) } +func ToErrorX(err error) (ex *ErrorX, ok bool) func Has(err, target error) bool func Is(err, target error) bool -func To(err error, target interface{}) bool -func As(err error, target interface{}) bool +func To(err error, target any) bool +func As(err error, target any) bool ``` #### Errorx 使用示例 @@ -402,21 +548,25 @@ runtime.goexit() /usr/local/Cellar/go/1.18/libexec/src/runtime/asm_amd64.s:1571 ``` + ### Formatting > Package `github.com/gookit/goutil/fmtutil` ```go // source at fmtutil/format.go -func DataSize(bytes uint64) string -func PrettyJSON(v interface{}) (string, error) +func DataSize(size uint64) string +func SizeToString(size uint64) string { return DataSize(size) } +func StringToByte(sizeStr string) uint64 { return ParseByte(sizeStr) } +func ParseByte(sizeStr string) uint64 +func PrettyJSON(v any) (string, error) func StringsToInts(ss []string) (ints []int, err error) -func ArgsWithSpaces(args []interface{}) (message string) +func ArgsWithSpaces(args []any) (message string) // source at fmtutil/time.go func HowLongAgo(sec int64) string ``` -### FileSystem +### File System > Package `github.com/gookit/goutil/fsutil` @@ -429,52 +579,49 @@ func IsFile(path string) bool func IsAbsPath(aPath string) bool func IsImageFile(path string) bool func IsZipFile(filepath string) bool -// source at fsutil/finder.go -func EmptyFinder() *FileFinder -func NewFinder(dirPaths []string, filePaths ...string) *FileFinder -func ExtFilterFunc(exts []string, include bool) FileFilterFunc -func SuffixFilterFunc(suffixes []string, include bool) FileFilterFunc -func PathNameFilterFunc(names []string, include bool) FileFilterFunc -func DotFileFilterFunc(include bool) FileFilterFunc -func ModTimeFilterFunc(limitSec int, op rune, include bool) FileFilterFunc -func GlobFilterFunc(patterns []string, include bool) FileFilterFunc -func RegexFilterFunc(pattern string, include bool) FileFilterFunc -func DotDirFilterFunc(include bool) DirFilterFunc -func DirNameFilterFunc(names []string, include bool) DirFilterFunc // source at fsutil/fsutil.go -func DiscardReader(src io.Reader) func OSTempFile(pattern string) (*os.File, error) func TempFile(dir, pattern string) (*os.File, error) func OSTempDir(pattern string) (string, error) func TempDir(dir, pattern string) (string, error) func MimeType(path string) (mime string) func ReaderMimeType(r io.Reader) (mime string) -func GlobWithFunc(pattern string, fn func(filePath string) error) (err error) // source at fsutil/info.go func Dir(fpath string) string func PathName(fpath string) string func Name(fpath string) string func FileExt(fpath string) string func Suffix(fpath string) string -func ExpandPath(path string) string +func Expand(pathStr string) string +func ExpandPath(pathStr string) string func Realpath(pathStr string) string +func SplitPath(pathStr string) (dir, name string) +func GlobWithFunc(pattern string, fn func(filePath string) error) (err error) +func FindInDir(dir string, handleFn HandleFunc, filters ...FilterFunc) (e error) // source at fsutil/operate.go func Mkdir(dirPath string, perm os.FileMode) error func MkParentDir(fpath string) error +func DiscardReader(src io.Reader) func MustReadFile(filePath string) []byte func MustReadReader(r io.Reader) []byte +func GetContents(in any) []byte func ReadExistFile(filePath string) []byte func OpenFile(filepath string, flag int, perm os.FileMode) (*os.File, error) -func QuickOpenFile(filepath string) (*os.File, error) -func CreateFile(fpath string, filePerm, dirPerm os.FileMode) (*os.File, error) +func QuickOpenFile(filepath string, fileFlag ...int) (*os.File, error) +func OpenReadFile(filepath string) (*os.File, error) +func CreateFile(fpath string, filePerm, dirPerm os.FileMode, fileFlag ...int) (*os.File, error) func MustCreateFile(filePath string, filePerm, dirPerm os.FileMode) *os.File -func CopyFile(src string, dst string) error -func MustCopyFile(src string, dst string) -func Remove(fpath string) error -func MustRemove(fpath string) -func QuietRemove(fpath string) -func DeleteIfExist(fpath string) error -func DeleteIfFileExist(fpath string) error +func PutContents(filePath string, data any, fileFlag ...int) (int, error) +func WriteFile(filePath string, data any, perm os.FileMode, fileFlag ...int) error +func CopyFile(srcPath, dstPath string) error +func MustCopyFile(srcPath, dstPath string) +func Remove(fPath string) error +func MustRemove(fPath string) +func QuietRemove(fPath string) { _ = os.Remove(fPath) } +func RmIfExist(fPath string) error { return DeleteIfExist(fPath) } +func DeleteIfExist(fPath string) error +func RmFileIfExist(fPath string) error { return DeleteIfFileExist(fPath) } +func DeleteIfFileExist(fPath string) error func Unzip(archive, targetDir string) (err error) ``` @@ -489,11 +636,11 @@ import ( "fmt" "os" - "github.com/gookit/goutil/fsutil" + "github.com/gookit/goutil/fsutil/finder" ) func main() { - f := fsutil.EmptyFinder() + f := finder.EmptyFinder() f. AddDir("./testdata"). @@ -505,7 +652,7 @@ func main() { fmt.Println(filePath) }) - fsutil.NewFinder([]string{"./testdata"}). + finder.NewFinder([]string{"./testdata"}). AddFile("finder.go"). NoDotDir(). EachStat(func(fi os.FileInfo, filePath string) { @@ -514,22 +661,24 @@ func main() { } ``` -### JSON + +### JSON Utils > Package `github.com/gookit/goutil/jsonutil` ```go // source at jsonutil/jsonutil.go -func WriteFile(filePath string, data interface{}) error -func ReadFile(filePath string, v interface{}) error -func Pretty(v interface{}) (string, error) -func Encode(v interface{}) ([]byte, error) -func EncodePretty(v interface{}) ([]byte, error) -func EncodeToWriter(v interface{}, w io.Writer) error -func EncodeUnescapeHTML(v interface{}) ([]byte, error) -func Decode(bts []byte, ptr interface{}) error -func DecodeString(str string, ptr interface{}) error -func DecodeReader(r io.Reader, ptr interface{}) error +func WriteFile(filePath string, data any) error +func ReadFile(filePath string, v any) error +func Pretty(v any) (string, error) +func Encode(v any) ([]byte, error) +func EncodePretty(v any) ([]byte, error) +func EncodeToWriter(v any, w io.Writer) error +func EncodeUnescapeHTML(v any) ([]byte, error) +func Decode(bts []byte, ptr any) error +func DecodeString(str string, ptr any) error +func DecodeReader(r io.Reader, ptr any) error +func Mapping(src, dst any) error func StripComments(src string) string ``` @@ -538,16 +687,34 @@ func StripComments(src string) string > Package `github.com/gookit/goutil/maputil` ```go +// source at maputil/check.go +func HasKey(mp, key any) (ok bool) +func HasAllKeys(mp any, keys ...any) (ok bool, noKey any) // source at maputil/convert.go func KeyToLower(src map[string]string) map[string]string -func ToStringMap(src map[string]interface{}) map[string]string -func HttpQueryString(data map[string]interface{}) string -func ToString(mp map[string]interface{}) string +func ToStringMap(src map[string]any) map[string]string +func HttpQueryString(data map[string]any) string +func ToString(mp map[string]any) string +func ToString2(mp any) string +func FormatIndent(mp any, indent string) string +func Flatten(mp map[string]any) map[string]any +func FlatWithFunc(mp map[string]any, fn reflects.FlatFunc) +// source at maputil/format.go +func NewFormatter(mp any) *MapFormatter +// source at maputil/get.go +func DeepGet(mp map[string]any, path string) (val any) +func QuietGet(mp map[string]any, path string) (val any) +func GetByPath(path string, mp map[string]any) (val any, ok bool) +func Keys(mp any) (keys []string) +func Values(mp any) (values []any) // source at maputil/maputil.go +func MergeSMap(src, dst map[string]string, ignoreCase bool) map[string]string func MergeStringMap(src, dst map[string]string, ignoreCase bool) map[string]string -func GetByPath(key string, mp map[string]interface{}) (val interface{}, ok bool) -func Keys(mp interface{}) (keys []string) -func Values(mp interface{}) (values []interface{}) +func MakeByPath(path string, val any) (mp map[string]any) +func MakeByKeys(keys []string, val any) (mp map[string]any) +// source at maputil/setval.go +func SetByPath(mp *map[string]any, path string, val any) error +func SetByKeys(mp *map[string]any, keys []string, val any) (err error) ``` ### Math/Number @@ -555,26 +722,47 @@ func Values(mp interface{}) (values []interface{}) > Package `github.com/gookit/goutil/mathutil` ```go +// source at mathutil/check.go +func Compare(srcVal, dstVal any, op string) (ok bool) +func CompInt64(srcI64, dstI64 int64, op string) (ok bool) +func CompFloat(srcF64, dstF64 float64, op string) (ok bool) // source at mathutil/convert.go -func Int(in interface{}) (int, error) -func MustInt(in interface{}) int -func IntOrPanic(in interface{}) int -func ToInt(in interface{}) (iVal int, err error) -func Uint(in interface{}) (uint64, error) -func MustUint(in interface{}) uint64 -func ToUint(in interface{}) (u64 uint64, err error) -func Int64(in interface{}) (int64, error) -func MustInt64(in interface{}) int64 -func ToInt64(in interface{}) (i64 int64, err error) -func Float(in interface{}) (float64, error) -func ToFloat(in interface{}) (f64 float64, err error) -func FloatOrPanic(in interface{}) float64 -func MustFloat(in interface{}) float64 -func TryToString(val interface{}, defaultAsErr bool) (str string, err error) -func StringOrPanic(val interface{}) string -func MustString(val interface{}) string -func ToString(val interface{}) (string, error) -func String(val interface{}) string +func Int(in any) (int, error) +func QuietInt(in any) int +func MustInt(in any) int +func IntOrPanic(in any) int +func IntOrErr(in any) (iVal int, err error) +func ToInt(in any) (iVal int, err error) +func StrInt(s string) int +func Uint(in any) (uint64, error) +func QuietUint(in any) uint64 +func MustUint(in any) uint64 +func UintOrErr(in any) (uint64, error) +func ToUint(in any) (u64 uint64, err error) +func Int64(in any) (int64, error) +func QuietInt64(in any) int64 +func MustInt64(in any) int64 +func Int64OrErr(in any) (int64, error) +func ToInt64(in any) (i64 int64, err error) +func QuietFloat(in any) float64 +func FloatOrPanic(in any) float64 +func MustFloat(in any) float64 +func Float(in any) (float64, error) +func FloatOrErr(in any) (float64, error) +func ToFloat(in any) (f64 float64, err error) +func StringOrPanic(val any) string +func MustString(val any) string +func ToString(val any) (string, error) +func StringOrErr(val any) (string, error) +func QuietString(val any) string +func String(val any) string +func TryToString(val any, defaultAsErr bool) (str string, err error) +// source at mathutil/mathutil.go +func MaxFloat(x, y float64) float64 +func MaxInt(x, y int) int +func SwapMaxInt(x, y int) (int, int) +func MaxI64(x, y int64) int64 +func SwapMaxI64(x, y int64) (int64, int64) // source at mathutil/number.go func IsNumeric(c byte) bool func Percent(val, total int) float64 @@ -588,6 +776,57 @@ func RandIntWithSeed(min, max int, seed int64) int func RandomIntWithSeed(min, max int, seed int64) int ``` +### Reflects + +> Package `github.com/gookit/goutil/reflects` + +```go +// source at reflects/check.go +func HasChild(v reflect.Value) bool +func IsNil(v reflect.Value) bool +func IsFunc(val any) bool +func IsEqual(src, dst any) bool +func IsEmpty(v reflect.Value) bool +func IsEmptyValue(v reflect.Value) bool +// source at reflects/conv.go +func BaseTypeVal(v reflect.Value) (value any, err error) +func ValueByType(val any, typ reflect.Type) (rv reflect.Value, err error) +func ValueByKind(val any, kind reflect.Kind) (rv reflect.Value, err error) +func String(rv reflect.Value) string +func ToString(rv reflect.Value) (str string, err error) +func ValToString(rv reflect.Value, defaultAsErr bool) (str string, err error) +// source at reflects/type.go +func ToBaseKind(kind reflect.Kind) BKind +func ToBKind(kind reflect.Kind) BKind +func TypeOf(v any) Type +// source at reflects/util.go +func Elem(v reflect.Value) reflect.Value +func Indirect(v reflect.Value) reflect.Value +func Len(v reflect.Value) int +func SliceSubKind(typ reflect.Type) reflect.Kind +func SetValue(rv reflect.Value, val any) error +func FlatMap(rv reflect.Value, fn FlatFunc) +// source at reflects/value.go +func Wrap(rv reflect.Value) Value +func ValueOf(v any) Value +``` + +### Stdio + +> Package `github.com/gookit/goutil/stdio` + +```go +// source at stdio/ioutil.go +func QuietFprint(w io.Writer, ss ...string) +func QuietFprintf(w io.Writer, tpl string, vs ...any) +func QuietFprintln(w io.Writer, ss ...string) +func QuietWriteString(w io.Writer, ss ...string) +func DiscardReader(src io.Reader) +func MustReadReader(r io.Reader) []byte +// source at stdio/writer.go +func NewWriteWrapper(w io.Writer) *WriteWrapper +``` + ### Standard > Package `github.com/gookit/goutil/stdutil` @@ -596,17 +835,25 @@ func RandomIntWithSeed(min, max int, seed int64) int // source at stdutil/chan.go func WaitCloseSignals(closer io.Closer) error func Go(f func() error) error +func SignalHandler(ctx context.Context, signals ...os.Signal) (execute func() error, interrupt func(error)) // source at stdutil/check.go +func IsNil(v any) bool +func IsEmpty(v any) bool +func IsFunc(val any) bool +func IsEqual(src, dst any) bool +func Contains(data, elem any) bool +func IsContains(data, elem any) bool +func CheckContains(data, elem any) (valid, found bool) func ValueIsEmpty(v reflect.Value) bool func ValueLen(v reflect.Value) int -// source at stdutil/convert.go -func ToString(v interface{}) string -func MustString(v interface{}) string -func TryString(v interface{}) (string, error) -func BaseTypeVal2(v reflect.Value) (value interface{}, err error) -func BaseTypeVal(val interface{}) (value interface{}, err error) +// source at stdutil/conv.go +func ToString(v any) string +func MustString(v any) string +func TryString(v any) (string, error) +func BaseTypeVal(val any) (value any, err error) +func BaseTypeVal2(v reflect.Value) (value any, err error) // source at stdutil/gofunc.go -func FuncName(fn interface{}) string +func FuncName(fn any) string func CutFuncName(fullFcName string) (pkgPath, shortFnName string) func PkgName(fullFcName string) string // source at stdutil/stack.go @@ -615,93 +862,136 @@ func GetCallerInfo(skip int) string func SimpleCallersInfo(skip, num int) []string func GetCallersInfo(skip, max int) []string // source at stdutil/stdutil.go +func DiscardE(_ error) {} func PanicIfErr(err error) func PanicIf(err error) -func Panicf(format string, v ...interface{}) +func Panicf(format string, v ...any) +func GoVersion() string ``` -### Struct +### Structs > Package `github.com/gookit/goutil/structs` ```go // source at structs/alias.go func NewAliases(checker func(alias string)) *Aliases +// source at structs/convert.go +func ToMap(st any, optFns ...MapOptFunc) map[string]any +func MustToMap(st any, optFns ...MapOptFunc) map[string]any +func TryToMap(st any, optFns ...MapOptFunc) (map[string]any, error) +func StructToMap(st any, optFns ...MapOptFunc) (map[string]any, error) // source at structs/data.go -func NewMapData() *MapDataStore +func NewData() *Data +// source at structs/setval.go +func InitDefaults(ptr any, optFns ...InitOptFunc) error +func SetValues(ptr any, data map[string]any, optFns ...SetOptFunc) error // source at structs/structs.go -func ToMap(st interface{}) map[string]interface{} -func TryToMap(st interface{}) (map[string]interface{}, error) -func MustToMap(st interface{}) map[string]interface{} +func MapStruct(srcSt, dstSt any) // source at structs/tags.go -func ParseTags(v interface{}) error -func ParseReflectTags(v reflect.Value) error -func ParseTagValue(str string) maputil.SMap -func ParseTagValueINI(field, str string) (mp maputil.SMap, err error) +func ParseTags(st any, tagNames []string) (map[string]maputil.SMap, error) +func ParseReflectTags(rt reflect.Type, tagNames []string) (map[string]maputil.SMap, error) +func NewTagParser(tagNames ...string) *TagParser +func ParseTagValueDefault(field, tagVal string) (mp maputil.SMap, err error) +func ParseTagValueDefine(sep string, defines []string) TagValFunc +func ParseTagValueNamed(field, tagVal string, keys ...string) (mp maputil.SMap, err error) +// source at structs/value.go +func NewValue(val any) *Value ``` -### String +### Strings > Package `github.com/gookit/goutil/strutil` ```go // source at strutil/bytes.go +func NewBuffer() *Buffer func NewByteChanPool(maxSize int, width int, capWidth int) *ByteChanPool // source at strutil/check.go -func IsNumeric(c byte) bool +func NoCaseEq(s, t string) bool +func IsNumChar(c byte) bool +func IsNumeric(s string) bool func IsAlphabet(char uint8) bool func IsAlphaNum(c uint8) bool func StrPos(s, sub string) int func BytePos(s string, bt byte) int -func RunePos(s string, ru rune) int func HasOneSub(s string, subs []string) bool func HasAllSubs(s string, subs []string) bool -func IsStartsOf(s string, subs []string) bool -func HasOnePrefix(s string, subs []string) bool -func IsStartOf(s, sub string) bool -func IsEndOf(s, sub string) bool -func Len(s string) int -func Utf8len(s string) int -func ValidUtf8String(s string) bool +func IsStartsOf(s string, prefixes []string) bool +func HasOnePrefix(s string, prefixes []string) bool +func HasPrefix(s string, prefix string) bool { return strings.HasPrefix(s, prefix) } +func IsStartOf(s, prefix string) bool { return strings.HasPrefix(s, prefix) } +func HasSuffix(s string, suffix string) bool { return strings.HasSuffix(s, suffix) } +func IsEndOf(s, suffix string) bool { return strings.HasSuffix(s, suffix) } +func IsValidUtf8(s string) bool { return utf8.ValidString(s) } func IsSpace(c byte) bool -func IsSpaceRune(r rune) bool -func IsEmpty(s string) bool +func IsEmpty(s string) bool { return len(s) == 0 } func IsBlank(s string) bool func IsNotBlank(s string) bool func IsBlankBytes(bs []byte) bool func IsSymbol(r rune) bool +func IsVersion(s string) bool +func Compare(s1, s2, op string) bool +func VersionCompare(v1, v2, op string) bool // source at strutil/convert.go -func Join(sep string, ss ...string) string -func Implode(sep string, ss ...string) string -func String(val interface{}) (string, error) -func MustString(in interface{}) string -func ToString(val interface{}) (string, error) -func AnyToString(val interface{}, defaultAsErr bool) (str string, err error) +func Quote(s string) string { return strconv.Quote(s) } +func Unquote(s string) string +func Join(sep string, ss ...string) string { return strings.Join(ss, sep) } +func JoinList(sep string, ss []string) string { return strings.Join(ss, sep) } +func Implode(sep string, ss ...string) string { return strings.Join(ss, sep) } +func String(val any) (string, error) +func QuietString(in any) string +func MustString(in any) string +func StringOrErr(val any) (string, error) +func ToString(val any) (string, error) +func AnyToString(val any, defaultAsErr bool) (str string, err error) func Byte2str(b []byte) string func Byte2string(b []byte) string func ToBytes(s string) (b []byte) func ToBool(s string) (bool, error) +func QuietBool(s string) bool func MustBool(s string) bool func Bool(s string) (bool, error) func Int(s string) (int, error) func ToInt(s string) (int, error) +func QuietInt(s string) int func MustInt(s string) int func IntOrPanic(s string) int +func Int64(s string) int64 +func QuietInt64(s string) int64 +func ToInt64(s string) (int64, error) +func Int64OrErr(s string) (int64, error) +func MustInt64(s string) int64 +func Int64OrPanic(s string) int64 func Ints(s string, sep ...string) []int -func ToInts(s string, sep ...string) ([]int, error) +func ToInts(s string, sep ...string) ([]int, error) { return ToIntSlice(s, sep...) } func ToIntSlice(s string, sep ...string) (ints []int, err error) -func ToArray(s string, sep ...string) []string -func Strings(s string, sep ...string) []string -func ToStrings(s string, sep ...string) []string +func ToArray(s string, sep ...string) []string { return ToSlice(s, sep...) } +func Strings(s string, sep ...string) []string { return ToSlice(s, sep...) } +func ToStrings(s string, sep ...string) []string { return ToSlice(s, sep...) } func ToSlice(s string, sep ...string) []string func ToOSArgs(s string) []string func MustToTime(s string, layouts ...string) time.Time func ToTime(s string, layouts ...string) (t time.Time, err error) +func ToDuration(s string) (time.Duration, error) // source at strutil/encode.go -func Base64(str string) string -func B64Encode(str string) string +func EscapeJS(s string) string +func EscapeHTML(s string) string +func AddSlashes(s string) string +func StripSlashes(s string) string +func Md5(src any) string +func MD5(src any) string { return Md5(src) } +func GenMd5(src any) string { return Md5(src) } +func Md5Bytes(src any) []byte func URLEncode(s string) string func URLDecode(s string) string +func B32Encode(str string) string +func B32Decode(str string) string +func B64Encode(str string) string +func B64EncodeBytes(src []byte) []byte +func B64Decode(str string) string +func B64DecodeBytes(str string) []byte +func Encoding(base int, typ BaseType) BaseEncoder // source at strutil/filter.go func Trim(s string, cutSet ...string) string func Ltrim(s string, cutSet ...string) string { return TrimLeft(s, cutSet...) } @@ -712,6 +1002,7 @@ func RTrim(s string, cutSet ...string) string { return TrimRight(s, cutSet...) } func TrimRight(s string, cutSet ...string) string func FilterEmail(s string) string // source at strutil/format.go +func Title(s string) string { return strings.ToTitle(s) } func Lower(s string) string { return strings.ToLower(s) } func Lowercase(s string) string { return strings.ToLower(s) } func Upper(s string) string { return strings.ToUpper(s) } @@ -719,27 +1010,61 @@ func Uppercase(s string) string { return strings.ToUpper(s) } func UpperWord(s string) string func LowerFirst(s string) string func UpperFirst(s string) string -func Snake(s string, sep ...string) string func SnakeCase(s string, sep ...string) string -func Camel(s string, sep ...string) string +func Camel(s string, sep ...string) string { return CamelCase(s, sep...) } func CamelCase(s string, sep ...string) string +func Indent(s, prefix string) string +func IndentBytes(b, prefix []byte) []byte // source at strutil/id.go func MicroTimeID() string func MicroTimeHexID() string +// source at strutil/padding.go +func Padding(s, pad string, length int, pos PosFlag) string +func PadLeft(s, pad string, length int) string +func PadRight(s, pad string, length int) string +func Resize(s string, length int, align PosFlag) string +func PadBytes(bs []byte, pad byte, length int, pos PosFlag) []byte +func PadBytesLeft(bs []byte, pad byte, length int) []byte +func PadBytesRight(bs []byte, pad byte, length int) []byte +func PadRunes(rs []rune, pad rune, length int, pos PosFlag) []rune +func PadRunesLeft(rs []rune, pad rune, length int) []rune +func PadRunesRight(rs []rune, pad rune, length int) []rune +func Repeat(s string, times int) string +func RepeatRune(char rune, times int) []rune +func RepeatBytes(char byte, times int) []byte // source at strutil/random.go -func Md5(src interface{}) string -func GenMd5(src interface{}) string func RandomChars(ln int) string func RandomCharsV2(ln int) string func RandomCharsV3(ln int) string func RandomBytes(length int) ([]byte, error) func RandomString(length int) (string, error) +// source at strutil/runes.go +func RuneIsWord(c rune) bool +func RuneIsLower(c rune) bool +func RuneIsUpper(c rune) bool +func RunePos(s string, ru rune) int { return strings.IndexRune(s, ru) } +func IsSpaceRune(r rune) bool +func Utf8Len(s string) int { return utf8.RuneCountInString(s) } +func Utf8len(s string) int { return utf8.RuneCountInString(s) } +func RuneCount(s string) int { return len([]rune(s)) } +func RuneWidth(r rune) int +func TextWidth(s string) int { return Utf8Width(s) } +func Utf8Width(s string) int { return RunesWidth([]rune(s)) } +func RunesWidth(rs []rune) (w int) +func TextTruncate(s string, w int, tail string) string { return Utf8Truncate(s, w, tail) } +func Utf8Truncate(s string, w int, tail string) string +func TextSplit(s string, w int) []string { return Utf8Split(s, w) } +func Utf8Split(s string, w int) []string +func TextWrap(s string, w int) string { return WidthWrap(s, w) } +func WidthWrap(s string, w int) string +func WordWrap(s string, w int) string // source at strutil/similar_find.go func NewComparator(src, dst string) *SimilarComparator func Similarity(s, t string, rate float32) (float32, bool) // source at strutil/split.go func Cut(s, sep string) (before string, after string, found bool) func MustCut(s, sep string) (before string, after string) +func TrimCut(s, sep string) (string, string) func SplitValid(s, sep string) (ss []string) { return Split(s, sep) } func Split(s, sep string) (ss []string) func SplitNValid(s, sep string, n int) (ss []string) { return SplitN(s, sep, n) } @@ -747,17 +1072,13 @@ func SplitN(s, sep string, n int) (ss []string) func SplitTrimmed(s, sep string) (ss []string) func SplitNTrimmed(s, sep string, n int) (ss []string) func Substr(s string, pos, length int) string +func SplitInlineComment(val string) (string, string) // source at strutil/strutil.go -func Padding(s, pad string, length int, pos uint8) string -func PadLeft(s, pad string, length int) string -func PadRight(s, pad string, length int) string -func Repeat(s string, times int) string -func RepeatRune(char rune, times int) (chars []rune) -func RepeatBytes(char byte, times int) (chars []byte) func Replaces(str string, pairs map[string]string) string -func PrettyJSON(v interface{}) (string, error) -func RenderTemplate(input string, data interface{}, fns template.FuncMap, isFile ...bool) string -func RenderText(input string, data interface{}, fns template.FuncMap, isFile ...bool) string +func PrettyJSON(v any) (string, error) +func RenderTemplate(input string, data any, fns template.FuncMap, isFile ...bool) string +func RenderText(input string, data any, fns template.FuncMap, isFile ...bool) string +func WrapTag(s, tag string) string ``` ### System @@ -766,39 +1087,51 @@ func RenderText(input string, data interface{}, fns template.FuncMap, isFile ... ```go // source at sysutil/exec.go +func NewCmd(bin string, args ...string) *cmdr.Cmd +func FlushExec(bin string, args ...string) error func QuickExec(cmdLine string, workDir ...string) (string, error) func ExecLine(cmdLine string, workDir ...string) (string, error) func ExecCmd(binName string, args []string, workDir ...string) (string, error) func ShellExec(cmdLine string, shells ...string) (string, error) -func FindExecutable(binName string) (string, error) -func Executable(binName string) (string, error) -func HasExecutable(binName string) bool +// source at sysutil/stack.go +func CallersInfos(skip, num int, filters ...func(file string, fc *runtime.Func) bool) []*CallerInfo // source at sysutil/sysenv.go -func Hostname() string -func IsWin() bool -func IsWindows() bool -func IsMac() bool -func IsLinux() bool func IsMSys() bool func IsConsole(out io.Writer) bool func IsTerminal(fd uintptr) bool func StdIsTerminal() bool +func Hostname() string func CurrentShell(onlyName bool) (path string) func HasShellEnv(shell string) bool func IsShellSpecialVar(c uint8) bool +func EnvPaths() []string +func FindExecutable(binName string) (string, error) +func Executable(binName string) (string, error) +func HasExecutable(binName string) bool +func SearchPath(keywords string) []string +// source at sysutil/sysgo.go +func GoVersion() string +func ParseGoVersion(line string) (*GoInfo, error) +func OsGoInfo() (*GoInfo, error) // source at sysutil/sysutil.go func Workdir() string func BinDir() string func BinFile() string // source at sysutil/sysutil_nonwin.go +func IsWin() bool +func IsWindows() bool +func IsMac() bool +func IsDarwin() bool +func IsLinux() bool func Kill(pid int, signal syscall.Signal) error func ProcessExists(pid int) bool +func OpenBrowser(URL string) error // source at sysutil/user.go func MustFindUser(uname string) *user.User func LoginUser() *user.User func CurrentUser() *user.User -func UserHomeDir() string func UHomeDir() string +func UserHomeDir() string func HomeDir() string func UserDir(subPath string) string func UserCacheDir(subPath string) string @@ -814,6 +1147,16 @@ func ChangeUserUidGid(newUid int, newGid int) (err error) > Package `github.com/gookit/goutil/testutil` ```go +// source at testutil/buffer.go +func NewBuffer() *Buffer +// source at testutil/envmock.go +func MockEnvValue(key, val string, fn func(nv string)) +func MockEnvValues(kvMap map[string]string, fn func()) +func MockOsEnvByText(envText string, fn func()) +func MockOsEnv(mp map[string]string, fn func()) +func ClearOSEnv() { os.Clearenv() } +func RevertOSEnv() +func MockCleanOsEnv(mp map[string]string, fn func()) // source at testutil/httpmock.go func NewHttpRequest(method, path string, data *MD) *http.Request func MockRequest(h http.Handler, method, path string, data *MD) *httptest.ResponseRecorder @@ -821,33 +1164,34 @@ func MockRequest(h http.Handler, method, path string, data *MD) *httptest.Respon func DiscardStdout() error func ReadOutput() (s string) func RewriteStdout() -func RestoreStdout() (s string) +func RestoreStdout(printData ...bool) (s string) func RewriteStderr() -func RestoreStderr() (s string) -func MockEnvValue(key, val string, fn func(nv string)) -func MockEnvValues(kvMap map[string]string, fn func()) -func MockOsEnvByText(envText string, fn func()) -func MockOsEnv(mp map[string]string, fn func()) +func RestoreStderr(printData ...bool) (s string) +// source at testutil/writer.go +func NewTestWriter() *TestWriter ``` ### Timex > Package `github.com/gookit/goutil/timex` +Provides an enhanced time.Time implementation, and add more commonly used functional methods. ```go +// source at timex/template.go +func ToLayout(template string) string // source at timex/timex.go -func Now() *TimeX -func New(t time.Time) *TimeX -func Wrap(t time.Time) *TimeX -func FromTime(t time.Time) *TimeX -func Local() *TimeX -func FromUnix(sec int64) *TimeX -func FromDate(s string, template ...string) (*TimeX, error) -func FromString(s string, layouts ...string) (*TimeX, error) -func LocalByName(tzName string) *TimeX -func SetLocalByName(tzName string) error +func Now() *Time +func New(t time.Time) *Time +func Wrap(t time.Time) *Time +func FromTime(t time.Time) *Time +func Local() *Time +func FromUnix(sec int64) *Time +func FromDate(s string, template ...string) (*Time, error) +func FromString(s string, layouts ...string) (*Time, error) +func LocalByName(tzName string) *Time // source at timex/util.go func NowUnix() int64 +func SetLocalByName(tzName string) error func Format(t time.Time) string func FormatBy(t time.Time, layout string) string func Date(t time.Time, template string) string @@ -860,6 +1204,8 @@ func NowAddDay(day int) time.Time func NowAddHour(hour int) time.Time func NowAddMinutes(minutes int) time.Time func NowAddSeconds(seconds int) time.Time +func NowHourStart() time.Time +func NowHourEnd() time.Time func AddDay(t time.Time, day int) time.Time func AddHour(t time.Time, hour int) time.Time func AddMinutes(t time.Time, minutes int) time.Time @@ -868,11 +1214,10 @@ func HourStart(t time.Time) time.Time func HourEnd(t time.Time) time.Time func DayStart(t time.Time) time.Time func DayEnd(t time.Time) time.Time -func NowHourStart() time.Time func TodayStart() time.Time func TodayEnd() time.Time func HowLongAgo(sec int64) string -func ToLayout(template string) string +func ToDuration(s string) (time.Duration, error) ``` #### Timex Usage @@ -897,7 +1242,7 @@ tx, err := timex.FromString("2022-04-20 19:40:34") // custom set the datetime layout tx, err := timex.FromString("2022-04-20 19:40:34", "2006-01-02 15:04:05") // use date template as layout -tx, err := timex.FromDate("2022-04-20 19:40:34", "Y-M-D H:I:S") +tx, err := timex.FromDate("2022-04-20 19:40:34", "Y-m-d H:I:S") ``` **Use timex instance** @@ -912,10 +1257,10 @@ change time: tx.Yesterday() tx.Tomorrow() -tx.DayStart() // get time at Y-M-D 00:00:00 -tx.DayEnd() // get time at Y-M-D 23:59:59 -tx.HourStart() // get time at Y-M-D H:00:00 -tx.HourEnd() // get time at Y-M-D H:59:59 +tx.DayStart() // get time at Y-m-d 00:00:00 +tx.DayEnd() // get time at Y-m-d 23:59:59 +tx.HourStart() // get time at Y-m-d H:00:00 +tx.HourEnd() // get time at Y-m-d H:59:59 tx.AddDay(2) tx.AddHour(1) @@ -947,31 +1292,40 @@ t := NowAddSeconds(180) // from now add 180 seconds **Convert time to date by template** +Template Chars: + ```text -Template Vars: Y,y - year Y - year 2006 y - year 06 - M,m - month 01 - D,d - day 02 - H,h - hour 15 - I,i - minute 04 - S,s - second 05 + m - month 01-12 + d - day 01-31 + H,h - hour + H - hour 00-23 + h - hour 01-12 + I,i - minute + I - minute 00-59 + i - minute 0-59 + S,s - second + S - second 00-59 + s - second 0-59 ``` +> More, please see [char map](./timex/template.go) + Examples, use timex: ```go -now := timex.Now() -date := now.DateFormat("Y-M-D H:i:s") // Output: 2022-04-20 19:40:34 -date = now.DateFormat("y-M-D H:i:s") // Output: 22-04-20 19:40:34 +tx := timex.Now() +date := tx.DateFormat("Y-m-d H:I:S") // Output: 2022-04-20 19:09:03 +date = tx.DateFormat("y-m-d h:i:s") // Output: 22-04-20 07:9:3 ``` Format time.Time: ```go -now := time.Now() -date := timex.DateFormat(now, "Y-M-D H:i:s") // Output: 2022-04-20 19:40:34 +tx := time.Now() +date := timex.DateFormat(tx, "Y-m-d H:I:S") // Output: 2022-04-20 19:40:34 ``` More usage: @@ -980,31 +1334,43 @@ More usage: ts := timex.NowUnix() // current unix timestamp date := FormatUnix(ts, "2006-01-02 15:04:05") // Get: 2022-04-20 19:40:34 -date := FormatUnixByTpl(ts, "Y-M-D H:I:S") // Get: 2022-04-20 19:40:34 +date := FormatUnixByTpl(ts, "Y-m-d H:I:S") // Get: 2022-04-20 19:40:34 ``` + ## Code Check & Testing ```bash gofmt -w -l ./ golint ./... -go test ./... + +# testing +go test -v ./... +go test -v -run ^TestErr$ +go test -v -run ^TestErr$ ./testutil/assert/... ``` +## Related + +- https://github.com/duke-git/lancet +- https://github.com/samber/lo +- https://github.com/zyedidia/generic +- https://github.com/thoas/go-funk + ## Gookit packages - - [gookit/ini](https://github.com/gookit/ini) Go config management, use INI files - - [gookit/rux](https://github.com/gookit/rux) Simple and fast request router for golang HTTP - - [gookit/gcli](https://github.com/gookit/gcli) Build CLI application, tool library, running CLI commands - - [gookit/slog](https://github.com/gookit/slog) Lightweight, easy to extend, configurable logging library written in Go - - [gookit/color](https://github.com/gookit/color) A command-line color library with true color support, universal API methods and Windows support - - [gookit/event](https://github.com/gookit/event) Lightweight event manager and dispatcher implements by Go - - [gookit/cache](https://github.com/gookit/cache) Generic cache use and cache manager for golang. support File, Memory, Redis, Memcached. - - [gookit/config](https://github.com/gookit/config) Go config management. support JSON, YAML, TOML, INI, HCL, ENV and Flags - - [gookit/filter](https://github.com/gookit/filter) Provide filtering, sanitizing, and conversion of golang data - - [gookit/validate](https://github.com/gookit/validate) Use for data validation and filtering. support Map, Struct, Form data - - [gookit/goutil](https://github.com/gookit/goutil) Some utils for the Go: string, array/slice, map, format, cli, env, filesystem, test and more - - More, please see https://github.com/gookit +- [gookit/ini](https://github.com/gookit/ini) Go config management, use INI files +- [gookit/rux](https://github.com/gookit/rux) Simple and fast request router for golang HTTP +- [gookit/gcli](https://github.com/gookit/gcli) Build CLI application, tool library, running CLI commands +- [gookit/slog](https://github.com/gookit/slog) Lightweight, easy to extend, configurable logging library written in Go +- [gookit/color](https://github.com/gookit/color) A command-line color library with true color support, universal API methods and Windows support +- [gookit/event](https://github.com/gookit/event) Lightweight event manager and dispatcher implements by Go +- [gookit/cache](https://github.com/gookit/cache) Generic cache use and cache manager for golang. support File, Memory, Redis, Memcached. +- [gookit/config](https://github.com/gookit/config) Go config management. support JSON, YAML, TOML, INI, HCL, ENV and Flags +- [gookit/filter](https://github.com/gookit/filter) Provide filtering, sanitizing, and conversion of golang data +- [gookit/validate](https://github.com/gookit/validate) Use for data validation and filtering. support Map, Struct, Form data +- [gookit/goutil](https://github.com/gookit/goutil) Some utils for the Go: string, array/slice, map, format, cli, env, filesystem, test and more +- More, please see https://github.com/gookit ## License diff --git a/vendor/github.com/gookit/goutil/arrutil/README.md b/vendor/github.com/gookit/goutil/arrutil/README.md new file mode 100644 index 00000000..9f9351ce --- /dev/null +++ b/vendor/github.com/gookit/goutil/arrutil/README.md @@ -0,0 +1,83 @@ +# Array/Slice Utils + +## Install + +```shell +go get github.com/gookit/goutil/arrutil +``` + +## Go docs + +- [Go docs](https://pkg.go.dev/github.com/gookit/goutil/arrutil) + +## Functions API + +```go +func AnyToString(arr any) string +func CloneSlice(data any) interface{} +func Contains(arr, val any) bool +func ExceptWhile(data any, fn Predicate) interface{} +func Excepts(first, second any, fn Comparer) interface{} +func Find(source any, fn Predicate) (interface{}, error) +func FindOrDefault(source any, fn Predicate, defaultValue any) interface{} +func FormatIndent(arr any, indent string) string +func GetRandomOne(arr any) interface{} +func HasValue(arr, val any) bool +func InStrings(elem string, ss []string) bool +func Int64sHas(ints []int64, val int64) bool +func Intersects(first any, second any, fn Comparer) interface{} +func IntsHas(ints []int, val int) bool +func JoinSlice(sep string, arr ...any) string +func JoinStrings(sep string, ss ...string) string +func MakeEmptySlice(itemType reflect.Type) interface{} +func Map[T any, V any](list []T, mapFn func(obj T) (val V, find bool)) []V +func Column[T any, V any](list []T, mapFn func(obj T) (val V, find bool)) []V +func MustToInt64s(arr any) []int64 +func MustToStrings(arr any) []string +func NotContains(arr, val any) bool +func RandomOne(arr any) interface{} +func Reverse(ss []string) +func SliceToInt64s(arr []any) []int64 +func SliceToString(arr ...any) string +func SliceToStrings(arr []any) []string +func StringsFilter(ss []string, filter ...func(s string) bool) []string +func StringsHas(ss []string, val string) bool +func StringsJoin(sep string, ss ...string) string +func StringsMap(ss []string, mapFn func(s string) string) []string +func StringsRemove(ss []string, s string) []string +func StringsToInts(ss []string) (ints []int, err error) +func StringsToSlice(ss []string) []interface{} +func TakeWhile(data any, fn Predicate) interface{} +func ToInt64s(arr any) (ret []int64, err error) +func ToString(arr []any) string +func ToStrings(arr any) (ret []string, err error) +func TrimStrings(ss []string, cutSet ...string) []string +func TwowaySearch(data any, item any, fn Comparer) (int, error) +func Union(first, second any, fn Comparer) interface{} +func Unique(arr any) interface{} +type ArrFormatter struct{ ... } + func NewFormatter(arr any) *ArrFormatter +``` + +## Code Check & Testing + +```bash +gofmt -w -l ./ +golint ./... +``` + +**Testing**: + +```shell +go test -v ./cliutil/... +``` + +**Test limit by regexp**: + +```shell +go test -v -run ^TestSetByKeys ./cliutil/... +``` + +## Refers + +- https://github.com/elliotchance/pie diff --git a/vendor/github.com/gookit/goutil/arrutil/arrutil.go b/vendor/github.com/gookit/goutil/arrutil/arrutil.go new file mode 100644 index 00000000..6f2f1bb5 --- /dev/null +++ b/vendor/github.com/gookit/goutil/arrutil/arrutil.go @@ -0,0 +1,118 @@ +// Package arrutil provides some util functions for array, slice +package arrutil + +import ( + "strings" + + "github.com/gookit/goutil/comdef" + "github.com/gookit/goutil/mathutil" +) + +// Reverse string slice [site user info 0] -> [0 info user site] +func Reverse(ss []string) { + ln := len(ss) + for i := 0; i < ln/2; i++ { + li := ln - i - 1 + ss[i], ss[li] = ss[li], ss[i] + } +} + +// StringsRemove a value form a string slice +func StringsRemove(ss []string, s string) []string { + ns := make([]string, 0, len(ss)) + for _, v := range ss { + if v != s { + ns = append(ns, v) + } + } + return ns +} + +// StringsFilter given strings, default will filter emtpy string. +// +// Usage: +// +// // output: [a, b] +// ss := arrutil.StringsFilter([]string{"a", "", "b", ""}) +func StringsFilter(ss []string, filter ...func(s string) bool) []string { + var fn func(s string) bool + if len(filter) > 0 && filter[0] != nil { + fn = filter[0] + } else { + fn = func(s string) bool { + return s != "" + } + } + + ns := make([]string, 0, len(ss)) + for _, s := range ss { + if fn(s) { + ns = append(ns, s) + } + } + return ns +} + +// StringsMap handle each string item, map to new strings +func StringsMap(ss []string, mapFn func(s string) string) []string { + ns := make([]string, 0, len(ss)) + for _, s := range ss { + ns = append(ns, mapFn(s)) + } + return ns +} + +// TrimStrings trim string slice item. +// +// Usage: +// +// // output: [a, b, c] +// ss := arrutil.TrimStrings([]string{",a", "b.", ",.c,"}, ",.") +func TrimStrings(ss []string, cutSet ...string) []string { + cutSetLn := len(cutSet) + hasCutSet := cutSetLn > 0 && cutSet[0] != "" + + var trimSet string + if hasCutSet { + trimSet = cutSet[0] + } + if cutSetLn > 1 { + trimSet = strings.Join(cutSet, "") + } + + ns := make([]string, 0, len(ss)) + for _, str := range ss { + if hasCutSet { + ns = append(ns, strings.Trim(str, trimSet)) + } else { + ns = append(ns, strings.TrimSpace(str)) + } + } + return ns +} + +// GetRandomOne get random element from an array/slice +func GetRandomOne[T any](arr []T) T { return RandomOne(arr) } + +// RandomOne get random element from an array/slice +func RandomOne[T any](arr []T) T { + if ln := len(arr); ln > 0 { + i := mathutil.RandomInt(0, len(arr)) + return arr[i] + } + panic("cannot get value from nil or empty slice") +} + +// Unique value in the given slice data. +func Unique[T ~string | comdef.XintOrFloat](arr []T) []T { + valMap := make(map[T]struct{}, len(arr)) + uniArr := make([]T, 0, len(arr)) + + for _, t := range arr { + if _, ok := valMap[t]; !ok { + valMap[t] = struct{}{} + uniArr = append(uniArr, t) + } + } + return uniArr +} diff --git a/vendor/github.com/gookit/goutil/arrutil/check.go b/vendor/github.com/gookit/goutil/arrutil/check.go new file mode 100644 index 00000000..93c7d838 --- /dev/null +++ b/vendor/github.com/gookit/goutil/arrutil/check.go @@ -0,0 +1,87 @@ +package arrutil + +import ( + "reflect" + "strings" + + "github.com/gookit/goutil/mathutil" +) + +// IntsHas check the []int contains the given value +func IntsHas(ints []int, val int) bool { + for _, ele := range ints { + if ele == val { + return true + } + } + return false +} + +// Int64sHas check the []int64 contains the given value +func Int64sHas(ints []int64, val int64) bool { + for _, ele := range ints { + if ele == val { + return true + } + } + return false +} + +// InStrings alias of StringsHas() +func InStrings(elem string, ss []string) bool { return StringsHas(ss, elem) } + +// StringsHas check the []string contains the given element +func StringsHas(ss []string, val string) bool { + for _, ele := range ss { + if ele == val { + return true + } + } + return false +} + +// HasValue check array(strings, intXs, uintXs) should be contained the given value(int(X),string). +func HasValue(arr, val any) bool { + return Contains(arr, val) +} + +// Contains check array(strings, intXs, uintXs) should be contained the given value(int(X),string). +func Contains(arr, val any) bool { + if val == nil || arr == nil { + return false + } + + // if is string value + if strVal, ok := val.(string); ok { + if ss, ok := arr.([]string); ok { + return StringsHas(ss, strVal) + } + + rv := reflect.ValueOf(arr) + if rv.Kind() == reflect.Slice || rv.Kind() == reflect.Array { + for i := 0; i < rv.Len(); i++ { + if v, ok := rv.Index(i).Interface().(string); ok && strings.EqualFold(v, strVal) { + return true + } + } + } + + return false + } + + // as int value + intVal, err := mathutil.Int64(val) + if err != nil { + return false + } + + if int64s, err := ToInt64s(arr); err == nil { + return Int64sHas(int64s, intVal) + } + return false +} + +// NotContains check array(strings, ints, uints) should be not contains the given value. +func NotContains(arr, val any) bool { + return !Contains(arr, val) +} diff --git a/vendor/github.com/gookit/goutil/arrutil/collection.go b/vendor/github.com/gookit/goutil/arrutil/collection.go new file mode 100644 index 00000000..f17fa95a --- /dev/null +++ b/vendor/github.com/gookit/goutil/arrutil/collection.go @@ -0,0 +1,354 @@ +package arrutil + +import ( + "errors" + "reflect" +) + +// ErrElementNotFound is the error returned when the element is not found. +const ErrElementNotFound = "element not found" + +// Comparer Function to compare two elements. +type Comparer func(a, b any) int + +// Predicate Function to predicate a struct/value satisfies a condition. +type Predicate func(a any) bool + +var ( + // StringEqualsComparer Comparer for string. It will compare the string by their value. + // returns: 0 if equal, -1 if a != b + StringEqualsComparer Comparer = func(a, b any) int { + typeOfA := reflect.TypeOf(a) + if typeOfA.Kind() == reflect.Ptr { + typeOfA = typeOfA.Elem() + } + + typeOfB := reflect.TypeOf(b) + if typeOfB.Kind() == reflect.Ptr { + typeOfB = typeOfB.Elem() + } + + if typeOfA != typeOfB { + return -1 + } + + strA := "" + strB := "" + + if val, ok := a.(string); ok { + strA = val + } else if val, ok := a.(*string); ok { + strA = *val + } else { + return -1 + } + + if val, ok := b.(string); ok { + strB = val + } else if val, ok := b.(*string); ok { + strB = *val + } else { + return -1 + } + + if strA == strB { + return 0 + } + return -1 + } + + // ReferenceEqualsComparer Comparer for strcut ptr. It will compare the struct by their ptr addr. + // returns: 0 if equal, -1 if a != b + ReferenceEqualsComparer Comparer = func(a, b any) int { + if a == b { + return 0 + } + return -1 + } + + // ElemTypeEqualsComparer Comparer for struct/value. It will compare the struct by their element type (reflect.Type.Elem()). + // returns: 0 if same type, -1 if not. + ElemTypeEqualsComparer Comparer = func(a, b any) int { + at := reflect.TypeOf(a) + bt := reflect.TypeOf(b) + if at.Kind() == reflect.Ptr { + at = at.Elem() + } + + if bt.Kind() == reflect.Ptr { + bt = bt.Elem() + } + + if at == bt { + return 0 + } + return -1 + } +) + +// TwowaySearch Find specialized element in a slice forward and backward in the same time, should be more quickly. +// +// data: the slice to search in. MUST BE A SLICE. +// item: the element to search. +// fn: the comparer function. +// return: the index of the element, or -1 if not found. +func TwowaySearch(data any, item any, fn Comparer) (int, error) { + if data == nil { + return -1, errors.New("collections.TwowaySearch: data is nil") + } + if fn == nil { + return -1, errors.New("collections.TwowaySearch: fn is nil") + } + + dataType := reflect.TypeOf(data) + if dataType.Kind() != reflect.Slice { + return -1, errors.New("collections.TwowaySearch: data is not a slice") + } + + dataVal := reflect.ValueOf(data) + if dataVal.Len() == 0 { + return -1, errors.New("collections.TwowaySearch: data is empty") + } + itemType := dataType.Elem() + if itemType.Kind() == reflect.Ptr { + itemType = itemType.Elem() + } + + if itemType != dataVal.Index(0).Type() { + return -1, errors.New("collections.TwowaySearch: item type is not the same as data type") + } + + forward := 0 + backward := dataVal.Len() - 1 + + for forward <= backward { + forwardVal := dataVal.Index(forward).Interface() + if fn(forwardVal, item) == 0 { + return forward, nil + } + + backwardVal := dataVal.Index(backward).Interface() + if fn(backwardVal, item) == 0 { + return backward, nil + } + + forward++ + backward-- + } + + return -1, errors.New(ErrElementNotFound) +} + +// MakeEmptySlice Create a new slice with the elements of the source that satisfy the predicate. +// +// itemType: the type of the elements in the source. +// returns: the new slice. +func MakeEmptySlice(itemType reflect.Type) any { + ret := reflect.MakeSlice(reflect.SliceOf(itemType), 0, 0).Interface() + return ret +} + +// CloneSlice Clone a slice. +// +// data: the slice to clone. +// returns: the cloned slice. +func CloneSlice(data any) any { + typeOfData := reflect.TypeOf(data) + if typeOfData.Kind() != reflect.Slice { + panic("collections.CloneSlice: data must be a slice") + } + return reflect.AppendSlice(reflect.New(reflect.SliceOf(typeOfData.Elem())).Elem(), reflect.ValueOf(data)).Interface() +} + +// Excepts Produces the set difference of two slice according to a comparer function. +// +// first: the first slice. MUST BE A SLICE. +// second: the second slice. MUST BE A SLICE. +// fn: the comparer function. +// returns: the difference of the two slices. +func Excepts(first, second any, fn Comparer) any { + typeOfFirst := reflect.TypeOf(first) + if typeOfFirst.Kind() != reflect.Slice { + panic("collections.Excepts: first must be a slice") + } + valOfFirst := reflect.ValueOf(first) + if valOfFirst.Len() == 0 { + return MakeEmptySlice(typeOfFirst.Elem()) + } + + typeOfSecond := reflect.TypeOf(second) + if typeOfSecond.Kind() != reflect.Slice { + panic("collections.Excepts: second must be a slice") + } + + valOfSecond := reflect.ValueOf(second) + if valOfSecond.Len() == 0 { + return CloneSlice(first) + } + + result := reflect.New(reflect.SliceOf(typeOfFirst.Elem())).Elem() + for i := 0; i < valOfFirst.Len(); i++ { + s := valOfFirst.Index(i).Interface() + if i, _ := TwowaySearch(second, s, fn); i < 0 { + result = reflect.Append(result, reflect.ValueOf(s)) + } + } + + return result.Interface() +} + +// Intersects Produces to intersect of two slice according to a comparer function. +// +// first: the first slice. MUST BE A SLICE. +// second: the second slice. MUST BE A SLICE. +// fn: the comparer function. +// returns: to intersect of the two slices. +func Intersects(first any, second any, fn Comparer) any { + typeOfFirst := reflect.TypeOf(first) + if typeOfFirst.Kind() != reflect.Slice { + panic("collections.Intersects: first must be a slice") + } + valOfFirst := reflect.ValueOf(first) + if valOfFirst.Len() == 0 { + return MakeEmptySlice(typeOfFirst.Elem()) + } + + typeOfSecond := reflect.TypeOf(second) + if typeOfSecond.Kind() != reflect.Slice { + panic("collections.Intersects: second must be a slice") + } + + valOfSecond := reflect.ValueOf(second) + if valOfSecond.Len() == 0 { + return MakeEmptySlice(typeOfFirst.Elem()) + } + + result := reflect.New(reflect.SliceOf(typeOfFirst.Elem())).Elem() + for i := 0; i < valOfFirst.Len(); i++ { + s := valOfFirst.Index(i).Interface() + if i, _ := TwowaySearch(second, s, fn); i >= 0 { + result = reflect.Append(result, reflect.ValueOf(s)) + } + } + + return result.Interface() +} + +// Union Produces the set union of two slice according to a comparer function +// +// first: the first slice. MUST BE A SLICE. +// second: the second slice. MUST BE A SLICE. +// fn: the comparer function. +// returns: the union of the two slices. +func Union(first, second any, fn Comparer) any { + excepts := Excepts(second, first, fn) + + typeOfFirst := reflect.TypeOf(first) + if typeOfFirst.Kind() != reflect.Slice { + panic("collections.Intersects: first must be a slice") + } + valOfFirst := reflect.ValueOf(first) + if valOfFirst.Len() == 0 { + return CloneSlice(second) + } + + result := reflect.AppendSlice(reflect.New(reflect.SliceOf(typeOfFirst.Elem())).Elem(), valOfFirst) + result = reflect.AppendSlice(result, reflect.ValueOf(excepts)) + return result.Interface() +} + +// Find Produces the struct/value of a slice according to a predicate function. +// +// source: the slice. MUST BE A SLICE. +// fn: the predicate function. +// returns: the struct/value of the slice. +func Find(source any, fn Predicate) (any, error) { + aType := reflect.TypeOf(source) + if aType.Kind() != reflect.Slice { + panic("collections.Find: source must be a slice") + } + + sourceVal := reflect.ValueOf(source) + if sourceVal.Len() == 0 { + return nil, errors.New(ErrElementNotFound) + } + + for i := 0; i < sourceVal.Len(); i++ { + s := sourceVal.Index(i).Interface() + if fn(s) { + return s, nil + } + } + return nil, errors.New(ErrElementNotFound) +} + +// FindOrDefault Produce the struct/value f a slice to a predicate function, +// Produce default value when predicate function not found. +// +// source: the slice. MUST BE A SLICE. +// fn: the predicate function. +// defaultValue: the default value. +// returns: the struct/value of the slice. +func FindOrDefault(source any, fn Predicate, defaultValue any) any { + item, err := Find(source, fn) + if err != nil { + if err.Error() == ErrElementNotFound { + return defaultValue + } + } + return item +} + +// TakeWhile Produce the set of a slice according to a predicate function, +// Produce empty slice when predicate function not matched. +// +// data: the slice. MUST BE A SLICE. +// fn: the predicate function. +// returns: the set of the slice. +func TakeWhile(data any, fn Predicate) any { + aType := reflect.TypeOf(data) + if aType.Kind() != reflect.Slice { + panic("collections.TakeWhile: data must be a slice") + } + + sourceVal := reflect.ValueOf(data) + if sourceVal.Len() == 0 { + return MakeEmptySlice(aType.Elem()) + } + + result := reflect.New(reflect.SliceOf(aType.Elem())).Elem() + for i := 0; i < sourceVal.Len(); i++ { + s := sourceVal.Index(i).Interface() + if fn(s) { + result = reflect.Append(result, reflect.ValueOf(s)) + } + } + return result.Interface() +} + +// ExceptWhile Produce the set of a slice except with a predicate function, +// Produce original slice when predicate function not match. +// +// data: the slice. MUST BE A SLICE. +// fn: the predicate function. +// returns: the set of the slice. +func ExceptWhile(data any, fn Predicate) any { + aType := reflect.TypeOf(data) + if aType.Kind() != reflect.Slice { + panic("collections.ExceptWhile: data must be a slice") + } + + sourceVal := reflect.ValueOf(data) + if sourceVal.Len() == 0 { + return MakeEmptySlice(aType.Elem()) + } + + result := reflect.New(reflect.SliceOf(aType.Elem())).Elem() + for i := 0; i < sourceVal.Len(); i++ { + s := sourceVal.Index(i).Interface() + if !fn(s) { + result = reflect.Append(result, reflect.ValueOf(s)) + } + } + return result.Interface() +} diff --git a/vendor/github.com/gookit/goutil/arrutil/collection_gte118.go b/vendor/github.com/gookit/goutil/arrutil/collection_gte118.go new file mode 100644 index 00000000..f580c599 --- /dev/null +++ b/vendor/github.com/gookit/goutil/arrutil/collection_gte118.go @@ -0,0 +1,20 @@ +package arrutil + +// type MapFn func(obj T) (target V, find bool) + +// Map an object list [object0{},object1{},...] to flatten list [object0.someKey, object1.someKey, ...] +func Map[T any, V any](list []T, mapFn func(obj T) (val V, find bool)) []V { + flatArr := make([]V, 0, len(list)) + + for _, obj := range list { + if target, find := mapFn(obj); find { + flatArr = append(flatArr, target) + } + } + return flatArr +} + +// Column alias of Map func +func Column[T any, V any](list []T, mapFn func(obj T) (val V, find bool)) []V { + return Map(list, mapFn) +} diff --git a/vendor/github.com/gookit/goutil/arrutil/convert.go b/vendor/github.com/gookit/goutil/arrutil/convert.go new file mode 100644 index 00000000..cff56bd0 --- /dev/null +++ b/vendor/github.com/gookit/goutil/arrutil/convert.go @@ -0,0 +1,168 @@ +package arrutil + +import ( + "errors" + "reflect" + "strconv" + "strings" + + "github.com/gookit/goutil/mathutil" + "github.com/gookit/goutil/strutil" +) + +// ErrInvalidType error +var ErrInvalidType = errors.New("the input param type is invalid") + +/************************************************************* + * helper func for strings + *************************************************************/ + +// JoinStrings alias of strings.Join +func JoinStrings(sep string, ss ...string) string { + return strings.Join(ss, sep) +} + +// StringsJoin alias of strings.Join +func StringsJoin(sep string, ss ...string) string { + return strings.Join(ss, sep) +} + +// StringsToInts string slice to int slice +func StringsToInts(ss []string) (ints []int, err error) { + for _, str := range ss { + iVal, err := strconv.Atoi(str) + if err != nil { + return nil, err + } + + ints = append(ints, iVal) + } + return +} + +// MustToStrings convert array or slice to []string +func MustToStrings(arr any) []string { + ret, _ := ToStrings(arr) + return ret +} + +// StringsToSlice convert []string to []any +func StringsToSlice(ss []string) []any { + args := make([]any, len(ss)) + for i, s := range ss { + args[i] = s + } + return args +} + +/************************************************************* + * helper func for slices + *************************************************************/ + +// ToInt64s convert any(allow: array,slice) to []int64 +func ToInt64s(arr any) (ret []int64, err error) { + rv := reflect.ValueOf(arr) + if rv.Kind() != reflect.Slice && rv.Kind() != reflect.Array { + err = ErrInvalidType + return + } + + for i := 0; i < rv.Len(); i++ { + i64, err := mathutil.Int64(rv.Index(i).Interface()) + if err != nil { + return []int64{}, err + } + + ret = append(ret, i64) + } + return +} + +// MustToInt64s convert any(allow: array,slice) to []int64 +func MustToInt64s(arr any) []int64 { + ret, _ := ToInt64s(arr) + return ret +} + +// SliceToInt64s convert []any to []int64 +func SliceToInt64s(arr []any) []int64 { + i64s := make([]int64, len(arr)) + for i, v := range arr { + i64s[i] = mathutil.MustInt64(v) + } + return i64s +} + +// ToStrings convert any(allow: array,slice) to []string +func ToStrings(arr any) (ret []string, err error) { + rv := reflect.ValueOf(arr) + if rv.Kind() != reflect.Slice && rv.Kind() != reflect.Array { + err = ErrInvalidType + return + } + + for i := 0; i < rv.Len(); i++ { + str, err := strutil.ToString(rv.Index(i).Interface()) + if err != nil { + return []string{}, err + } + + ret = append(ret, str) + } + return +} + +// SliceToStrings convert []any to []string +func SliceToStrings(arr []any) []string { + ss := make([]string, len(arr)) + for i, v := range arr { + ss[i] = strutil.MustString(v) + } + return ss +} + +// AnyToString simple and quickly convert any array, slice to string +func AnyToString(arr any) string { + return NewFormatter(arr).Format() +} + +// SliceToString convert []any to string +func SliceToString(arr ...any) string { return ToString(arr) } + +// ToString simple and quickly convert []any to string +func ToString(arr []any) string { + // like fmt.Println([]any(nil)) + if arr == nil { + return "[]" + } + + var sb strings.Builder + sb.WriteByte('[') + + for i, v := range arr { + if i > 0 { + sb.WriteByte(',') + } + sb.WriteString(strutil.MustString(v)) + } + + sb.WriteByte(']') + return sb.String() +} + +// JoinSlice join []any slice to string. +func JoinSlice(sep string, arr ...any) string { + if arr == nil { + return "" + } + + var sb strings.Builder + for i, v := range arr { + if i > 0 { + sb.WriteString(sep) + } + sb.WriteString(strutil.MustString(v)) + } + + return sb.String() +} diff --git a/vendor/github.com/gookit/goutil/arrutil/enum.go b/vendor/github.com/gookit/goutil/arrutil/enum.go new file mode 100644 index 00000000..5897b221 --- /dev/null +++ b/vendor/github.com/gookit/goutil/arrutil/enum.go @@ -0,0 +1,51 @@ +package arrutil + +import ( + "strconv" + "strings" +) + +// Ints type +type Ints []int + +// String to string +func (is Ints) String() string { + ss := make([]string, len(is)) + for i, iv := range is { + ss[i] = strconv.Itoa(iv) + } + return strings.Join(ss, ",") +} + +// Has given element +func (is Ints) Has(i int) bool { + for _, iv := range is { + if i == iv { + return true + } + } + return false +} + +// Strings type +type Strings []string + +// String to string +func (ss Strings) String() string { + return strings.Join(ss, ",") +} + +// Join to string +func (ss Strings) Join(sep string) string { + return strings.Join(ss, sep) +} + +// Has given element +func (ss Strings) Has(sub string) bool { + for _, s := range ss { + if s == sub { + return true + } + } + return false +} diff --git a/vendor/github.com/gookit/goutil/arrutil/format.go b/vendor/github.com/gookit/goutil/arrutil/format.go new file mode 100644 index 00000000..d00b582a --- /dev/null +++ b/vendor/github.com/gookit/goutil/arrutil/format.go @@ -0,0 +1,125 @@ +package arrutil + +import ( + "io" + "reflect" + + "github.com/gookit/goutil/comdef" + "github.com/gookit/goutil/strutil" +) + +// ArrFormatter struct +type ArrFormatter struct { + comdef.BaseFormatter + // Prefix string for each element + Prefix string + // Indent string for format each element + Indent string + // ClosePrefix string for last "]" + ClosePrefix string +} + +// NewFormatter instance +func NewFormatter(arr any) *ArrFormatter { + f := &ArrFormatter{} + f.Src = arr + + return f +} + +// WithFn for config self +func (f *ArrFormatter) WithFn(fn func(f *ArrFormatter)) *ArrFormatter { + fn(f) + return f +} + +// WithIndent string +func (f *ArrFormatter) WithIndent(indent string) *ArrFormatter { + f.Indent = indent + return f +} + +// FormatTo to custom buffer +func (f *ArrFormatter) FormatTo(w io.Writer) { + f.SetOutput(w) + f.doFormat() +} + +// Format to string +func (f *ArrFormatter) String() string { + f.Format() + return f.Format() +} + +// Format to string +func (f *ArrFormatter) Format() string { + f.doFormat() + return f.BsWriter().String() +} + +// Format to string +// +//goland:noinspection GoUnhandledErrorResult +func (f *ArrFormatter) doFormat() { + if f.Src == nil { + return + } + + rv, ok := f.Src.(reflect.Value) + if !ok { + rv = reflect.ValueOf(f.Src) + } + + rv = reflect.Indirect(rv) + if rv.Kind() != reflect.Slice && rv.Kind() != reflect.Array { + return + } + + writer := f.BsWriter() + arrLn := rv.Len() + if arrLn == 0 { + writer.WriteString("[]") + return + } + + // if f.AfterReset { + // defer f.Reset() + // } + + // sb.Grow(arrLn * 4) + writer.WriteByte('[') + + indentLn := len(f.Indent) + if indentLn > 0 { + writer.WriteByte('\n') + } + + for i := 0; i < arrLn; i++ { + if indentLn > 0 { + writer.WriteString(f.Indent) + } + writer.WriteString(strutil.QuietString(rv.Index(i).Interface())) + + if i < arrLn-1 { + writer.WriteByte(',') + + // no indent, with space + if indentLn == 0 { + writer.WriteByte(' ') + } + } + if indentLn > 0 { + writer.WriteByte('\n') + } + } + + if f.ClosePrefix != "" { + writer.WriteString(f.ClosePrefix) + } + writer.WriteByte(']') +} + +// FormatIndent array data to string. +func FormatIndent(arr any, indent string) string { + return NewFormatter(arr).WithIndent(indent).Format() +} diff --git a/vendor/github.com/gookit/goutil/check.go b/vendor/github.com/gookit/goutil/check.go new file mode 100644 index 00000000..a0b0a0fc --- /dev/null +++ b/vendor/github.com/gookit/goutil/check.go @@ -0,0 +1,72 @@ +package goutil + +import ( + "reflect" + + "github.com/gookit/goutil/reflects" + "github.com/gookit/goutil/stdutil" +) + +// IsNil value check +func IsNil(v any) bool { + if v == nil { + return true + } + return reflects.IsNil(reflect.ValueOf(v)) +} + +// IsEmpty value check +func IsEmpty(v any) bool { + if v == nil { + return true + } + return reflects.IsEmpty(reflect.ValueOf(v)) +} + +// IsFunc value +func IsFunc(val any) bool { + if val == nil { + return false + } + return reflect.TypeOf(val).Kind() == reflect.Func +} + +// IsEqual determines if two objects are considered equal. +// +// TIP: cannot compare function type +func IsEqual(src, dst any) bool { + if src == nil || dst == nil { + return src == dst + } + + // cannot compare function type + if IsFunc(src) || IsFunc(dst) { + return false + } + return reflects.IsEqual(src, dst) +} + +// Contains try loop over the data check if the data includes the element. +// alias of the IsContains +// +// TIP: only support types: string, map, array, slice +// +// map - check key exists +// string - check sub-string exists +// array,slice - check sub-element exists +func Contains(data, elem any) bool { + _, found := stdutil.CheckContains(data, elem) + return found +} + +// IsContains try loop over the data check if the data includes the element. +// +// TIP: only support types: string, map, array, slice +// +// map - check key exists +// string - check sub-string exists +// array,slice - check sub-element exists +func IsContains(data, elem any) bool { + _, found := stdutil.CheckContains(data, elem) + return found +} diff --git a/vendor/github.com/gookit/goutil/cliutil/cmdline/cmdline.go b/vendor/github.com/gookit/goutil/cliutil/cmdline/cmdline.go new file mode 100644 index 00000000..6dbebc50 --- /dev/null +++ b/vendor/github.com/gookit/goutil/cliutil/cmdline/cmdline.go @@ -0,0 +1,2 @@ +// Package cmdline provide quick build and parse cmd line string. +package cmdline diff --git a/vendor/github.com/gookit/goutil/comdef/comdef.go b/vendor/github.com/gookit/goutil/comdef/comdef.go new file mode 100644 index 00000000..2a3f7ef9 --- /dev/null +++ b/vendor/github.com/gookit/goutil/comdef/comdef.go @@ -0,0 +1,29 @@ +// Package comdef provide some common type or constant definitions +package comdef + +import ( + "fmt" + "io" +) + +// ByteStringWriter interface +type ByteStringWriter interface { + io.Writer + io.ByteWriter + io.StringWriter + fmt.Stringer +} + +// StringWriteStringer interface +type StringWriteStringer interface { + io.StringWriter + fmt.Stringer +} + +type ( + // MarshalFunc define + MarshalFunc func(v any) ([]byte, error) + + // UnmarshalFunc define + UnmarshalFunc func(bts []byte, ptr any) error +) diff --git a/vendor/github.com/gookit/goutil/comdef/consts.go b/vendor/github.com/gookit/goutil/comdef/consts.go new file mode 100644 index 00000000..1534a196 --- /dev/null +++ b/vendor/github.com/gookit/goutil/comdef/consts.go @@ -0,0 +1,25 @@ +package comdef + +// consts for compare operation +const ( + OpEq = "=" + OpNeq = "!=" + OpLt = "<" + OpLte = "<=" + OpGt = ">" + OpGte = ">=" +) + +// consts quote chars +const ( + SingleQuote = '\'' + DoubleQuote = '"' + SlashQuote = '\\' + + SingleQuoteStr = "'" + DoubleQuoteStr = `"` + SlashQuoteStr = "\\" +) + +// NoIdx invalid index or length +const NoIdx = -1 diff --git a/vendor/github.com/gookit/goutil/comdef/errors.go b/vendor/github.com/gookit/goutil/comdef/errors.go new file mode 100644 index 00000000..c210de32 --- /dev/null +++ b/vendor/github.com/gookit/goutil/comdef/errors.go @@ -0,0 +1,6 @@ +package comdef + +import "errors" + +// ErrConvType error +var ErrConvType = errors.New("convert value type error") diff --git a/vendor/github.com/gookit/goutil/comdef/formatter.go b/vendor/github.com/gookit/goutil/comdef/formatter.go new file mode 100644 index 00000000..7bd713ad --- /dev/null +++ b/vendor/github.com/gookit/goutil/comdef/formatter.go @@ -0,0 +1,57 @@ +package comdef + +import ( + "bytes" + "io" + + "github.com/gookit/goutil/stdio" +) + +// DataFormatter interface +type DataFormatter interface { + Format() string + FormatTo(w io.Writer) +} + +// BaseFormatter struct +type BaseFormatter struct { + ow ByteStringWriter + // Out formatted to the writer + Out io.Writer + // Src data(array, map, struct) for format + Src any + // MaxDepth limit depth for array, map data TODO + MaxDepth int + // Prefix string for each element + Prefix string + // Indent string for format each element + Indent string + // ClosePrefix string for last "]", "}" + ClosePrefix string +} + +// Reset after format +func (f *BaseFormatter) Reset() { + f.Out = nil + f.Src = nil +} + +// SetOutput writer +func (f *BaseFormatter) SetOutput(out io.Writer) { + f.Out = out +} + +// BsWriter build and get +func (f *BaseFormatter) BsWriter() ByteStringWriter { + if f.ow == nil { + if f.Out == nil { + f.ow = new(bytes.Buffer) + } else if ow, ok := f.Out.(ByteStringWriter); ok { + f.ow = ow + } else { + f.ow = stdio.NewWriteWrapper(f.Out) + } + } + + return f.ow +} diff --git a/vendor/github.com/gookit/goutil/comdef/symbols.go b/vendor/github.com/gookit/goutil/comdef/symbols.go new file mode 100644 index 00000000..899dc554 --- /dev/null +++ b/vendor/github.com/gookit/goutil/comdef/symbols.go @@ -0,0 +1,41 @@ +package comdef + +const ( + // CommaStr const define + CommaStr = "," + // CommaChar define + CommaChar = ',' + + // EqualStr define + EqualStr = "=" + // EqualChar define + EqualChar = '=' + + // ColonStr define + ColonStr = ":" + // ColonChar define + ColonChar = ':' + + // SemicolonStr semicolon define + SemicolonStr = ";" + // SemicolonChar define + SemicolonChar = ';' + + // PathStr define const + PathStr = "/" + // PathChar define + PathChar = '/' + + // DefaultSep comma string + DefaultSep = "," + + // SpaceChar char + SpaceChar = ' ' + // SpaceStr string + SpaceStr = " " + + // NewlineChar char + NewlineChar = '\n' + // NewlineStr string + NewlineStr = "\n" +) diff --git a/vendor/github.com/gookit/goutil/comdef/types.go b/vendor/github.com/gookit/goutil/comdef/types.go new file mode 100644 index 00000000..0a26e290 --- /dev/null +++ b/vendor/github.com/gookit/goutil/comdef/types.go @@ -0,0 +1,39 @@ +package comdef + +// Int interface type +type Int interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 +} + +// Uint interface type +type Uint interface { + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 +} + +// Xint interface type. all int or uint types +type Xint interface { + // equal: ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint32 | ~uint64 + Int | Uint +} + +// Float interface type +type Float interface { + ~float32 | ~float64 +} + +// IntOrFloat interface type. all int and float types +type IntOrFloat interface { + Int | Float +} + +// XintOrFloat interface type. all (x)int and float types +type XintOrFloat interface { + Int | Uint | Float +} + +// ScalarType interface type. +// +// contains: (x)int, float, ~string, ~bool types +type ScalarType interface { + Int | Uint | Float | ~string | ~bool +} diff --git a/vendor/github.com/gookit/goutil/conv.go b/vendor/github.com/gookit/goutil/conv.go new file mode 100644 index 00000000..7686747d --- /dev/null +++ b/vendor/github.com/gookit/goutil/conv.go @@ -0,0 +1,82 @@ +package goutil + +import ( + "reflect" + "strconv" + + "github.com/gookit/goutil/internal/comfunc" + "github.com/gookit/goutil/mathutil" + "github.com/gookit/goutil/reflects" + "github.com/gookit/goutil/strutil" +) + +// Bool convert value to bool +func Bool(v any) bool { + bl, _ := comfunc.ToBool(v) + return bl +} + +// ToBool try to convert type to bool +func ToBool(v any) (bool, error) { + return comfunc.ToBool(v) +} + +// String always convert value to string, will ignore error +func String(v any) string { + s, _ := strutil.AnyToString(v, false) + return s +} + +// ToString convert value to string, will return error on fail. +func ToString(v any) (string, error) { + return strutil.AnyToString(v, true) +} + +// Int convert value to int +func Int(v any) int { + iv, _ := mathutil.ToInt(v) + return iv +} + +// ToInt try to convert value to int +func ToInt(v any) (int, error) { + return mathutil.ToInt(v) +} + +// Int64 convert value to int64 +func Int64(v any) int64 { + iv, _ := mathutil.ToInt64(v) + return iv +} + +// ToInt64 try to convert value to int64 +func ToInt64(v any) (int64, error) { + return mathutil.ToInt64(v) +} + +// Uint convert value to uint64 +func Uint(v any) uint64 { + iv, _ := mathutil.ToUint(v) + return iv +} + +// ToUint try to convert value to uint64 +func ToUint(v any) (uint64, error) { + return mathutil.ToUint(v) +} + +// BaseTypeVal convert custom type or intX,uintX,floatX to generic base type. +// +// intX/unitX => int64 +// floatX => float64 +// string => string +// +// returns int64,string,float or error +func BaseTypeVal(val any) (value any, err error) { + return reflects.BaseTypeVal(reflect.ValueOf(val)) +} + +// BoolString convert +func BoolString(bl bool) string { + return strconv.FormatBool(bl) +} diff --git a/vendor/github.com/gookit/goutil/envutil/README.md b/vendor/github.com/gookit/goutil/envutil/README.md new file mode 100644 index 00000000..1f727c7e --- /dev/null +++ b/vendor/github.com/gookit/goutil/envutil/README.md @@ -0,0 +1,60 @@ +# Env Util + +Provide some commonly ENV util functions. + +## Install + +```shell +go get github.com/gookit/goutil/envutil +``` + +## Go docs + +- [Go docs](https://pkg.go.dev/github.com/gookit/goutil/envutil) + +## Functions API + +```go +func Environ() map[string]string +func GetBool(name string, def ...bool) bool +func GetInt(name string, def ...int) int +func Getenv(name string, def ...string) string +func HasShellEnv(shell string) bool +func IsConsole(out io.Writer) bool +func IsGithubActions() bool +func IsLinux() bool +func IsMSys() bool +func IsMac() bool +func IsSupport256Color() bool +func IsSupportColor() bool +func IsSupportTrueColor() bool +func IsTerminal(fd uintptr) bool +func IsWSL() bool +func IsWin() bool +func IsWindows() bool +func ParseEnvValue(val string) string +func ParseValue(val string) (newVal string) +func SetEnvs(mp map[string]string) +func StdIsTerminal() bool +func VarParse(val string) string +func VarReplace(s string) string +``` + +## Code Check & Testing + +```bash +gofmt -w -l ./ +golint ./... +``` + +**Testing**: + +```shell +go test -v ./envutil/... +``` + +**Test limit by regexp**: + +```shell +go test -v -run ^TestSetByKeys ./envutil/... +``` diff --git a/vendor/github.com/gookit/goutil/envutil/envutil.go b/vendor/github.com/gookit/goutil/envutil/envutil.go index 0bd8d387..9680cefd 100644 --- a/vendor/github.com/gookit/goutil/envutil/envutil.go +++ b/vendor/github.com/gookit/goutil/envutil/envutil.go @@ -2,67 +2,48 @@ package envutil import ( "os" - "regexp" - "strings" -) -// VarReplace replaces ${var} or $var in the string according to the values. -// is alias of the os.ExpandEnv() -func VarReplace(s string) string { - return os.ExpandEnv(s) -} + "github.com/gookit/goutil/internal/comfunc" +) // ValueGetter Env value provider func. +// // TIPS: you can custom provide data. var ValueGetter = os.Getenv -// parse env value, allow: -// only key - "${SHELL}" -// with default - "${NotExist|defValue}" -// multi key - "${GOPATH}/${APP_ENV | prod}/dir" -// Notice: -// must add "?" - To ensure that there is no greedy match -// var envRegex = regexp.MustCompile(`\${[\w-| ]+}`) -var envRegex = regexp.MustCompile(`\${.+?}`) +// VarReplace replaces ${var} or $var in the string according to the values. +// +// is alias of the os.ExpandEnv() +func VarReplace(s string) string { return os.ExpandEnv(s) } // VarParse alias of the ParseValue -func VarParse(str string) string { - return ParseEnvValue(str) +func VarParse(val string) string { + return comfunc.ParseEnvVar(val, ValueGetter) } // ParseEnvValue alias of the ParseValue -func ParseEnvValue(str string) string { - return ParseValue(str) +func ParseEnvValue(val string) string { + return comfunc.ParseEnvVar(val, ValueGetter) } // ParseValue parse ENV var value from input string, support default value. -// vars like ${var}, ${var| default} +// +// Format: +// +// ${var_name} Only var name +// ${var_name | default} With default value // // Usage: -// envutil.ParseValue() +// +// envutil.ParseValue("${ APP_NAME }") +// envutil.ParseValue("${ APP_ENV | dev }") func ParseValue(val string) (newVal string) { - if strings.Index(val, "${") == -1 { - return val - } - - var name, def string - return envRegex.ReplaceAllStringFunc(val, func(eVar string) string { - // eVar like "${NotExist|defValue}", first remove "${" and "}", then split it - ss := strings.SplitN(eVar[2:len(eVar)-1], "|", 2) - - // with default value. ${NotExist|defValue} - if len(ss) == 2 { - name, def = strings.TrimSpace(ss[0]), strings.TrimSpace(ss[1]) - } else { - def = eVar // use raw value - name = strings.TrimSpace(ss[0]) - } + return comfunc.ParseEnvVar(val, ValueGetter) +} - // get ENV value by name - eVal := ValueGetter(name) - if eVal == "" { - eVal = def - } - return eVal - }) +// SetEnvs to os +func SetEnvs(mp map[string]string) { + for key, value := range mp { + _ = os.Setenv(key, value) + } } diff --git a/vendor/github.com/gookit/goutil/envutil/get.go b/vendor/github.com/gookit/goutil/envutil/get.go index 1b594a0c..19b7ee43 100644 --- a/vendor/github.com/gookit/goutil/envutil/get.go +++ b/vendor/github.com/gookit/goutil/envutil/get.go @@ -2,8 +2,8 @@ package envutil import ( "os" - "strings" + "github.com/gookit/goutil/internal/comfunc" "github.com/gookit/goutil/strutil" ) @@ -19,7 +19,7 @@ func Getenv(name string, def ...string) string { // GetInt get int ENV value by key name, can with default value func GetInt(name string, def ...int) int { if val := os.Getenv(name); val != "" { - return strutil.MustInt(val) + return strutil.QuietInt(val) } if len(def) > 0 { @@ -31,7 +31,7 @@ func GetInt(name string, def ...int) int { // GetBool get bool ENV value by key name, can with default value func GetBool(name string, def ...bool) bool { if val := os.Getenv(name); val != "" { - return strutil.MustBool(val) + return strutil.QuietBool(val) } if len(def) > 0 { @@ -42,17 +42,5 @@ func GetBool(name string, def ...bool) bool { // Environ like os.Environ, but will returns key-value map[string]string data. func Environ() map[string]string { - envList := os.Environ() - envMap := make(map[string]string, len(envList)) - - for _, str := range envList { - nodes := strings.SplitN(str, "=", 2) - - if len(nodes) < 2 { - envMap[nodes[0]] = "" - } else { - envMap[nodes[0]] = nodes[1] - } - } - return envMap + return comfunc.Environ() } diff --git a/vendor/github.com/gookit/goutil/envutil/info.go b/vendor/github.com/gookit/goutil/envutil/info.go index 2662b077..5a66150f 100644 --- a/vendor/github.com/gookit/goutil/envutil/info.go +++ b/vendor/github.com/gookit/goutil/envutil/info.go @@ -6,8 +6,9 @@ import ( "runtime" "strings" + "github.com/gookit/goutil/internal/comfunc" "github.com/gookit/goutil/sysutil" - "github.com/mattn/go-isatty" + "golang.org/x/term" ) // IsWin system. linux windows darwin @@ -66,9 +67,11 @@ func IsWSL() bool { // IsTerminal isatty check // // Usage: -// envutil.IsTerminal(os.Stdout.Fd()) +// +// envutil.IsTerminal(os.Stdout.Fd()) func IsTerminal(fd uintptr) bool { - return isatty.IsTerminal(fd) + // return isatty.IsTerminal(fd) // "github.com/mattn/go-isatty" + return term.IsTerminal(int(fd)) } // StdIsTerminal os.Stdout is terminal @@ -82,22 +85,27 @@ func IsConsole(out io.Writer) bool { } // HasShellEnv has shell env check. +// // Usage: -// HasShellEnv("sh") -// HasShellEnv("bash") +// +// HasShellEnv("sh") +// HasShellEnv("bash") func HasShellEnv(shell string) bool { - return sysutil.HasShellEnv(shell) + return comfunc.HasShellEnv(shell) } // Support color: -// "TERM=xterm" -// "TERM=xterm-vt220" -// "TERM=xterm-256color" -// "TERM=screen-256color" -// "TERM=tmux-256color" -// "TERM=rxvt-unicode-256color" +// +// "TERM=xterm" +// "TERM=xterm-vt220" +// "TERM=xterm-256color" +// "TERM=screen-256color" +// "TERM=tmux-256color" +// "TERM=rxvt-unicode-256color" +// // Don't support color: -// "TERM=cygwin" +// +// "TERM=cygwin" var specialColorTerms = map[string]bool{ "alacritty": true, } @@ -105,9 +113,12 @@ var specialColorTerms = map[string]bool{ // IsSupportColor check current console is support color. // // Supported: -// linux, mac, or windows's ConEmu, Cmder, putty, git-bash.exe +// +// linux, mac, or windows's ConEmu, Cmder, putty, git-bash.exe +// // Not support: -// windows cmd.exe, powerShell.exe +// +// windows cmd.exe, powerShell.exe func IsSupportColor() bool { envTerm := os.Getenv("TERM") if strings.Contains(envTerm, "xterm") { @@ -153,3 +164,8 @@ func IsSupportTrueColor() bool { // "COLORTERM=truecolor" return strings.Contains(os.Getenv("COLORTERM"), "truecolor") } + +// IsGithubActions env +func IsGithubActions() bool { + return os.Getenv("GITHUB_ACTIONS") == "true" +} diff --git a/vendor/github.com/gookit/goutil/errorx/README.md b/vendor/github.com/gookit/goutil/errorx/README.md new file mode 100644 index 00000000..1e23ca64 --- /dev/null +++ b/vendor/github.com/gookit/goutil/errorx/README.md @@ -0,0 +1,221 @@ +# ErrorX + +`errorx` provide an enhanced error implements for go, allow with stacktraces and wrap another error. + +## Install + +```go +go get github.com/gookit/goutil/errorx +``` + +## Go docs + +- [Go docs](https://pkg.go.dev/github.com/gookit/goutil/errorx) + +## Usage + +### Create error with call stack info + +- use the `errorx.New` instead `errors.New` + +```go +func doSomething() error { + if false { + // return errors.New("a error happen") + return errorx.New("a error happen") + } +} +``` + +- use the `errorx.Newf` or `errorx.Errorf` instead `fmt.Errorf` + +```go +func doSomething() error { + if false { + // return fmt.Errorf("a error %s", "happen") + return errorx.Newf("a error %s", "happen") + } +} +``` + +### Wrap the previous error + +used like this before: + +```go + if err := SomeFunc(); err != nil { + return err + } +``` + +can be replaced with: + +```go + if err := SomeFunc(); err != nil { + return errors.Stacked(err) + } +``` + +## Output details + +error output details for use `errorx` + +### Use errorx.New + +`errorx` functions for new error: + +```go +func New(msg string) error +func Newf(tpl string, vars ...interface{}) error +func Errorf(tpl string, vars ...interface{}) error +``` + +Examples: + +```go + err := errorx.New("the error message") + + fmt.Println(err) + // fmt.Printf("%v\n", err) + // fmt.Printf("%#v\n", err) +``` + +> from the test: `errorx_test.TestNew()` + +**Output**: + +```text +the error message +STACK: +github.com/gookit/goutil/errorx_test.returnXErr() + /Users/inhere/Workspace/godev/gookit/goutil/errorx/errorx_test.go:21 +github.com/gookit/goutil/errorx_test.returnXErrL2() + /Users/inhere/Workspace/godev/gookit/goutil/errorx/errorx_test.go:25 +github.com/gookit/goutil/errorx_test.TestNew() + /Users/inhere/Workspace/godev/gookit/goutil/errorx/errorx_test.go:29 +testing.tRunner() + /usr/local/Cellar/go/1.18/libexec/src/testing/testing.go:1439 +runtime.goexit() + /usr/local/Cellar/go/1.18/libexec/src/runtime/asm_amd64.s:1571 +``` + +### Use errorx.With + +`errorx` functions for with another error: + +```go +func With(err error, msg string) error +func Withf(err error, tpl string, vars ...interface{}) error +``` + +With a go raw error: + +```go + err1 := returnErr("first error message") + + err2 := errorx.With(err1, "second error message") + fmt.Println(err2) +``` + +> from the test: `errorx_test.TestWith_goerr()` + +**Output**: + +```text +second error message +STACK: +github.com/gookit/goutil/errorx_test.TestWith_goerr() + /Users/inhere/Workspace/godev/gookit/goutil/errorx/errorx_test.go:51 +testing.tRunner() + /usr/local/Cellar/go/1.18/libexec/src/testing/testing.go:1439 +runtime.goexit() + /usr/local/Cellar/go/1.18/libexec/src/runtime/asm_amd64.s:1571 + +Previous: first error message +``` + +With a `errorx` error: + +```go + err1 := returnXErr("first error message") + err2 := errorx.With(err1, "second error message") + fmt.Println(err2) +``` + +> from the test: `errorx_test.TestWith_errorx()` + +**Output**: + +```text +second error message +STACK: +github.com/gookit/goutil/errorx_test.TestWith_errorx() + /Users/inhere/Workspace/godev/gookit/goutil/errorx/errorx_test.go:64 +testing.tRunner() + /usr/local/Cellar/go/1.18/libexec/src/testing/testing.go:1439 +runtime.goexit() + /usr/local/Cellar/go/1.18/libexec/src/runtime/asm_amd64.s:1571 + +Previous: first error message +STACK: +github.com/gookit/goutil/errorx_test.returnXErr() + /Users/inhere/Workspace/godev/gookit/goutil/errorx/errorx_test.go:21 +github.com/gookit/goutil/errorx_test.TestWith_errorx() + /Users/inhere/Workspace/godev/gookit/goutil/errorx/errorx_test.go:61 +testing.tRunner() + /usr/local/Cellar/go/1.18/libexec/src/testing/testing.go:1439 +runtime.goexit() + /usr/local/Cellar/go/1.18/libexec/src/runtime/asm_amd64.s:1571 + +``` + +### Use errorx.Wrap + +```go +err := errors.New("first error message") +err = errorx.Wrap(err, "second error message") +err = errorx.Wrap(err, "third error message") +// fmt.Println(err) +// fmt.Println(err.Error()) +``` + +Direct print the `err`: + +```text +third error message +Previous: second error message +Previous: first error message +``` + +Print the `err.Error()`: + +```text +third error message; second error message; first error message +``` + +## Code Check & Testing + +```bash +gofmt -w -l ./ +golint ./... +``` + +**Testing**: + +```shell +go test -v ./errorx/... +``` + +**Test limit by regexp**: + +```shell +go test -v -run ^TestSetByKeys ./errorx/... +``` + +## Refers + +- golang errors +- https://github.com/joomcode/errorx +- https://github.com/pkg/errors +- https://github.com/juju/errors +- https://github.com/go-errors/errors diff --git a/vendor/github.com/gookit/goutil/errorx/errors.go b/vendor/github.com/gookit/goutil/errorx/errors.go new file mode 100644 index 00000000..a87c2b3c --- /dev/null +++ b/vendor/github.com/gookit/goutil/errorx/errors.go @@ -0,0 +1,144 @@ +package errorx + +import ( + "fmt" + "strconv" + "strings" +) + +// ErrorCoder interface +type ErrorCoder interface { + error + Code() int +} + +// ErrorR useful for web service replay/response. +// code == 0 is successful. otherwise, is failed. +type ErrorR interface { + ErrorCoder + fmt.Stringer + IsSuc() bool + IsFail() bool +} + +// error reply struct +type errorR struct { + code int + msg string +} + +// NewR code with error response +func NewR(code int, msg string) ErrorR { + return &errorR{code: code, msg: msg} +} + +// Fail code with error response +func Fail(code int, msg string) ErrorR { + return &errorR{code: code, msg: msg} +} + +// Suc success response reply +func Suc(msg string) ErrorR { + return &errorR{code: 0, msg: msg} +} + +// IsSuc code value check +func (e *errorR) IsSuc() bool { + return e.code == 0 +} + +// IsFail code value check +func (e *errorR) IsFail() bool { + return e.code != 0 +} + +// Code value +func (e *errorR) Code() int { + return e.code +} + +// Error string +func (e *errorR) Error() string { + return e.msg +} + +// String get +func (e *errorR) String() string { + return e.msg + "(code: " + strconv.FormatInt(int64(e.code), 10) + ")" +} + +// GoString get. +func (e *errorR) GoString() string { + return e.String() +} + +// ErrMap multi error map +type ErrMap map[string]error + +// Error string +func (e ErrMap) Error() string { + var sb strings.Builder + for name, err := range e { + sb.WriteString(name) + sb.WriteByte(':') + sb.WriteString(err.Error()) + sb.WriteByte('\n') + } + return sb.String() +} + +// ErrorOrNil error +func (e ErrMap) ErrorOrNil() error { + if len(e) == 0 { + return nil + } + return e +} + +// IsEmpty error +func (e ErrMap) IsEmpty() bool { + return len(e) == 0 +} + +// One error +func (e ErrMap) One() error { + for _, err := range e { + return err + } + return nil +} + +// Errors multi error list +type Errors []error +type ErrList = Errors + +// Error string +func (es Errors) Error() string { + var sb strings.Builder + for _, err := range es { + sb.WriteString(err.Error()) + sb.WriteByte('\n') + } + return sb.String() +} + +// ErrorOrNil error +func (es Errors) ErrorOrNil() error { + if len(es) == 0 { + return nil + } + return es +} + +// IsEmpty error +func (es Errors) IsEmpty() bool { + return len(es) == 0 +} + +// First error +func (es Errors) First() error { + if len(es) > 0 { + return es[0] + } + return nil +} diff --git a/vendor/github.com/gookit/goutil/errorx/errorx.go b/vendor/github.com/gookit/goutil/errorx/errorx.go new file mode 100644 index 00000000..b7b2625f --- /dev/null +++ b/vendor/github.com/gookit/goutil/errorx/errorx.go @@ -0,0 +1,336 @@ +// Package errorx provide an enhanced error implements for go, +// allow with stacktraces and wrap another error. +package errorx + +import ( + "bytes" + "errors" + "fmt" + "io" +) + +// Causer interface for get first cause error +type Causer interface { + // Cause returns the first cause error by call err.Cause(). + // Otherwise, will returns current error. + Cause() error +} + +// Unwrapper interface for get previous error +type Unwrapper interface { + // Unwrap returns previous error by call err.Unwrap(). + // Otherwise, will returns nil. + Unwrap() error +} + +// XErrorFace interface +type XErrorFace interface { + error + Causer + Unwrapper +} + +// Exception interface +// type Exception interface { +// XErrorFace +// Code() string +// Message() string +// StackString() string +// } + +/************************************************************* + * implements XErrorFace interface + *************************************************************/ + +// ErrorX struct +// +// TIPS: +// +// fmt pkg call order: Format > GoString > Error > String +type ErrorX struct { + // trace stack + *stack + prev error + msg string +} + +// Cause implements Causer. +func (e *ErrorX) Cause() error { + if e.prev == nil { + return e + } + + if ex, ok := e.prev.(*ErrorX); ok { + return ex.Cause() + } + return e.prev +} + +// Unwrap implements Unwrapper. +func (e *ErrorX) Unwrap() error { + return e.prev +} + +// Format error +func (e *ErrorX) Format(s fmt.State, verb rune) { + // format current error: only output on have msg + if len(e.msg) > 0 { + _, _ = io.WriteString(s, e.msg) + if e.stack != nil { + e.stack.Format(s, verb) + } + } + + // format prev error + if e.prev == nil { + return + } + + _, _ = s.Write([]byte("\nPrevious: ")) + if ex, ok := e.prev.(*ErrorX); ok { + ex.Format(s, verb) + } else { + _, _ = s.Write([]byte(e.prev.Error())) + } +} + +// GoString to GO string, contains stack information. +// printing an error with %#v will produce useful information. +func (e *ErrorX) GoString() string { + // var sb strings.Builder + var buf bytes.Buffer + _, _ = e.WriteTo(&buf) + return buf.String() +} + +// Error to string, not contains stack information. +func (e *ErrorX) Error() string { + var buf bytes.Buffer + e.writeMsgTo(&buf) + return buf.String() +} + +// String error to string, contains stack information. +func (e *ErrorX) String() string { + return e.GoString() +} + +// WriteTo write the error to a writer, contains stack information. +func (e *ErrorX) WriteTo(w io.Writer) (n int64, err error) { + // current error: only output on have msg + if len(e.msg) > 0 { + _, _ = w.Write([]byte(e.msg)) + + // with stack + if e.stack != nil { + _, _ = e.stack.WriteTo(w) + } + } + + // with prev error + if e.prev != nil { + _, _ = io.WriteString(w, "\nPrevious: ") + + if ex, ok := e.prev.(*ErrorX); ok { + _, _ = ex.WriteTo(w) + } else { + _, _ = io.WriteString(w, e.prev.Error()) + } + } + return +} + +// Message error message of current +func (e *ErrorX) Message() string { + return e.msg +} + +// StackString returns error stack string of current. +func (e *ErrorX) StackString() string { + if e.stack != nil { + return e.stack.String() + } + return "" +} + +// writeMsgTo write the error msg to a writer +func (e *ErrorX) writeMsgTo(w io.Writer) { + // current error + if len(e.msg) > 0 { + _, _ = w.Write([]byte(e.msg)) + } + + // with prev error + if e.prev != nil { + _, _ = w.Write([]byte("; ")) + if ex, ok := e.prev.(*ErrorX); ok { + ex.writeMsgTo(w) + } else { + _, _ = io.WriteString(w, e.prev.Error()) + } + } +} + +// CallerFunc returns the error caller func. if stack is nil, will return nil +func (e *ErrorX) CallerFunc() *Func { + if e.stack == nil { + return nil + } + return FuncForPC(e.stack.CallerPC()) +} + +// Location information for the caller func. more please see CallerFunc +// +// Returns eg: +// +// github.com/gookit/goutil/errorx_test.TestWithPrev(), errorx_test.go:34 +func (e *ErrorX) Location() string { + if e.stack == nil { + return "unknown" + } + return e.CallerFunc().Location() +} + +/************************************************************* + * new error with call stacks + *************************************************************/ + +// New error message and with caller stacks +func New(msg string) error { + return &ErrorX{ + msg: msg, + stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth), + } +} + +// Newf error with format message, and with caller stacks. +// alias of Errorf() +func Newf(tpl string, vars ...any) error { + return &ErrorX{ + msg: fmt.Sprintf(tpl, vars...), + stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth), + } +} + +// Errorf error with format message, and with caller stacks +func Errorf(tpl string, vars ...any) error { + return &ErrorX{ + msg: fmt.Sprintf(tpl, vars...), + stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth), + } +} + +// With prev error and error message, and with caller stacks +func With(err error, msg string) error { + return &ErrorX{ + msg: msg, + prev: err, + stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth), + } +} + +// Withf error and with format message, and with caller stacks +func Withf(err error, tpl string, vars ...any) error { + return &ErrorX{ + msg: fmt.Sprintf(tpl, vars...), + prev: err, + stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth), + } +} + +// WithPrev error and message, and with caller stacks. alias of With() +func WithPrev(err error, msg string) error { + return &ErrorX{ + msg: msg, + prev: err, + stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth), + } +} + +// WithPrevf error and with format message, and with caller stacks. alias of Withf() +func WithPrevf(err error, tpl string, vars ...any) error { + return &ErrorX{ + msg: fmt.Sprintf(tpl, vars...), + prev: err, + stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth), + } +} + +/************************************************************* + * wrap go error with call stacks + *************************************************************/ + +// WithStack wrap a go error with a stacked trace. If err is nil, will return nil. +func WithStack(err error) error { + if err == nil { + return nil + } + return &ErrorX{ + msg: err.Error(), + // prev: err, + stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth), + } +} + +// Traced warp a go error and with caller stacks. alias of WithStack() +func Traced(err error) error { + if err == nil { + return nil + } + return &ErrorX{ + msg: err.Error(), + stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth), + } +} + +// Stacked warp a go error and with caller stacks. alias of WithStack() +func Stacked(err error) error { + if err == nil { + return nil + } + return &ErrorX{ + msg: err.Error(), + stack: callersStack(stdOpt.SkipDepth, stdOpt.TraceDepth), + } +} + +// WithOptions new error with some option func +func WithOptions(msg string, fns ...func(opt *ErrStackOpt)) error { + opt := newErrOpt() + for _, fn := range fns { + fn(opt) + } + + return &ErrorX{ + msg: msg, + stack: callersStack(opt.SkipDepth, opt.TraceDepth), + } +} + +/************************************************************* + * helper func for wrap error without stacks + *************************************************************/ + +// Wrap error and with message, but not with stack +func Wrap(err error, msg string) error { + if err == nil { + return errors.New(msg) + } + + return &ErrorX{ + msg: msg, + prev: err, + } +} + +// Wrapf error with format message, but not with stack +func Wrapf(err error, tpl string, vars ...any) error { + if err == nil { + return fmt.Errorf(tpl, vars...) + } + + return &ErrorX{ + msg: fmt.Sprintf(tpl, vars...), + prev: err, + } +} diff --git a/vendor/github.com/gookit/goutil/errorx/stack.go b/vendor/github.com/gookit/goutil/errorx/stack.go new file mode 100644 index 00000000..13f78e58 --- /dev/null +++ b/vendor/github.com/gookit/goutil/errorx/stack.go @@ -0,0 +1,187 @@ +package errorx + +import ( + "bytes" + "fmt" + "io" + "path" + "runtime" + "strconv" +) + +// stack represents a stack of program counters. +type stack []uintptr + +// Format stack trace +func (s *stack) Format(fs fmt.State, verb rune) { + switch verb { + // case 'v', 's': + case 'v': + _, _ = s.WriteTo(fs) + } +} + +// StackLen for error +func (s *stack) StackLen() int { + return len(*s) +} + +// WriteTo for error +func (s *stack) WriteTo(w io.Writer) (int64, error) { + if len(*s) == 0 { + return 0, nil + } + + nn, _ := w.Write([]byte("\nSTACK:\n")) + for _, pc := range *s { + // For historical reasons if pc is interpreted as a uintptr + // its value represents the program counter + 1. + fc := runtime.FuncForPC(pc - 1) + if fc == nil { + continue + } + + // file eg: workspace/godev/gookit/goutil/errorx/errorx_test.go + file, line := fc.FileLine(pc - 1) + // f.Name() eg: github.com/gookit/goutil/errorx_test.TestWithPrev() + location := fc.Name() + "()\n " + file + ":" + strconv.Itoa(line) + "\n" + + n, _ := w.Write([]byte(location)) + nn += n + } + + return int64(nn), nil +} + +// String format to string +func (s *stack) String() string { + var buf bytes.Buffer + _, _ = s.WriteTo(&buf) + return buf.String() +} + +// StackFrames stack frame list +func (s *stack) StackFrames() *runtime.Frames { + return runtime.CallersFrames(*s) +} + +// CallerPC the caller PC value in the stack. it is first frame. +func (s *stack) CallerPC() uintptr { + if len(*s) == 0 { + return 0 + } + + // For historical reasons if pc is interpreted as a uintptr + // its value represents the program counter + 1. + return (*s)[0] - 1 +} + +/************************************************************* + * For error caller func + *************************************************************/ + +// Func struct +type Func struct { + *runtime.Func + pc uintptr +} + +// FuncForPC create. +func FuncForPC(pc uintptr) *Func { + fc := runtime.FuncForPC(pc) + if fc == nil { + return nil + } + + return &Func{ + pc: pc, + Func: fc, + } +} + +// FileLine of the func +func (f *Func) FileLine() (file string, line int) { + return f.Func.FileLine(f.pc) +} + +// Location simple location info for the func +// +// Returns eg: +// +// github.com/gookit/goutil/errorx_test.TestWithPrev(), errorx_test.go:34 +func (f *Func) Location() string { + file, line := f.FileLine() + + return f.Name() + "(), " + path.Base(file) + ":" + strconv.Itoa(line) +} + +// String of the func +// +// Returns eg: +// +// github.com/gookit/goutil/errorx_test.TestWithPrev() +// At /path/to/github.com/gookit/goutil/errorx_test.go:34 +func (f *Func) String() string { + file, line := f.FileLine() + + return f.Name() + "()\n At " + file + ":" + strconv.Itoa(line) +} + +// MarshalText handle +func (f *Func) MarshalText() ([]byte, error) { + return []byte(f.String()), nil +} + +/************************************************************* + * helper func for callers stacks + *************************************************************/ + +// ErrStackOpt struct +type ErrStackOpt struct { + SkipDepth int + TraceDepth int +} + +// default option +var stdOpt = newErrOpt() + +// ResetStdOpt config +func ResetStdOpt() { + stdOpt = newErrOpt() +} + +func newErrOpt() *ErrStackOpt { + return &ErrStackOpt{ + SkipDepth: 3, + TraceDepth: 8, + } +} + +// Config the stdOpt setting +func Config(fns ...func(opt *ErrStackOpt)) { + for _, fn := range fns { + fn(stdOpt) + } +} + +// SkipDepth setting +func SkipDepth(skipDepth int) func(opt *ErrStackOpt) { + return func(opt *ErrStackOpt) { + opt.SkipDepth = skipDepth + } +} + +// TraceDepth setting +func TraceDepth(traceDepth int) func(opt *ErrStackOpt) { + return func(opt *ErrStackOpt) { + opt.TraceDepth = traceDepth + } +} + +func callersStack(skip, depth int) *stack { + pcs := make([]uintptr, depth) + num := runtime.Callers(skip, pcs[:]) + + var st stack = pcs[0:num] + return &st +} diff --git a/vendor/github.com/gookit/goutil/errorx/util.go b/vendor/github.com/gookit/goutil/errorx/util.go new file mode 100644 index 00000000..54cf9ea6 --- /dev/null +++ b/vendor/github.com/gookit/goutil/errorx/util.go @@ -0,0 +1,88 @@ +package errorx + +import ( + "errors" + "fmt" +) + +// Raw new a raw go error. alias of errors.New() +func Raw(msg string) error { + return errors.New(msg) +} + +// Rawf new a raw go error. alias of errors.New() +func Rawf(tpl string, vars ...any) error { + return fmt.Errorf(tpl, vars...) +} + +/************************************************************* + * helper func for error + *************************************************************/ + +// Cause returns the first cause error by call err.Cause(). +// Otherwise, will returns current error. +func Cause(err error) error { + if err == nil { + return nil + } + + if err, ok := err.(Causer); ok { + return err.Cause() + } + return err +} + +// Unwrap returns previous error by call err.Unwrap(). +// Otherwise, will returns nil. +func Unwrap(err error) error { + if err == nil { + return nil + } + + if err, ok := err.(Unwrapper); ok { + return err.Unwrap() + } + return nil +} + +// Previous alias of Unwrap() +func Previous(err error) error { return Unwrap(err) } + +// ToErrorX convert check +func ToErrorX(err error) (ex *ErrorX, ok bool) { + ex, ok = err.(*ErrorX) + return +} + +// Has check err has contains target, or err is eq target. +// alias of errors.Is() +func Has(err, target error) bool { + return errors.Is(err, target) +} + +// Is alias of errors.Is() +func Is(err, target error) bool { + return errors.Is(err, target) +} + +// To try convert err to target, returns is result. +// +// NOTICE: target must be ptr and not nil. alias of errors.As() +// +// Usage: +// +// var ex *errorx.ErrorX +// err := doSomething() +// if errorx.To(err, &ex) { +// fmt.Println(ex.GoString()) +// } +func To(err error, target any) bool { + return errors.As(err, target) +} + +// As same of the To(), alias of errors.As() +// +// NOTICE: target must be ptr and not nil +func As(err error, target any) bool { + return errors.As(err, target) +} diff --git a/vendor/github.com/gookit/goutil/fmtutil/fmtutil.go b/vendor/github.com/gookit/goutil/fmtutil/fmtutil.go new file mode 100644 index 00000000..fcfd27dd --- /dev/null +++ b/vendor/github.com/gookit/goutil/fmtutil/fmtutil.go @@ -0,0 +1,2 @@ +// Package fmtutil provide some format util functions. +package fmtutil diff --git a/vendor/github.com/gookit/goutil/fmtutil/format.go b/vendor/github.com/gookit/goutil/fmtutil/format.go index 488bc5e1..a403d40c 100644 --- a/vendor/github.com/gookit/goutil/fmtutil/format.go +++ b/vendor/github.com/gookit/goutil/fmtutil/format.go @@ -4,6 +4,8 @@ import ( "encoding/json" "fmt" "strconv" + "strings" + "unicode" ) // data size @@ -16,29 +18,77 @@ const ( // DataSize format bytes number friendly. // // Usage: -// file, err := os.Open(path) -// fl, err := file.Stat() -// fmtSize := DataSize(fl.Size()) -func DataSize(bytes uint64) string { +// +// file, err := os.Open(path) +// fl, err := file.Stat() +// fmtSize := DataSize(fl.Size()) +func DataSize(size uint64) string { switch { - case bytes < 1024: - return fmt.Sprintf("%dB", bytes) - case bytes < 1024*1024: - return fmt.Sprintf("%.2fK", float64(bytes)/1024) - case bytes < 1024*1024*1024: - return fmt.Sprintf("%.2fM", float64(bytes)/1024/1024) + case size < 1024: + return fmt.Sprintf("%dB", size) + case size < 1024*1024: + return fmt.Sprintf("%.2fK", float64(size)/1024) + case size < 1024*1024*1024: + return fmt.Sprintf("%.2fM", float64(size)/1024/1024) default: - return fmt.Sprintf("%.2fG", float64(bytes)/1024/1024/1024) + return fmt.Sprintf("%.2fG", float64(size)/1024/1024/1024) + } +} + +// SizeToString alias of the DataSize +func SizeToString(size uint64) string { return DataSize(size) } + +// StringToByte alias of the ParseByte +func StringToByte(sizeStr string) uint64 { return ParseByte(sizeStr) } + +// ParseByte converts size string like 1GB/1g or 12mb/12M into an unsigned integer number of bytes +func ParseByte(sizeStr string) uint64 { + sizeStr = strings.TrimSpace(sizeStr) + lastPos := len(sizeStr) - 1 + if lastPos < 1 { + return 0 + } + + if sizeStr[lastPos] == 'b' || sizeStr[lastPos] == 'B' { + // last second char is k,m,g + lastSec := sizeStr[lastPos-1] + if lastSec > 'A' { + lastPos -= 1 + } + } + + multiplier := float64(1) + switch unicode.ToLower(rune(sizeStr[lastPos])) { + case 'k': + multiplier = 1 << 10 + sizeStr = strings.TrimSpace(sizeStr[:lastPos]) + case 'm': + multiplier = 1 << 20 + sizeStr = strings.TrimSpace(sizeStr[:lastPos]) + case 'g': + multiplier = 1 << 30 + sizeStr = strings.TrimSpace(sizeStr[:lastPos]) + default: // b + multiplier = 1 + sizeStr = strings.TrimSpace(sizeStr[:lastPos]) } + + size, _ := strconv.ParseFloat(sizeStr, 64) + if size < 0 { + return 0 + } + + return uint64(size * multiplier) } // PrettyJSON get pretty Json string -func PrettyJSON(v interface{}) (string, error) { +func PrettyJSON(v any) (string, error) { out, err := json.MarshalIndent(v, "", " ") return string(out), err } -// StringsToInts string slice to int slice. alias of the arrutil.StringsToInts() +// StringsToInts string slice to int slice. +// Deprecated: please use the arrutil.StringsToInts() func StringsToInts(ss []string) (ints []int, err error) { for _, str := range ss { iVal, err := strconv.Atoi(str) @@ -52,7 +102,7 @@ func StringsToInts(ss []string) (ints []int, err error) { } // ArgsWithSpaces it like Println, will add spaces for each argument -func ArgsWithSpaces(args []interface{}) (message string) { +func ArgsWithSpaces(args []any) (message string) { if ln := len(args); ln == 0 { message = "" } else if ln == 1 { diff --git a/vendor/github.com/gookit/goutil/fsutil/check.go b/vendor/github.com/gookit/goutil/fsutil/check.go new file mode 100644 index 00000000..4411b398 --- /dev/null +++ b/vendor/github.com/gookit/goutil/fsutil/check.go @@ -0,0 +1,120 @@ +package fsutil + +import ( + "bytes" + "os" + "path" +) + +var ( + // DefaultDirPerm perm and flags for create log file + DefaultDirPerm os.FileMode = 0775 + DefaultFilePerm os.FileMode = 0665 + OnlyReadFilePerm os.FileMode = 0444 + + // DefaultFileFlags for create and write + DefaultFileFlags = os.O_CREATE | os.O_WRONLY | os.O_APPEND + // OnlyReadFileFlags open file for read + OnlyReadFileFlags = os.O_RDONLY +) + +// alias methods +var ( + DirExist = IsDir + FileExist = IsFile + PathExist = PathExists +) + +// PathExists reports whether the named file or directory exists. +func PathExists(path string) bool { + if path == "" { + return false + } + + if _, err := os.Stat(path); err != nil { + if os.IsNotExist(err) { + return false + } + } + return true +} + +// IsDir reports whether the named directory exists. +func IsDir(path string) bool { + if path == "" { + return false + } + + if fi, err := os.Stat(path); err == nil { + return fi.IsDir() + } + return false +} + +// FileExists reports whether the named file or directory exists. +func FileExists(path string) bool { + return IsFile(path) +} + +// IsFile reports whether the named file or directory exists. +func IsFile(path string) bool { + if path == "" { + return false + } + + if fi, err := os.Stat(path); err == nil { + return !fi.IsDir() + } + return false +} + +// IsAbsPath is abs path. +func IsAbsPath(aPath string) bool { + return path.IsAbs(aPath) +} + +// ImageMimeTypes refer net/http package +var ImageMimeTypes = map[string]string{ + "bmp": "image/bmp", + "gif": "image/gif", + "ief": "image/ief", + "jpg": "image/jpeg", + // "jpe": "image/jpeg", + "jpeg": "image/jpeg", + "png": "image/png", + "svg": "image/svg+xml", + "ico": "image/x-icon", + "webp": "image/webp", +} + +// IsImageFile check file is image file. +func IsImageFile(path string) bool { + mime := MimeType(path) + if mime == "" { + return false + } + + for _, imgMime := range ImageMimeTypes { + if imgMime == mime { + return true + } + } + return false +} + +// IsZipFile check is zip file. +// from https://blog.csdn.net/wangshubo1989/article/details/71743374 +func IsZipFile(filepath string) bool { + f, err := os.Open(filepath) + if err != nil { + return false + } + defer f.Close() + + buf := make([]byte, 4) + if n, err := f.Read(buf); err != nil || n < 4 { + return false + } + + return bytes.Equal(buf, []byte("PK\x03\x04")) +} diff --git a/vendor/github.com/gookit/goutil/fsutil/fsutil.go b/vendor/github.com/gookit/goutil/fsutil/fsutil.go new file mode 100644 index 00000000..36ac591b --- /dev/null +++ b/vendor/github.com/gookit/goutil/fsutil/fsutil.go @@ -0,0 +1,79 @@ +// Package fsutil Filesystem util functions, quick create, read and write file. eg: file and dir check, operate +package fsutil + +import ( + "io" + "net/http" + "os" +) + +const ( + // MimeSniffLen sniff Length, use for detect file mime type + MimeSniffLen = 512 +) + +// OSTempFile create a temp file on os.TempDir() +// +// Usage: +// +// fsutil.OSTempFile("example.*.txt") +func OSTempFile(pattern string) (*os.File, error) { + return os.CreateTemp(os.TempDir(), pattern) +} + +// TempFile is like os.CreateTemp, but can custom temp dir. +// +// Usage: +// +// fsutil.TempFile("", "example.*.txt") +func TempFile(dir, pattern string) (*os.File, error) { + return os.CreateTemp(dir, pattern) +} + +// OSTempDir creates a new temp dir on os.TempDir and return the temp dir path +// +// Usage: +// +// fsutil.OSTempDir("example.*") +func OSTempDir(pattern string) (string, error) { + return os.MkdirTemp(os.TempDir(), pattern) +} + +// TempDir creates a new temp dir and return the temp dir path +// +// Usage: +// +// fsutil.TempDir("", "example.*") +// fsutil.TempDir("testdata", "example.*") +func TempDir(dir, pattern string) (string, error) { + return os.MkdirTemp(dir, pattern) +} + +// MimeType get File Mime Type name. eg "image/png" +func MimeType(path string) (mime string) { + file, err := os.Open(path) + if err != nil { + return + } + + return ReaderMimeType(file) +} + +// ReaderMimeType get the io.Reader mimeType +// +// Usage: +// +// file, err := os.Open(filepath) +// if err != nil { +// return +// } +// mime := ReaderMimeType(file) +func ReaderMimeType(r io.Reader) (mime string) { + var buf [MimeSniffLen]byte + n, _ := io.ReadFull(r, buf[:]) + if n == 0 { + return "" + } + + return http.DetectContentType(buf[:n]) +} diff --git a/vendor/github.com/gookit/goutil/fsutil/info.go b/vendor/github.com/gookit/goutil/fsutil/info.go new file mode 100644 index 00000000..14e68066 --- /dev/null +++ b/vendor/github.com/gookit/goutil/fsutil/info.go @@ -0,0 +1,124 @@ +package fsutil + +import ( + "io/ioutil" + "os" + "path" + "path/filepath" + + "github.com/gookit/goutil/internal/comfunc" +) + +// Dir get dir path, without last name. +func Dir(fpath string) string { + return filepath.Dir(fpath) +} + +// PathName get file/dir name from full path +func PathName(fpath string) string { + return path.Base(fpath) +} + +// Name get file/dir name from full path +func Name(fpath string) string { + return filepath.Base(fpath) +} + +// FileExt get filename ext. alias of path.Ext() +func FileExt(fpath string) string { + return path.Ext(fpath) +} + +// Suffix get filename ext. alias of path.Ext() +func Suffix(fpath string) string { + return path.Ext(fpath) +} + +// Expand will parse first `~` as user home dir path. +func Expand(pathStr string) string { + return comfunc.ExpandPath(pathStr) +} + +// ExpandPath will parse `~` as user home dir path. +func ExpandPath(pathStr string) string { + return comfunc.ExpandPath(pathStr) +} + +// Realpath returns the shortest path name equivalent to path by purely lexical processing. +func Realpath(pathStr string) string { + return path.Clean(pathStr) +} + +// SplitPath splits path immediately following the final Separator, separating it into a directory and file name component +func SplitPath(pathStr string) (dir, name string) { + return filepath.Split(pathStr) +} + +// GlobWithFunc handle matched file +func GlobWithFunc(pattern string, fn func(filePath string) error) (err error) { + files, err := filepath.Glob(pattern) + if err != nil { + return err + } + + for _, filePath := range files { + err = fn(filePath) + if err != nil { + break + } + } + return +} + +type ( + // FilterFunc type for FindInDir + FilterFunc func(fPath string, fi os.FileInfo) bool + // HandleFunc type for FindInDir + HandleFunc func(fPath string, fi os.FileInfo) error +) + +// FindInDir code refer the go pkg: path/filepath.glob() +// +// filters: return false will skip the file. +func FindInDir(dir string, handleFn HandleFunc, filters ...FilterFunc) (e error) { + fi, err := os.Stat(dir) + if err != nil { + return // ignore I/O error + } + if !fi.IsDir() { + return // ignore I/O error + } + + // names, _ := d.Readdirnames(-1) + // sort.Strings(names) + + stats, err := ioutil.ReadDir(dir) + if err != nil { + return + } + + for _, fi := range stats { + baseName := fi.Name() + filePath := dir + "/" + baseName + + // call filters + if len(filters) > 0 { + var filtered = false + for _, filter := range filters { + if !filter(filePath, fi) { + filtered = true + break + } + } + + if filtered { + continue + } + } + + if err := handleFn(filePath, fi); err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/gookit/goutil/fsutil/operate.go b/vendor/github.com/gookit/goutil/fsutil/operate.go new file mode 100644 index 00000000..31025242 --- /dev/null +++ b/vendor/github.com/gookit/goutil/fsutil/operate.go @@ -0,0 +1,360 @@ +package fsutil + +import ( + "archive/zip" + "fmt" + "io" + "io/ioutil" + "os" + "path" + "path/filepath" + "strings" +) + +// Mkdir alias of os.MkdirAll() +func Mkdir(dirPath string, perm os.FileMode) error { + return os.MkdirAll(dirPath, perm) +} + +// MkParentDir quick create parent dir +func MkParentDir(fpath string) error { + dirPath := filepath.Dir(fpath) + if !IsDir(dirPath) { + return os.MkdirAll(dirPath, 0775) + } + return nil +} + +// DiscardReader anything from the reader +func DiscardReader(src io.Reader) { + _, _ = io.Copy(ioutil.Discard, src) +} + +// MustReadFile read file contents, will panic on error +func MustReadFile(filePath string) []byte { + bs, err := ioutil.ReadFile(filePath) + if err != nil { + panic(err) + } + + return bs +} + +// MustReadReader read contents from io.Reader, will panic on error +func MustReadReader(r io.Reader) []byte { + // TODO go 1.16+ bs, err := io.ReadAll(r) + bs, err := ioutil.ReadAll(r) + if err != nil { + panic(err) + } + return bs +} + +// GetContents read contents from path or io.Reader, will panic on error +func GetContents(in any) []byte { + if fPath, ok := in.(string); ok { + return MustReadFile(fPath) + } + + if r, ok := in.(io.Reader); ok { + return MustReadReader(r) + } + + panic("invalid type of input") +} + +// ReadExistFile read file contents if existed, will panic on error +func ReadExistFile(filePath string) []byte { + if IsFile(filePath) { + bs, err := ioutil.ReadFile(filePath) + if err != nil { + panic(err) + } + return bs + } + return nil +} + +// ************************************************************ +// open/create files +// ************************************************************ + +// some flag consts for open file +const ( + FsCWAFlags = os.O_CREATE | os.O_WRONLY | os.O_APPEND // create, append write-only + FsCWTFlags = os.O_CREATE | os.O_WRONLY | os.O_TRUNC // create, override write-only + FsCWFlags = os.O_CREATE | os.O_WRONLY // create, write-only + FsRFlags = os.O_RDONLY // read-only +) + +// OpenFile like os.OpenFile, but will auto create dir. +func OpenFile(filepath string, flag int, perm os.FileMode) (*os.File, error) { + fileDir := path.Dir(filepath) + if err := os.MkdirAll(fileDir, DefaultDirPerm); err != nil { + return nil, err + } + + file, err := os.OpenFile(filepath, flag, perm) + if err != nil { + return nil, err + } + return file, nil +} + +/* TODO MustOpenFile() */ + +// QuickOpenFile like os.OpenFile, open for write, if not exists, will create it. +// +// Tip: file flag default is FsCWAFlags +func QuickOpenFile(filepath string, fileFlag ...int) (*os.File, error) { + flag := FsCWAFlags + if len(fileFlag) > 0 { + flag = fileFlag[0] + } + + return OpenFile(filepath, flag, DefaultFilePerm) +} + +// OpenReadFile like os.OpenFile, open file for read contents +func OpenReadFile(filepath string) (*os.File, error) { + return os.OpenFile(filepath, FsRFlags, OnlyReadFilePerm) +} + +// CreateFile create file if not exists +// +// Usage: +// +// CreateFile("path/to/file.txt", 0664, 0666) +func CreateFile(fpath string, filePerm, dirPerm os.FileMode, fileFlag ...int) (*os.File, error) { + dirPath := path.Dir(fpath) + if !IsDir(dirPath) { + err := os.MkdirAll(dirPath, dirPerm) + if err != nil { + return nil, err + } + } + + flag := FsCWTFlags + if len(fileFlag) > 0 { + flag = fileFlag[0] + } + + return os.OpenFile(fpath, flag, filePerm) +} + +// MustCreateFile create file, will panic on error +func MustCreateFile(filePath string, filePerm, dirPerm os.FileMode) *os.File { + file, err := CreateFile(filePath, filePerm, dirPerm) + if err != nil { + panic(err) + } + return file +} + +// ************************************************************ +// write, copy files +// ************************************************************ + +// PutContents create file and write contents to file at once. +// +// data type allow: string, []byte, io.Reader +// +// Tip: file flag default is FsCWAFlags +// +// Usage: +// +// fsutil.PutContents(filePath, contents, fsutil.FsCWTFlags) +func PutContents(filePath string, data any, fileFlag ...int) (int, error) { + // create and open file + dstFile, err := QuickOpenFile(filePath, fileFlag...) + if err != nil { + return 0, err + } + + defer dstFile.Close() + switch typData := data.(type) { + case []byte: + return dstFile.Write(typData) + case string: + return dstFile.WriteString(typData) + case io.Reader: // eg: buffer + n, err := io.Copy(dstFile, typData) + return int(n), err + default: + panic("PutContents: data type only allow: []byte, string, io.Reader") + } +} + +// WriteFile create file and write contents to file, can set perm for file. +// +// data type allow: string, []byte, io.Reader +// +// Tip: file flag default is FsCWTFlags +// +// Usage: +// +// fsutil.WriteFile(filePath, contents, 0666, fsutil.FsCWAFlags) +func WriteFile(filePath string, data any, perm os.FileMode, fileFlag ...int) error { + flag := FsCWTFlags + if len(fileFlag) > 0 { + flag = fileFlag[0] + } + + f, err := os.OpenFile(filePath, flag, perm) + if err != nil { + return err + } + + switch typData := data.(type) { + case []byte: + _, err = f.Write(typData) + case string: + _, err = f.WriteString(typData) + case io.Reader: // eg: buffer + _, err = io.Copy(f, typData) + default: + _ = f.Close() + panic("WriteFile: data type only allow: []byte, string, io.Reader") + } + + if err1 := f.Close(); err1 != nil && err == nil { + err = err1 + } + return err +} + +// CopyFile copy a file to another file path. +func CopyFile(srcPath, dstPath string) error { + srcFile, err := os.OpenFile(srcPath, FsRFlags, 0) + if err != nil { + return err + } + defer srcFile.Close() + + // create and open file + dstFile, err := QuickOpenFile(dstPath, FsCWTFlags) + if err != nil { + return err + } + defer dstFile.Close() + + _, err = io.Copy(dstFile, srcFile) + return err +} + +// MustCopyFile copy file to another path. +func MustCopyFile(srcPath, dstPath string) { + err := CopyFile(srcPath, dstPath) + if err != nil { + panic(err) + } +} + +// ************************************************************ +// remove files +// ************************************************************ + +// alias methods +var ( + // MustRm removes the named file or (empty) directory. + MustRm = MustRemove + // QuietRm removes the named file or (empty) directory. + QuietRm = QuietRemove +) + +// Remove removes the named file or (empty) directory. +func Remove(fPath string) error { + return os.Remove(fPath) +} + +// MustRemove removes the named file or (empty) directory. +// NOTICE: will panic on error +func MustRemove(fPath string) { + if err := os.Remove(fPath); err != nil { + panic(err) + } +} + +// QuietRemove removes the named file or (empty) directory. +// +// NOTICE: will ignore error +func QuietRemove(fPath string) { _ = os.Remove(fPath) } + +// RmIfExist removes the named file or (empty) directory on exists. +func RmIfExist(fPath string) error { return DeleteIfExist(fPath) } + +// DeleteIfExist removes the named file or (empty) directory on exists. +func DeleteIfExist(fPath string) error { + if PathExists(fPath) { + return os.Remove(fPath) + } + return nil +} + +// RmFileIfExist removes the named file on exists. +func RmFileIfExist(fPath string) error { return DeleteIfFileExist(fPath) } + +// DeleteIfFileExist removes the named file on exists. +func DeleteIfFileExist(fPath string) error { + if IsFile(fPath) { + return os.Remove(fPath) + } + return nil +} + +// ************************************************************ +// other operates +// ************************************************************ + +// Unzip a zip archive +// from https://blog.csdn.net/wangshubo1989/article/details/71743374 +func Unzip(archive, targetDir string) (err error) { + reader, err := zip.OpenReader(archive) + if err != nil { + return err + } + + if err = os.MkdirAll(targetDir, DefaultDirPerm); err != nil { + return + } + + for _, file := range reader.File { + + if strings.Contains(file.Name, "..") { + return fmt.Errorf("illegal file path in zip: %v", file.Name) + } + + fullPath := filepath.Join(targetDir, file.Name) + + if file.FileInfo().IsDir() { + err = os.MkdirAll(fullPath, file.Mode()) + if err != nil { + return err + } + continue + } + + fileReader, err := file.Open() + if err != nil { + return err + } + + targetFile, err := os.OpenFile(fullPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode()) + if err != nil { + _ = fileReader.Close() + return err + } + + _, err = io.Copy(targetFile, fileReader) + + // close all + _ = fileReader.Close() + targetFile.Close() + + if err != nil { + return err + } + } + + return +} diff --git a/vendor/github.com/gookit/goutil/goutil.go b/vendor/github.com/gookit/goutil/goutil.go index 76c08de1..97a1fe90 100644 --- a/vendor/github.com/gookit/goutil/goutil.go +++ b/vendor/github.com/gookit/goutil/goutil.go @@ -1,4 +1,4 @@ -// Package goutil 💪 Useful utils for the Go: int, string, array/slice, map, error, time, format, CLI, ENV, filesystem, +// Package goutil 💪 Useful utils for Go: int, string, array/slice, map, error, time, format, CLI, ENV, filesystem, // system, testing, debug and more. package goutil @@ -8,41 +8,56 @@ import ( "github.com/gookit/goutil/stdutil" ) -// PanicIfErr if error is not empty +// Value alias of stdutil.Value +type Value = stdutil.Value + +// Go is a basic promise implementation: it wraps calls a function in a goroutine +// and returns a channel which will later return the function's return value. +func Go(f func() error) error { + ch := make(chan error) + go func() { + ch <- f() + }() + return <-ch +} + +// PanicIfErr if error is not empty, will panic func PanicIfErr(err error) { if err != nil { panic(err) } } +// PanicErr if error is not empty, will panic +func PanicErr(err error) { + if err != nil { + panic(err) + } +} + +// MustOK if error is not empty, will panic +func MustOK(err error) { + if err != nil { + panic(err) + } +} + // Panicf format panic message use fmt.Sprintf -func Panicf(format string, v ...interface{}) { +func Panicf(format string, v ...any) { panic(fmt.Sprintf(format, v...)) } // FuncName get func name -func FuncName(f interface{}) string { +func FuncName(f any) string { return stdutil.FuncName(f) } -// PkgName get current package name +// PkgName get current package name. alias of stdutil.PkgName() // // Usage: +// // funcName := goutil.FuncName(fn) // pgkName := goutil.PkgName(funcName) func PkgName(funcName string) string { return stdutil.PkgName(funcName) } - -// GetCallStacks stacks is a wrapper for runtime. -// Stack that attempts to recover the data for all goroutines. -// from glog package -func GetCallStacks(all bool) []byte { - return stdutil.GetCallStacks(all) -} - -// GetCallersInfo returns an array of strings containing the file and line number -// of each stack frame leading -func GetCallersInfo(skip, max int) (callers []string) { - return stdutil.GetCallersInfo(skip, max) -} diff --git a/vendor/github.com/gookit/goutil/group.go b/vendor/github.com/gookit/goutil/group.go new file mode 100644 index 00000000..770c9e23 --- /dev/null +++ b/vendor/github.com/gookit/goutil/group.go @@ -0,0 +1,46 @@ +package goutil + +import ( + "context" + + "golang.org/x/sync/errgroup" +) + +// ErrGroup is a collection of goroutines working on subtasks that +// are part of the same overall task. +// +// Refers: +// +// https://github.com/neilotoole/errgroup +// https://github.com/fatih/semgroup +type ErrGroup struct { + *errgroup.Group +} + +// NewCtxErrGroup instance +func NewCtxErrGroup(ctx context.Context, limit ...int) (*ErrGroup, context.Context) { + egg, ctx1 := errgroup.WithContext(ctx) + if len(limit) > 0 && limit[0] > 0 { + egg.SetLimit(limit[0]) + } + + eg := &ErrGroup{Group: egg} + return eg, ctx1 +} + +// NewErrGroup instance +func NewErrGroup(limit ...int) *ErrGroup { + eg := &ErrGroup{Group: new(errgroup.Group)} + + if len(limit) > 0 && limit[0] > 0 { + eg.SetLimit(limit[0]) + } + return eg +} + +// Add one or more handler at once +func (g *ErrGroup) Add(handlers ...func() error) { + for _, handler := range handlers { + g.Go(handler) + } +} diff --git a/vendor/github.com/gookit/goutil/internal/comfunc/cmdline.go b/vendor/github.com/gookit/goutil/internal/comfunc/cmdline.go new file mode 100644 index 00000000..b9b474a9 --- /dev/null +++ b/vendor/github.com/gookit/goutil/internal/comfunc/cmdline.go @@ -0,0 +1,31 @@ +package comfunc + +import ( + "fmt" + "strings" +) + +// Cmdline build +func Cmdline(args []string, binName ...string) string { + b := new(strings.Builder) + + if len(binName) > 0 { + b.WriteString(binName[0]) + b.WriteByte(' ') + } + + for i, a := range args { + if i > 0 { + b.WriteByte(' ') + } + + if strings.ContainsRune(a, '"') { + b.WriteString(fmt.Sprintf(`'%s'`, a)) + } else if a == "" || strings.ContainsRune(a, '\'') || strings.ContainsRune(a, ' ') { + b.WriteString(fmt.Sprintf(`"%s"`, a)) + } else { + b.WriteString(a) + } + } + return b.String() +} diff --git a/vendor/github.com/gookit/goutil/internal/comfunc/comfunc.go b/vendor/github.com/gookit/goutil/internal/comfunc/comfunc.go new file mode 100644 index 00000000..017bed6c --- /dev/null +++ b/vendor/github.com/gookit/goutil/internal/comfunc/comfunc.go @@ -0,0 +1,79 @@ +package comfunc + +import ( + "os" + "regexp" + "strings" +) + +// Environ like os.Environ, but will returns key-value map[string]string data. +func Environ() map[string]string { + envList := os.Environ() + envMap := make(map[string]string, len(envList)) + + for _, str := range envList { + nodes := strings.SplitN(str, "=", 2) + + if len(nodes) < 2 { + envMap[nodes[0]] = "" + } else { + envMap[nodes[0]] = nodes[1] + } + } + return envMap +} + +// parse env value, allow: +// +// only key - "${SHELL}" +// with default - "${NotExist | defValue}" +// multi key - "${GOPATH}/${APP_ENV | prod}/dir" +// +// Notice: +// +// must add "?" - To ensure that there is no greedy match +// var envRegex = regexp.MustCompile(`\${[\w-| ]+}`) +var envRegex = regexp.MustCompile(`\${.+?}`) + +// ParseEnvVar parse ENV var value from input string, support default value. +// +// Format: +// +// ${var_name} Only var name +// ${var_name | default} With default value +// +// Usage: +// +// comfunc.ParseEnvVar("${ APP_NAME }") +// comfunc.ParseEnvVar("${ APP_ENV | dev }") +func ParseEnvVar(val string, getFn func(string) string) (newVal string) { + if !strings.Contains(val, "${") { + return val + } + + // default use os.Getenv + if getFn == nil { + getFn = os.Getenv + } + + var name, def string + return envRegex.ReplaceAllStringFunc(val, func(eVar string) string { + // eVar like "${NotExist|defValue}", first remove "${" and "}", then split it + ss := strings.SplitN(eVar[2:len(eVar)-1], "|", 2) + + // with default value. ${NotExist|defValue} + if len(ss) == 2 { + name, def = strings.TrimSpace(ss[0]), strings.TrimSpace(ss[1]) + } else { + def = eVar // use raw value + name = strings.TrimSpace(ss[0]) + } + + // get ENV value by name + eVal := getFn(name) + if eVal == "" { + eVal = def + } + return eVal + }) +} diff --git a/vendor/github.com/gookit/goutil/internal/comfunc/convert.go b/vendor/github.com/gookit/goutil/internal/comfunc/convert.go new file mode 100644 index 00000000..430882c1 --- /dev/null +++ b/vendor/github.com/gookit/goutil/internal/comfunc/convert.go @@ -0,0 +1,39 @@ +package comfunc + +import ( + "fmt" + "strings" + + "github.com/gookit/goutil/comdef" +) + +// Bool try to convert type to bool +func Bool(v any) bool { + bl, _ := ToBool(v) + return bl +} + +// ToBool try to convert type to bool +func ToBool(v any) (bool, error) { + if bl, ok := v.(bool); ok { + return bl, nil + } + + if str, ok := v.(string); ok { + return StrToBool(str) + } + return false, comdef.ErrConvType +} + +// StrToBool parse string to bool. like strconv.ParseBool() +func StrToBool(s string) (bool, error) { + lower := strings.ToLower(s) + switch lower { + case "1", "on", "yes", "true": + return true, nil + case "0", "off", "no", "false": + return false, nil + } + + return false, fmt.Errorf("'%s' cannot convert to bool", s) +} diff --git a/vendor/github.com/gookit/goutil/internal/comfunc/sysfunc.go b/vendor/github.com/gookit/goutil/internal/comfunc/sysfunc.go new file mode 100644 index 00000000..a7829671 --- /dev/null +++ b/vendor/github.com/gookit/goutil/internal/comfunc/sysfunc.go @@ -0,0 +1,111 @@ +package comfunc + +import ( + "bytes" + "os" + "os/exec" + "path/filepath" + "strings" +) + +// ExpandPath will parse first `~` as user home dir path. +func ExpandPath(pathStr string) string { + if len(pathStr) == 0 { + return pathStr + } + + if pathStr[0] != '~' { + return pathStr + } + + if len(pathStr) > 1 && pathStr[1] != '/' && pathStr[1] != '\\' { + return pathStr + } + + homeDir, err := os.UserHomeDir() + if err != nil { + return pathStr + } + + return homeDir + pathStr[1:] +} + +// ExecCmd an command and return output. +// +// Usage: +// +// ExecCmd("ls", []string{"-al"}) +func ExecCmd(binName string, args []string, workDir ...string) (string, error) { + // create a new Cmd instance + cmd := exec.Command(binName, args...) + if len(workDir) > 0 { + cmd.Dir = workDir[0] + } + + bs, err := cmd.Output() + return string(bs), err +} + +// ShellExec exec command by shell +// cmdLine eg. "ls -al" +func ShellExec(cmdLine string, shells ...string) (string, error) { + // shell := "/bin/sh" + shell := "sh" + if len(shells) > 0 { + shell = shells[0] + } + + var out bytes.Buffer + + cmd := exec.Command(shell, "-c", cmdLine) + cmd.Stdout = &out + + if err := cmd.Run(); err != nil { + return "", err + } + return out.String(), nil +} + +// curShell cache +var curShell string + +// CurrentShell get current used shell env file. +// +// eg "/bin/zsh" "/bin/bash". +// if onlyName=true, will return "zsh", "bash" +func CurrentShell(onlyName bool) (path string) { + var err error + if curShell == "" { + path, err = ShellExec("echo $SHELL") + if err != nil { + return "" + } + + path = strings.TrimSpace(path) + // cache result + curShell = path + } else { + path = curShell + } + + if onlyName && len(path) > 0 { + path = filepath.Base(path) + } + return +} + +// HasShellEnv has shell env check. +// +// Usage: +// +// HasShellEnv("sh") +// HasShellEnv("bash") +func HasShellEnv(shell string) bool { + // can also use: "echo $0" + out, err := ShellExec("echo OK", shell) + if err != nil { + return false + } + + return strings.TrimSpace(out) == "OK" +} diff --git a/vendor/github.com/gookit/goutil/jsonutil/jsonutil.go b/vendor/github.com/gookit/goutil/jsonutil/jsonutil.go deleted file mode 100644 index 6aa7047a..00000000 --- a/vendor/github.com/gookit/goutil/jsonutil/jsonutil.go +++ /dev/null @@ -1,117 +0,0 @@ -package jsonutil - -import ( - "bytes" - "encoding/json" - "io" - "io/ioutil" - "os" - "regexp" - "strings" - "text/scanner" -) - -// WriteFile write data to JSON file -func WriteFile(filePath string, data interface{}) error { - jsonBytes, err := Encode(data) - if err != nil { - return err - } - return ioutil.WriteFile(filePath, jsonBytes, 0664) -} - -// ReadFile Read JSON file data -func ReadFile(filePath string, v interface{}) error { - file, err := os.Open(filePath) - if err != nil { - return err - } - - defer file.Close() - return json.NewDecoder(file).Decode(v) -} - -// Pretty JSON string and return -func Pretty(v interface{}) (string, error) { - out, err := json.MarshalIndent(v, "", " ") - return string(out), err -} - -// Encode data to json bytes. -func Encode(v interface{}) ([]byte, error) { - return json.Marshal(v) -} - -// EncodePretty encode pretty JSON data to json bytes. -func EncodePretty(v interface{}) ([]byte, error) { - return json.MarshalIndent(v, "", " ") -} - -// EncodeToWriter encode data to writer. -func EncodeToWriter(v interface{}, w io.Writer) error { - return json.NewEncoder(w).Encode(v) -} - -// EncodeUnescapeHTML data to json bytes. will close escape HTML -func EncodeUnescapeHTML(v interface{}) ([]byte, error) { - buf := &bytes.Buffer{} - enc := json.NewEncoder(buf) - enc.SetEscapeHTML(false) - - err := enc.Encode(v) - if err != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -// Decode json bytes to data ptr. -func Decode(bts []byte, ptr interface{}) error { - return json.Unmarshal(bts, ptr) -} - -// DecodeString json string to data ptr. -func DecodeString(str string, ptr interface{}) error { - return json.Unmarshal([]byte(str), ptr) -} - -// DecodeReader decode JSON from io reader. -func DecodeReader(r io.Reader, ptr interface{}) error { - return json.NewDecoder(r).Decode(ptr) -} - -// `(?s:` enable match multi line -var jsonMLComments = regexp.MustCompile(`(?s:/\*.*?\*/\s*)`) - -// StripComments strip comments for a JSON string -func StripComments(src string) string { - // multi line comments - if strings.Contains(src, "/*") { - src = jsonMLComments.ReplaceAllString(src, "") - } - - // single line comments - if !strings.Contains(src, "//") { - return strings.TrimSpace(src) - } - - // strip inline comments - var s scanner.Scanner - - s.Init(strings.NewReader(src)) - s.Filename = "comments" - s.Mode ^= scanner.SkipComments // don't skip comments - - buf := new(bytes.Buffer) - for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() { - txt := s.TokenText() - if !strings.HasPrefix(txt, "//") && !strings.HasPrefix(txt, "/*") { - buf.WriteString(txt) - // } else { - // fmt.Printf("%s: %s\n", s.Position, txt) - } - } - - return buf.String() -} diff --git a/vendor/github.com/gookit/goutil/maputil/README.md b/vendor/github.com/gookit/goutil/maputil/README.md new file mode 100644 index 00000000..e5805564 --- /dev/null +++ b/vendor/github.com/gookit/goutil/maputil/README.md @@ -0,0 +1,72 @@ +# Map Utils + +`maputil` provide map data util functions. eg: convert, sub-value get, simple merge + +- use `map[string]any` as Data +- deep get value by key path +- deep set value by key path + +## Install + +```bash +go get github.com/gookit/goutil/maputil +``` + +## Go docs + +- [Go docs](https://pkg.go.dev/github.com/gookit/goutil/maputil) + +## Usage + +### Deep get value + +```go +mp := map[string]any { + "top1": "val1", + "arr1": []string{"ab", "cd"} + "map1": map[string]any{ + "sub1": "val2", + }, +} + +fmt.Println(maputil.DeepGet(mp, "map1.sub1")) // Output: VAL3 + +// get value from slice. +fmt.Println(maputil.DeepGet(mp, "arr1.1")) // Output: cd +fmt.Println(maputil.DeepGet(mp, "arr1[1]")) // Output: cd +``` + +### Deep set value + +```go +mp := map[string]any { + "top1": "val1", + "arr1": []string{"ab"} + "map1": map[string]any{ + "sub1": "val2", + }, +} + +err := maputil.SetByPath(&mp, "map1.newKey", "VAL3") + +fmt.Println(maputil.DeepGet(mp, "map1.newKey")) // Output: VAL3 +``` + +## Code Check & Testing + +```bash +gofmt -w -l ./ +golint ./... +``` + +**Testing**: + +```shell +go test -v ./maputil/... +``` + +**Test limit by regexp**: + +```shell +go test -v -run ^TestSetByKeys ./maputil/... +``` diff --git a/vendor/github.com/gookit/goutil/maputil/alias.go b/vendor/github.com/gookit/goutil/maputil/alias.go new file mode 100644 index 00000000..78618c45 --- /dev/null +++ b/vendor/github.com/gookit/goutil/maputil/alias.go @@ -0,0 +1,44 @@ +package maputil + +import "fmt" + +// Aliases implemented a simple string alias map. +type Aliases map[string]string + +// AddAlias to the Aliases +func (as Aliases) AddAlias(real, alias string) { + if rn, ok := as[alias]; ok { + panic(fmt.Sprintf("The alias '%s' is already used by '%s'", alias, rn)) + } + as[alias] = real +} + +// AddAliases to the Aliases +func (as Aliases) AddAliases(real string, aliases []string) { + for _, a := range aliases { + as.AddAlias(real, a) + } +} + +// AddAliasMap to the Aliases +func (as Aliases) AddAliasMap(alias2real map[string]string) { + for a, r := range alias2real { + as.AddAlias(r, a) + } +} + +// HasAlias in the Aliases +func (as Aliases) HasAlias(alias string) bool { + if _, ok := as[alias]; ok { + return true + } + return false +} + +// ResolveAlias by given name. +func (as Aliases) ResolveAlias(alias string) string { + if name, ok := as[alias]; ok { + return name + } + return alias +} diff --git a/vendor/github.com/gookit/goutil/maputil/check.go b/vendor/github.com/gookit/goutil/maputil/check.go new file mode 100644 index 00000000..d2c10ff2 --- /dev/null +++ b/vendor/github.com/gookit/goutil/maputil/check.go @@ -0,0 +1,46 @@ +package maputil + +import ( + "reflect" + + "github.com/gookit/goutil/reflects" +) + +// HasKey check of the given map. +func HasKey(mp, key any) (ok bool) { + rftVal := reflect.Indirect(reflect.ValueOf(mp)) + if rftVal.Kind() != reflect.Map { + return + } + + for _, keyRv := range rftVal.MapKeys() { + if reflects.IsEqual(keyRv.Interface(), key) { + return true + } + } + return +} + +// HasAllKeys check of the given map. +func HasAllKeys(mp any, keys ...any) (ok bool, noKey any) { + rftVal := reflect.Indirect(reflect.ValueOf(mp)) + if rftVal.Kind() != reflect.Map { + return + } + + for _, key := range keys { + var exist bool + for _, keyRv := range rftVal.MapKeys() { + if reflects.IsEqual(keyRv.Interface(), key) { + exist = true + break + } + } + + if !exist { + return false, key + } + } + + return true, nil +} diff --git a/vendor/github.com/gookit/goutil/maputil/convert.go b/vendor/github.com/gookit/goutil/maputil/convert.go new file mode 100644 index 00000000..da77b89f --- /dev/null +++ b/vendor/github.com/gookit/goutil/maputil/convert.go @@ -0,0 +1,108 @@ +package maputil + +import ( + "reflect" + "strings" + + "github.com/gookit/goutil/reflects" + "github.com/gookit/goutil/strutil" +) + +// KeyToLower convert keys to lower case. +func KeyToLower(src map[string]string) map[string]string { + newMp := make(map[string]string, len(src)) + for k, v := range src { + k = strings.ToLower(k) + newMp[k] = v + } + + return newMp +} + +// ToStringMap convert map[string]any to map[string]string +func ToStringMap(src map[string]any) map[string]string { + newMp := make(map[string]string, len(src)) + for k, v := range src { + newMp[k] = strutil.MustString(v) + } + + return newMp +} + +// HttpQueryString convert map[string]any data to http query string. +func HttpQueryString(data map[string]any) string { + ss := make([]string, 0, len(data)) + for k, v := range data { + ss = append(ss, k+"="+strutil.QuietString(v)) + } + + return strings.Join(ss, "&") +} + +// ToString simple and quickly convert map[string]any to string. +func ToString(mp map[string]any) string { + if mp == nil { + return "" + } + if len(mp) == 0 { + return "{}" + } + + buf := make([]byte, 0, len(mp)*16) + buf = append(buf, '{') + + for k, val := range mp { + buf = append(buf, k...) + buf = append(buf, ':') + + str := strutil.QuietString(val) + buf = append(buf, str...) + buf = append(buf, ',', ' ') + } + + // remove last ', ' + buf = append(buf[:len(buf)-2], '}') + return strutil.Byte2str(buf) +} + +// ToString2 simple and quickly convert a map to string. +func ToString2(mp any) string { + return NewFormatter(mp).Format() +} + +// FormatIndent format map data to string with newline and indent. +func FormatIndent(mp any, indent string) string { + return NewFormatter(mp).WithIndent(indent).Format() +} + +/************************************************************* + * Flat convert tree map to flatten key-value map. + *************************************************************/ + +// Flatten convert tree map to flat key-value map. +// +// Examples: +// +// {"top": {"sub": "value", "sub2": "value2"} } +// -> +// {"top.sub": "value", "top.sub2": "value2" } +func Flatten(mp map[string]any) map[string]any { + if mp == nil { + return nil + } + + flatMp := make(map[string]any, len(mp)*2) + reflects.FlatMap(reflect.ValueOf(mp), func(path string, val reflect.Value) { + flatMp[path] = val.Interface() + }) + + return flatMp +} + +// FlatWithFunc flat a tree-map with custom collect handle func +func FlatWithFunc(mp map[string]any, fn reflects.FlatFunc) { + if mp == nil || fn == nil { + return + } + reflects.FlatMap(reflect.ValueOf(mp), fn) +} diff --git a/vendor/github.com/gookit/goutil/maputil/data.go b/vendor/github.com/gookit/goutil/maputil/data.go new file mode 100644 index 00000000..6720d514 --- /dev/null +++ b/vendor/github.com/gookit/goutil/maputil/data.go @@ -0,0 +1,219 @@ +package maputil + +import ( + "strings" + + "github.com/gookit/goutil/mathutil" + "github.com/gookit/goutil/strutil" +) + +// Data an map data type +type Data map[string]any +type Map = Data + +// Has value on the data map +func (d Data) Has(key string) bool { + _, ok := d.GetByPath(key) + return ok +} + +// IsEmtpy if the data map +func (d Data) IsEmtpy() bool { + return len(d) == 0 +} + +// Value get from the data map +func (d Data) Value(key string) (any, bool) { + val, ok := d.GetByPath(key) + return val, ok +} + +// Get value from the data map. +// Supports dot syntax to get deep values. eg: top.sub +func (d Data) Get(key string) any { + if val, ok := d.GetByPath(key); ok { + return val + } + return nil +} + +// GetByPath get value from the data map by path. eg: top.sub +// Supports dot syntax to get deep values. +func (d Data) GetByPath(path string) (any, bool) { + if val, ok := d[path]; ok { + return val, true + } + + // is key path. + if strings.ContainsRune(path, '.') { + val, ok := GetByPath(path, d) + if ok { + return val, true + } + } + return nil, false +} + +// Set value to the data map +func (d Data) Set(key string, val any) { + d[key] = val +} + +// SetByPath sets a value in the map. +// Supports dot syntax to set deep values. +// +// For example: +// +// d.SetByPath("name.first", "Mat") +func (d Data) SetByPath(path string, value any) error { + if path == "" { + return nil + } + return d.SetByKeys(strings.Split(path, KeySepStr), value) +} + +// SetByKeys sets a value in the map by path keys. +// Supports dot syntax to set deep values. +// +// For example: +// +// d.SetByKeys([]string{"name", "first"}, "Mat") +func (d Data) SetByKeys(keys []string, value any) error { + kln := len(keys) + if kln == 0 { + return nil + } + + // special handle d is empty. + if len(d) == 0 { + if kln == 1 { + d.Set(keys[0], value) + } else { + d.Set(keys[0], MakeByKeys(keys[1:], value)) + } + return nil + } + + return SetByKeys((*map[string]any)(&d), keys, value) + // It's ok, but use `func (d *Data)` + // return SetByKeys((*map[string]any)(d), keys, value) +} + +// Default get value from the data map with default value +func (d Data) Default(key string, def any) any { + if val, ok := d.GetByPath(key); ok { + return val + } + return def +} + +// Int value get +func (d Data) Int(key string) int { + if val, ok := d.GetByPath(key); ok { + return mathutil.QuietInt(val) + } + return 0 +} + +// Int64 value get +func (d Data) Int64(key string) int64 { + if val, ok := d.GetByPath(key); ok { + return mathutil.QuietInt64(val) + } + return 0 +} + +// Str value get by key +func (d Data) Str(key string) string { + if val, ok := d.GetByPath(key); ok { + return strutil.QuietString(val) + } + return "" +} + +// Bool value get +func (d Data) Bool(key string) bool { + val, ok := d.GetByPath(key) + if !ok { + return false + } + if bl, ok := val.(bool); ok { + return bl + } + + if str, ok := val.(string); ok { + return strutil.QuietBool(str) + } + return false +} + +// Strings get []string value +func (d Data) Strings(key string) []string { + val, ok := d.GetByPath(key) + if !ok { + return nil + } + + if ss, ok := val.([]string); ok { + return ss + } + return nil +} + +// StrSplit get strings by split key value +func (d Data) StrSplit(key, sep string) []string { + if val, ok := d.GetByPath(key); ok { + return strings.Split(strutil.QuietString(val), sep) + } + return nil +} + +// StringsByStr value get by key +func (d Data) StringsByStr(key string) []string { + if val, ok := d.GetByPath(key); ok { + return strings.Split(strutil.QuietString(val), ",") + } + return nil +} + +// StringMap get map[string]string value +func (d Data) StringMap(key string) map[string]string { + val, ok := d.GetByPath(key) + if !ok { + return nil + } + + if smp, ok := val.(map[string]string); ok { + return smp + } + return nil +} + +// Sub get sub value as new Data +func (d Data) Sub(key string) Data { + if val, ok := d.GetByPath(key); ok { + if sub, ok := val.(map[string]any); ok { + return sub + } + } + return nil +} + +// Keys of the data map +func (d Data) Keys() []string { + keys := make([]string, 0, len(d)) + for k := range d { + keys = append(keys, k) + } + return keys +} + +// ToStringMap convert to map[string]string +func (d Data) ToStringMap() map[string]string { + return ToStringMap(d) +} + +// String data to string +func (d Data) String() string { + return ToString(d) +} diff --git a/vendor/github.com/gookit/goutil/maputil/format.go b/vendor/github.com/gookit/goutil/maputil/format.go new file mode 100644 index 00000000..96e5be3a --- /dev/null +++ b/vendor/github.com/gookit/goutil/maputil/format.go @@ -0,0 +1,125 @@ +package maputil + +import ( + "io" + "reflect" + + "github.com/gookit/goutil/comdef" + "github.com/gookit/goutil/strutil" +) + +// MapFormatter struct +type MapFormatter struct { + comdef.BaseFormatter + // Prefix string for each element + Prefix string + // Indent string for each element + Indent string + // ClosePrefix string for last "}" + ClosePrefix string + // AfterReset after reset on call Format(). + // AfterReset bool +} + +// NewFormatter instance +func NewFormatter(mp any) *MapFormatter { + f := &MapFormatter{} + f.Src = mp + + return f +} + +// WithFn for config self +func (f *MapFormatter) WithFn(fn func(f *MapFormatter)) *MapFormatter { + fn(f) + return f +} + +// WithIndent string +func (f *MapFormatter) WithIndent(indent string) *MapFormatter { + f.Indent = indent + return f +} + +// FormatTo to custom buffer +func (f *MapFormatter) FormatTo(w io.Writer) { + f.SetOutput(w) + f.doFormat() +} + +// Format to string +func (f *MapFormatter) String() string { + return f.Format() +} + +// Format to string +func (f *MapFormatter) Format() string { + f.doFormat() + return f.BsWriter().String() +} + +// Format map data to string. +// +//goland:noinspection GoUnhandledErrorResult +func (f *MapFormatter) doFormat() { + if f.Src == nil { + return + } + + rv, ok := f.Src.(reflect.Value) + if !ok { + rv = reflect.ValueOf(f.Src) + } + + rv = reflect.Indirect(rv) + if rv.Kind() != reflect.Map { + return + } + + buf := f.BsWriter() + ln := rv.Len() + if ln == 0 { + buf.WriteString("{}") + return + } + + // buf.Grow(ln * 16) + buf.WriteByte('{') + + indentLn := len(f.Indent) + if indentLn > 0 { + buf.WriteByte('\n') + } + + for i, key := range rv.MapKeys() { + kStr := strutil.QuietString(key.Interface()) + if indentLn > 0 { + buf.WriteString(f.Indent) + } + + buf.WriteString(kStr) + buf.WriteByte(':') + + vStr := strutil.QuietString(rv.MapIndex(key).Interface()) + buf.WriteString(vStr) + if i < ln-1 { + buf.WriteByte(',') + + // no indent, with space + if indentLn == 0 { + buf.WriteByte(' ') + } + } + + // with newline + if indentLn > 0 { + buf.WriteByte('\n') + } + } + + if f.ClosePrefix != "" { + buf.WriteString(f.ClosePrefix) + } + + buf.WriteByte('}') +} diff --git a/vendor/github.com/gookit/goutil/maputil/get.go b/vendor/github.com/gookit/goutil/maputil/get.go new file mode 100644 index 00000000..151d918c --- /dev/null +++ b/vendor/github.com/gookit/goutil/maputil/get.go @@ -0,0 +1,114 @@ +package maputil + +import ( + "reflect" + "strconv" + "strings" +) + +// DeepGet value by key path. eg "top" "top.sub" +func DeepGet(mp map[string]any, path string) (val any) { + val, _ = GetByPath(path, mp) + return +} + +// QuietGet value by key path. eg "top" "top.sub" +func QuietGet(mp map[string]any, path string) (val any) { + val, _ = GetByPath(path, mp) + return +} + +// GetByPath get value by key path from a map(map[string]any). eg "top" "top.sub" +func GetByPath(path string, mp map[string]any) (val any, ok bool) { + if val, ok := mp[path]; ok { + return val, true + } + + // no sub key + if len(mp) == 0 || !strings.ContainsRune(path, '.') { + return nil, false + } + + // has sub key. eg. "top.sub" + keys := strings.Split(path, ".") + topK := keys[0] + + // find top item data use top key + var item any + if item, ok = mp[topK]; !ok { + return + } + + for _, k := range keys[1:] { + switch tData := item.(type) { + case map[string]string: // is simple map + if item, ok = tData[k]; !ok { + return + } + case map[string]any: // is map(decode from toml/json) + if item, ok = tData[k]; !ok { + return + } + case map[any]any: // is map(decode from yaml) + if item, ok = tData[k]; !ok { + return + } + case []any: // is a slice + if item, ok = getBySlice(k, tData); !ok { + return + } + case []string, []int, []float32, []float64, []bool, []rune: + slice := reflect.ValueOf(tData) + sData := make([]any, slice.Len()) + for i := 0; i < slice.Len(); i++ { + sData[i] = slice.Index(i).Interface() + } + if item, ok = getBySlice(k, sData); !ok { + return + } + default: // error + return nil, false + } + } + + return item, true +} + +func getBySlice(k string, slice []any) (val any, ok bool) { + i, err := strconv.ParseInt(k, 10, 64) + if err != nil { + return nil, false + } + if size := int64(len(slice)); i >= size { + return nil, false + } + return slice[i], true +} + +// Keys get all keys of the given map. +func Keys(mp any) (keys []string) { + rftVal := reflect.Indirect(reflect.ValueOf(mp)) + if rftVal.Kind() != reflect.Map { + return + } + + keys = make([]string, 0, rftVal.Len()) + for _, key := range rftVal.MapKeys() { + keys = append(keys, key.String()) + } + return +} + +// Values get all values from the given map. +func Values(mp any) (values []any) { + rftVal := reflect.Indirect(reflect.ValueOf(mp)) + if rftVal.Kind() != reflect.Map { + return + } + + values = make([]any, 0, rftVal.Len()) + for _, key := range rftVal.MapKeys() { + values = append(values, rftVal.MapIndex(key).Interface()) + } + return +} diff --git a/vendor/github.com/gookit/goutil/maputil/maputil.go b/vendor/github.com/gookit/goutil/maputil/maputil.go new file mode 100644 index 00000000..e661f06d --- /dev/null +++ b/vendor/github.com/gookit/goutil/maputil/maputil.go @@ -0,0 +1,103 @@ +// Package maputil provide map data util functions. eg: convert, sub-value get, simple merge +package maputil + +import ( + "reflect" + "strings" + + "github.com/gookit/goutil/arrutil" +) + +// Key, value sep char consts +const ( + ValSepStr = "," + ValSepChar = ',' + KeySepStr = "." + KeySepChar = '.' +) + +// MergeSMap simple merge two string map. merge src to dst map +func MergeSMap(src, dst map[string]string, ignoreCase bool) map[string]string { + return MergeStringMap(src, dst, ignoreCase) +} + +// MergeStringMap simple merge two string map. merge src to dst map +func MergeStringMap(src, dst map[string]string, ignoreCase bool) map[string]string { + for k, v := range src { + if ignoreCase { + k = strings.ToLower(k) + } + + dst[k] = v + } + return dst +} + +// MakeByPath build new value by key names +// +// Example: +// +// "site.info" +// -> +// map[string]any { +// site: {info: val} +// } +// +// // case 2, last key is slice: +// "site.tags[1]" +// -> +// map[string]any { +// site: {tags: [val]} +// } +func MakeByPath(path string, val any) (mp map[string]any) { + return MakeByKeys(strings.Split(path, KeySepStr), val) +} + +// MakeByKeys build new value by key names +// +// Example: +// +// // case 1: +// []string{"site", "info"} +// -> +// map[string]any { +// site: {info: val} +// } +// +// // case 2, last key is slice: +// []string{"site", "tags[1]"} +// -> +// map[string]any { +// site: {tags: [val]} +// } +func MakeByKeys(keys []string, val any) (mp map[string]any) { + size := len(keys) + + // if last key contains slice index, make slice wrap the val + lastKey := keys[size-1] + if newK, idx, ok := parseArrKeyIndex(lastKey); ok { + // valTyp := reflect.TypeOf(val) + sliTyp := reflect.SliceOf(reflect.TypeOf(val)) + sliVal := reflect.MakeSlice(sliTyp, idx+1, idx+1) + sliVal.Index(idx).Set(reflect.ValueOf(val)) + + // update val and last key + val = sliVal.Interface() + keys[size-1] = newK + } + + if size == 1 { + return map[string]any{keys[0]: val} + } + + // multi nodes + arrutil.Reverse(keys) + for _, p := range keys { + if mp == nil { + mp = map[string]any{p: val} + } else { + mp = map[string]any{p: mp} + } + } + return +} diff --git a/vendor/github.com/gookit/goutil/maputil/setval.go b/vendor/github.com/gookit/goutil/maputil/setval.go new file mode 100644 index 00000000..210181fc --- /dev/null +++ b/vendor/github.com/gookit/goutil/maputil/setval.go @@ -0,0 +1,340 @@ +package maputil + +import ( + "fmt" + "reflect" + "strconv" + "strings" + + "github.com/gookit/goutil/strutil" +) + +// SetByPath set sub-map value by key path. +// Supports dot syntax to set deep values. +// +// For example: +// +// SetByPath("name.first", "Mat") +func SetByPath(mp *map[string]any, path string, val any) error { + return SetByKeys(mp, strings.Split(path, KeySepStr), val) +} + +// SetByKeys set sub-map value by path keys. +// Supports dot syntax to set deep values. +// +// For example: +// +// SetByKeys([]string{"name", "first"}, "Mat") +func SetByKeys(mp *map[string]any, keys []string, val any) (err error) { + kln := len(keys) + if kln == 0 { + return nil + } + + mpv := *mp + if len(mpv) == 0 { + *mp = MakeByKeys(keys, val) + return nil + } + + topK := keys[0] + if kln == 1 { + mpv[topK] = val + return nil + } + + if _, ok := mpv[topK]; !ok { + mpv[topK] = MakeByKeys(keys[1:], val) + return nil + } + + rv := reflect.ValueOf(mp).Elem() + return setMapByKeys(rv, keys, reflect.ValueOf(val)) +} + +func setMapByKeys(rv reflect.Value, keys []string, nv reflect.Value) (err error) { + if rv.Kind() != reflect.Map { + return fmt.Errorf("input parameter#rv must be a Map, but was %s", rv.Kind()) + } + + // If the map is nil, make a new map + if rv.IsNil() { + mapType := reflect.MapOf(rv.Type().Key(), rv.Type().Elem()) + rv.Set(reflect.MakeMap(mapType)) + } + + var ok bool + maxI := len(keys) - 1 + for i, key := range keys { + idx := -1 + isMap := rv.Kind() == reflect.Map + isSlice := rv.Kind() == reflect.Slice + isLast := i == len(keys)-1 + + // slice index key must be ended on the keys. + // eg: "top.arr[2]" -> "arr[2]" + if pos := strings.IndexRune(key, '['); pos > 0 { + var realKey string + if realKey, idx, ok = parseArrKeyIndex(key); ok { + // update value + key = realKey + if !isMap { + err = fmt.Errorf( + "current value#%s type is %s, cannot get sub-value by key: %s", + strings.Join(keys[i:], "."), + rv.Kind(), + key, + ) + break + } + + rftK := reflect.ValueOf(key) + tmpV := rv.MapIndex(rftK) + if !tmpV.IsValid() { + if isLast { + sliVal := reflect.MakeSlice(reflect.SliceOf(nv.Type()), idx+1, idx+1) + sliVal.Index(idx).Set(nv) + rv.SetMapIndex(rftK, sliVal) + } else { + // deep make map by keys + newVal := MakeByKeys(keys[i+1:], nv.Interface()) + mpVal := reflect.ValueOf(newVal) + + sliVal := reflect.MakeSlice(reflect.SliceOf(mpVal.Type()), idx+1, idx+1) + sliVal.Index(idx).Set(mpVal) + + rv.SetMapIndex(rftK, sliVal) + } + break + } + + // get real type: any -> map + if tmpV.Kind() == reflect.Interface { + tmpV = tmpV.Elem() + } + + if tmpV.Kind() != reflect.Slice { + err = fmt.Errorf( + "current value#%s type is %s, cannot set sub by index: %d", + strings.Join(keys[i:], "."), + tmpV.Kind(), + idx, + ) + break + } + + wantLen := idx + 1 + sliLen := tmpV.Len() + elemTyp := tmpV.Type().Elem() + + if wantLen > sliLen { + newAdd := reflect.MakeSlice(tmpV.Type(), 0, wantLen-sliLen) + for i := 0; i < wantLen-sliLen; i++ { + newAdd = reflect.Append(newAdd, reflect.New(elemTyp).Elem()) + } + + tmpV = reflect.AppendSlice(tmpV, newAdd) + } + + if !isLast { + if elemTyp.Kind() == reflect.Map { + err := setMapByKeys(tmpV.Index(idx), keys[i+1:], nv) + if err != nil { + return err + } + + // tmpV.Index(idx).Set(elemV) + rv.SetMapIndex(rftK, tmpV) + } else { + err = fmt.Errorf( + "key %s[%d] elem must be map for set sub-value by remain path: %s", + key, + idx, + strings.Join(keys[i:], "."), + ) + } + } else { + // last - set value + tmpV.Index(idx).Set(nv) + rv.SetMapIndex(rftK, tmpV) + } + break + } + } + + // set value on last key + if isLast { + if isMap { + rv.SetMapIndex(reflect.ValueOf(key), nv) + break + } + + if isSlice { + // key is slice index + if strutil.IsNumeric(key) { + idx, _ = strconv.Atoi(key) + } + + if idx > -1 { + wantLen := idx + 1 + sliLen := rv.Len() + + if wantLen > sliLen { + elemTyp := rv.Type().Elem() + newAdd := reflect.MakeSlice(rv.Type(), 0, wantLen-sliLen) + + for i := 0; i < wantLen-sliLen; i++ { + newAdd = reflect.Append(newAdd, reflect.New(elemTyp).Elem()) + } + + if !rv.CanAddr() { + err = fmt.Errorf("cannot set value to a cannot addr slice, key: %s", key) + break + } + + rv.Set(reflect.AppendSlice(rv, newAdd)) + } + + rv.Index(idx).Set(nv) + } else { + err = fmt.Errorf("cannot set slice value by named key %q", key) + } + } else { + err = fmt.Errorf( + "cannot set sub-value for type %q(path %q, key %q)", + rv.Kind(), + strings.Join(keys[:i], "."), + key, + ) + } + + break + } + + if isMap { + rftK := reflect.ValueOf(key) + if tmpV := rv.MapIndex(rftK); tmpV.IsValid() { + var isPtr bool + // get real type: any -> map + tmpV, isPtr = getRealVal(tmpV) + if tmpV.Kind() == reflect.Map { + rv = tmpV + continue + } + + // sub is slice and is not ptr + if tmpV.Kind() == reflect.Slice { + if isPtr { + rv = tmpV + continue // to (E) + } + + // next key is index number. + nxtKey := keys[i+1] + if strutil.IsNumeric(nxtKey) { + idx, _ = strconv.Atoi(nxtKey) + sliLen := tmpV.Len() + wantLen := idx + 1 + + if wantLen > sliLen { + elemTyp := tmpV.Type().Elem() + newAdd := reflect.MakeSlice(tmpV.Type(), 0, wantLen-sliLen) + for i := 0; i < wantLen-sliLen; i++ { + newAdd = reflect.Append(newAdd, reflect.New(elemTyp).Elem()) + } + + tmpV = reflect.AppendSlice(tmpV, newAdd) + } + + // rv = tmpV.Index(idx) // TODO + if i+1 == maxI { + tmpV.Index(idx).Set(nv) + } else { + err := setMapByKeys(tmpV.Index(idx), keys[i+1:], nv) + if err != nil { + return err + } + } + + rv.SetMapIndex(rftK, tmpV) + } else { + err = fmt.Errorf("cannot set slice value by named key %s(parent: %s)", nxtKey, key) + } + } else { + err = fmt.Errorf( + "map item type is %s(path:%q), cannot set sub-value by path %q", + tmpV.Kind(), + strings.Join(keys[0:i+1], "."), + strings.Join(keys[i+1:], "."), + ) + } + } else { + // deep make map by keys + newVal := MakeByKeys(keys[i+1:], nv.Interface()) + rv.SetMapIndex(rftK, reflect.ValueOf(newVal)) + } + + break + } else if isSlice && strutil.IsNumeric(key) { // (E). slice from ptr slice + idx, _ = strconv.Atoi(key) + sliLen := rv.Len() + wantLen := idx + 1 + + if wantLen > sliLen { + elemTyp := rv.Type().Elem() + newAdd := reflect.MakeSlice(rv.Type(), 0, wantLen-sliLen) + for i := 0; i < wantLen-sliLen; i++ { + newAdd = reflect.Append(newAdd, reflect.New(elemTyp).Elem()) + } + + rv = reflect.AppendSlice(rv, newAdd) + } + + rv = rv.Index(idx) + } else { + err = fmt.Errorf( + "map item type is %s, cannot set sub-value by path %q", + rv.Kind(), + strings.Join(keys[i:], "."), + ) + } + } + return +} + +func getRealVal(rv reflect.Value) (reflect.Value, bool) { + // get real type: any -> map + if rv.Kind() == reflect.Interface { + rv = rv.Elem() + } + + isPtr := false + if rv.Kind() == reflect.Ptr { + isPtr = true + rv = rv.Elem() + } + + return rv, isPtr +} + +// "arr[2]" => "arr", 2, true +func parseArrKeyIndex(key string) (string, int, bool) { + pos := strings.IndexRune(key, '[') + if pos < 1 || !strings.HasSuffix(key, "]") { + return key, 0, false + } + + var idx int + var err error + + idxStr := key[pos+1 : len(key)-1] + if idxStr != "" { + idx, err = strconv.Atoi(idxStr) + if err != nil { + return key, 0, false + } + } + + key = key[:pos] + return key, idx, true +} diff --git a/vendor/github.com/gookit/goutil/maputil/smap.go b/vendor/github.com/gookit/goutil/maputil/smap.go new file mode 100644 index 00000000..d3859821 --- /dev/null +++ b/vendor/github.com/gookit/goutil/maputil/smap.go @@ -0,0 +1,117 @@ +package maputil + +import ( + "github.com/gookit/goutil/mathutil" + "github.com/gookit/goutil/strutil" +) + +// SMap is alias of map[string]string +type SMap map[string]string + +// IsEmpty of the data map +func (m SMap) IsEmpty() bool { + return len(m) == 0 +} + +// Has key on the data map +func (m SMap) Has(key string) bool { + _, ok := m[key] + return ok +} + +// HasValue on the data map +func (m SMap) HasValue(val string) bool { + for _, v := range m { + if v == val { + return true + } + } + return false +} + +// Value get from the data map +func (m SMap) Value(key string) (string, bool) { + val, ok := m[key] + return val, ok +} + +// Default get value by key. if not found, return defVal +func (m SMap) Default(key, defVal string) string { + if val, ok := m[key]; ok { + return val + } + return defVal +} + +// Get value by key +func (m SMap) Get(key string) string { + return m[key] +} + +// Int value get +func (m SMap) Int(key string) int { + if val, ok := m[key]; ok { + return mathutil.QuietInt(val) + } + return 0 +} + +// Int64 value get +func (m SMap) Int64(key string) int64 { + if val, ok := m[key]; ok { + return mathutil.QuietInt64(val) + } + return 0 +} + +// Str value get +func (m SMap) Str(key string) string { + return m[key] +} + +// Bool value get +func (m SMap) Bool(key string) bool { + if val, ok := m[key]; ok { + return strutil.QuietBool(val) + } + return false +} + +// Ints value to []int +func (m SMap) Ints(key string) []int { + if val, ok := m[key]; ok { + return strutil.Ints(val, ValSepStr) + } + return nil +} + +// Strings value to []string +func (m SMap) Strings(key string) (ss []string) { + if val, ok := m[key]; ok { + return strutil.ToSlice(val, ValSepStr) + } + return +} + +// Keys of the string-map +func (m SMap) Keys() []string { + keys := make([]string, 0, len(m)) + for k := range m { + keys = append(keys, k) + } + return keys +} + +// Values of the string-map +func (m SMap) Values() []string { + ss := make([]string, 0, len(m)) + for _, v := range m { + ss = append(ss, v) + } + return ss +} + +// String data to string +func (m SMap) String() string { + return ToString2(m) +} diff --git a/vendor/github.com/gookit/goutil/mathutil/README.md b/vendor/github.com/gookit/goutil/mathutil/README.md new file mode 100644 index 00000000..71c9a7a3 --- /dev/null +++ b/vendor/github.com/gookit/goutil/mathutil/README.md @@ -0,0 +1,28 @@ +# Math Utils + +- some features + +## Install + +```bash +go get github.com/gookit/goutil/mathutil +``` + +## Go docs + +- [Go docs](https://pkg.go.dev/github.com/gookit/goutil/mathutil) + +## Usage + + +## Testings + +```shell +go test -v ./mathutil/... +``` + +Test limit by regexp: + +```shell +go test -v -run ^TestSetByKeys ./mathutil/... +``` diff --git a/vendor/github.com/gookit/goutil/mathutil/check.go b/vendor/github.com/gookit/goutil/mathutil/check.go new file mode 100644 index 00000000..45c49927 --- /dev/null +++ b/vendor/github.com/gookit/goutil/mathutil/check.go @@ -0,0 +1,81 @@ +package mathutil + +// Compare intX,floatX value by given op. returns `srcVal op(=,!=,<,<=,>,>=) dstVal` +// +// Usage: +// +// mathutil.Compare(2, 3, ">") // false +// mathutil.Compare(2, 1.3, ">") // true +// mathutil.Compare(2.2, 1.3, ">") // true +// mathutil.Compare(2.1, 2, ">") // true +func Compare(srcVal, dstVal any, op string) (ok bool) { + if srcVal == nil || dstVal == nil { + return false + } + + // float + if srcFlt, ok := srcVal.(float64); ok { + if dstFlt, err := ToFloat(dstVal); err == nil { + return CompFloat(srcFlt, dstFlt, op) + } + return false + } + + if srcFlt, ok := srcVal.(float32); ok { + if dstFlt, err := ToFloat(dstVal); err == nil { + return CompFloat(float64(srcFlt), dstFlt, op) + } + return false + } + + // as int64 + srcInt, err := ToInt64(srcVal) + if err != nil { + return false + } + + dstInt, err := ToInt64(dstVal) + if err != nil { + return false + } + + return CompInt64(srcInt, dstInt, op) +} + +// CompInt64 compare int64, returns the srcI64 op dstI64 +func CompInt64(srcI64, dstI64 int64, op string) (ok bool) { + switch op { + case "<", "lt": + ok = srcI64 < dstI64 + case "<=", "lte": + ok = srcI64 <= dstI64 + case ">", "gt": + ok = srcI64 > dstI64 + case ">=", "gte": + ok = srcI64 >= dstI64 + case "=", "eq": + ok = srcI64 == dstI64 + case "!=", "ne", "neq": + ok = srcI64 != dstI64 + } + return +} + +// CompFloat compare float64 +func CompFloat(srcF64, dstF64 float64, op string) (ok bool) { + switch op { + case "<", "lt": + ok = srcF64 < dstF64 + case "<=", "lte": + ok = srcF64 <= dstF64 + case ">", "gt": + ok = srcF64 > dstF64 + case ">=", "gte": + ok = srcF64 >= dstF64 + case "=", "eq": + ok = srcF64 == dstF64 + case "!=", "ne", "neq": + ok = srcF64 != dstF64 + } + return +} diff --git a/vendor/github.com/gookit/goutil/mathutil/convert.go b/vendor/github.com/gookit/goutil/mathutil/convert.go index 65cf07a7..2b64ba68 100644 --- a/vendor/github.com/gookit/goutil/mathutil/convert.go +++ b/vendor/github.com/gookit/goutil/mathutil/convert.go @@ -2,17 +2,12 @@ package mathutil import ( "encoding/json" - "errors" "fmt" "strconv" "strings" "time" -) -var ( - // ErrConvertFail convert error - ErrConvertFail = errors.New("convert data type is failure") - // ErrConvertFail = errors.New("convert data type is failure") + "github.com/gookit/goutil/comdef" ) /************************************************************* @@ -20,18 +15,24 @@ var ( *************************************************************/ // Int convert value to int -func Int(in interface{}) (int, error) { +func Int(in any) (int, error) { return ToInt(in) } -// MustInt convert value to int -func MustInt(in interface{}) int { +// QuietInt convert value to int, will ignore error +func QuietInt(in any) int { + val, _ := ToInt(in) + return val +} + +// MustInt convert value to int, will panic on error +func MustInt(in any) int { val, _ := ToInt(in) return val } // IntOrPanic convert value to int, will panic on error -func IntOrPanic(in interface{}) int { +func IntOrPanic(in any) int { val, err := ToInt(in) if err != nil { panic(err) @@ -39,8 +40,13 @@ func IntOrPanic(in interface{}) int { return val } -// ToInt convert string to int -func ToInt(in interface{}) (iVal int, err error) { +// IntOrErr convert value to int, return error on failed +func IntOrErr(in any) (iVal int, err error) { + return ToInt(in) +} + +// ToInt convert value to int, return error on failed +func ToInt(in any) (iVal int, err error) { switch tVal := in.(type) { case nil: iVal = 0 @@ -77,28 +83,45 @@ func ToInt(in interface{}) (iVal int, err error) { i64, err = tVal.Int64() iVal = int(i64) default: - err = ErrConvertFail + err = comdef.ErrConvType } return } +// StrInt convert. +func StrInt(s string) int { + iVal, _ := strconv.Atoi(strings.TrimSpace(s)) + return iVal +} + /************************************************************* * convert value to uint *************************************************************/ -// Uint convert string to uint -func Uint(in interface{}) (uint64, error) { +// Uint convert string to uint, return error on failed +func Uint(in any) (uint64, error) { return ToUint(in) } -// MustUint convert string to uint -func MustUint(in interface{}) uint64 { +// QuietUint convert string to uint, will ignore error +func QuietUint(in any) uint64 { val, _ := ToUint(in) return val } -// ToUint convert string to uint -func ToUint(in interface{}) (u64 uint64, err error) { +// MustUint convert string to uint, will panic on error +func MustUint(in any) uint64 { + val, _ := ToUint(in) + return val +} + +// UintOrErr convert value to uint, return error on failed +func UintOrErr(in any) (uint64, error) { + return ToUint(in) +} + +// ToUint convert value to uint, return error on failed +func ToUint(in any) (u64 uint64, err error) { switch tVal := in.(type) { case nil: u64 = 0 @@ -135,7 +158,7 @@ func ToUint(in interface{}) (u64 uint64, err error) { case string: u64, err = strconv.ParseUint(strings.TrimSpace(tVal), 10, 0) default: - err = ErrConvertFail + err = comdef.ErrConvType } return } @@ -144,19 +167,32 @@ func ToUint(in interface{}) (u64 uint64, err error) { * convert value to int64 *************************************************************/ -// Int64 convert string to int64 -func Int64(in interface{}) (int64, error) { +// Int64 convert string to int64, return error on failed +func Int64(in any) (int64, error) { return ToInt64(in) } -// MustInt64 convert -func MustInt64(in interface{}) int64 { +// QuietInt64 convert value to int64, will ignore error +func QuietInt64(in any) int64 { + i64, _ := ToInt64(in) + return i64 +} + +// MustInt64 convert value to int64, will panic on error +func MustInt64(in any) int64 { i64, _ := ToInt64(in) return i64 } -// ToInt64 convert string to int64 -func ToInt64(in interface{}) (i64 int64, err error) { +// TODO StrictInt64,AsInt64 strict convert to int64 + +// Int64OrErr convert string to int64, return error on failed +func Int64OrErr(in any) (int64, error) { + return ToInt64(in) +} + +// ToInt64 convert string to int64, return error on failed +func ToInt64(in any) (i64 int64, err error) { switch tVal := in.(type) { case nil: i64 = 0 @@ -191,7 +227,7 @@ func ToInt64(in interface{}) (i64 int64, err error) { case json.Number: i64, err = tVal.Int64() default: - err = ErrConvertFail + err = comdef.ErrConvType } return } @@ -200,18 +236,44 @@ func ToInt64(in interface{}) (i64 int64, err error) { * convert value to float *************************************************************/ -// Float convert value to float64 -func Float(in interface{}) (float64, error) { +// QuietFloat convert value to float64, will ignore error +func QuietFloat(in any) float64 { + val, _ := ToFloat(in) + return val +} + +// FloatOrPanic convert value to float64, will panic on error +func FloatOrPanic(in any) float64 { + val, err := ToFloat(in) + if err != nil { + panic(err) + } + return val +} + +// MustFloat convert value to float64 TODO will panic on error +func MustFloat(in any) float64 { + val, _ := ToFloat(in) + return val +} + +// Float convert value to float64, return error on failed +func Float(in any) (float64, error) { + return ToFloat(in) +} + +// FloatOrErr convert value to float64, return error on failed +func FloatOrErr(in any) (float64, error) { return ToFloat(in) } -// ToFloat convert value to float64 -func ToFloat(in interface{}) (f64 float64, err error) { +// ToFloat convert value to float64, return error on failed +func ToFloat(in any) (f64 float64, err error) { switch tVal := in.(type) { case nil: f64 = 0 case string: - f64, err = strconv.ParseFloat(strings.TrimSpace(tVal), 0) + f64, err = strconv.ParseFloat(strings.TrimSpace(tVal), 64) case int: f64 = float64(tVal) case int8: @@ -241,34 +303,55 @@ func ToFloat(in interface{}) (f64 float64, err error) { case json.Number: f64, err = tVal.Float64() default: - err = ErrConvertFail + err = comdef.ErrConvType } return } -// FloatOrPanic convert value to float64, will panic on error -func FloatOrPanic(in interface{}) float64 { - val, err := ToFloat(in) +/************************************************************* + * convert intX/floatX to string + *************************************************************/ + +// StringOrPanic convert intX/floatX value to string, will panic on error +func StringOrPanic(val any) string { + str, err := TryToString(val, true) if err != nil { panic(err) } - return val + return str } -// MustFloat convert value to float64 TODO will panic on error -func MustFloat(in interface{}) float64 { - val, _ := ToFloat(in) - return val +// MustString convert intX/floatX value to string, will panic on error +func MustString(val any) string { + return StringOrPanic(val) } -/************************************************************* - * convert intX/floatX to string - *************************************************************/ +// ToString convert intX/floatX value to string, return error on failed +func ToString(val any) (string, error) { + return TryToString(val, true) +} + +// StringOrErr convert intX/floatX value to string, return error on failed +func StringOrErr(val any) (string, error) { + return TryToString(val, true) +} + +// QuietString convert intX/floatX value to string, other type convert by fmt.Sprint +func QuietString(val any) string { + str, _ := TryToString(val, false) + return str +} + +// String convert intX/floatX value to string, other type convert by fmt.Sprint +func String(val any) string { + str, _ := TryToString(val, false) + return str +} // TryToString try convert intX/floatX value to string // // if defaultAsErr is False, will use fmt.Sprint convert other type -func TryToString(val interface{}, defaultAsErr bool) (str string, err error) { +func TryToString(val any, defaultAsErr bool) (str string, err error) { if val == nil { return } @@ -304,35 +387,10 @@ func TryToString(val interface{}, defaultAsErr bool) (str string, err error) { str = value.String() default: if defaultAsErr { - err = ErrConvertFail + err = comdef.ErrConvType } else { str = fmt.Sprint(value) } } return } - -// StringOrPanic convert intX/floatX value to string, will panic on error -func StringOrPanic(val interface{}) string { - str, err := TryToString(val, true) - if err != nil { - panic(err) - } - return str -} - -// MustString convert intX/floatX value to string, will panic on error -func MustString(val interface{}) string { - return StringOrPanic(val) -} - -// ToString convert intX/floatX value to string -func ToString(val interface{}) (string, error) { - return TryToString(val, true) -} - -// String convert intX/floatX value to string, other type convert by fmt.Sprint -func String(val interface{}) string { - str, _ := TryToString(val, false) - return str -} diff --git a/vendor/github.com/gookit/goutil/mathutil/mathutil.go b/vendor/github.com/gookit/goutil/mathutil/mathutil.go new file mode 100644 index 00000000..c379efc5 --- /dev/null +++ b/vendor/github.com/gookit/goutil/mathutil/mathutil.go @@ -0,0 +1,40 @@ +package mathutil + +import "math" + +// MaxFloat compare and return max value +func MaxFloat(x, y float64) float64 { + return math.Max(x, y) +} + +// MaxInt compare and return max value +func MaxInt(x, y int) int { + if x > y { + return x + } + return y +} + +// SwapMaxInt compare and return max, min value +func SwapMaxInt(x, y int) (int, int) { + if x > y { + return x, y + } + return y, x +} + +// MaxI64 compare and return max value +func MaxI64(x, y int64) int64 { + if x > y { + return x + } + return y +} + +// SwapMaxI64 compare and return max, min value +func SwapMaxI64(x, y int64) (int64, int64) { + if x > y { + return x, y + } + return y, x +} diff --git a/vendor/github.com/gookit/goutil/mathutil/random.go b/vendor/github.com/gookit/goutil/mathutil/random.go index 58a613a4..477b56ef 100644 --- a/vendor/github.com/gookit/goutil/mathutil/random.go +++ b/vendor/github.com/gookit/goutil/mathutil/random.go @@ -8,9 +8,10 @@ import ( // RandomInt return a random int at the [min, max) // // Usage: -// RandomInt(10, 99) -// RandomInt(100, 999) -// RandomInt(1000, 9999) +// +// RandomInt(10, 99) +// RandomInt(100, 999) +// RandomInt(1000, 9999) func RandomInt(min, max int) int { rand.Seed(time.Now().UnixNano()) return min + rand.Intn(max-min) @@ -27,8 +28,9 @@ func RandIntWithSeed(min, max int, seed int64) int { // RandomIntWithSeed return a random int at the [min, max) // // Usage: -// seed := time.Now().UnixNano() -// RandomIntWithSeed(1000, 9999, seed) +// +// seed := time.Now().UnixNano() +// RandomIntWithSeed(1000, 9999, seed) func RandomIntWithSeed(min, max int, seed int64) int { rand.Seed(seed) return min + rand.Intn(max-min) diff --git a/vendor/github.com/gookit/goutil/reflects/README.md b/vendor/github.com/gookit/goutil/reflects/README.md new file mode 100644 index 00000000..84269fe6 --- /dev/null +++ b/vendor/github.com/gookit/goutil/reflects/README.md @@ -0,0 +1,30 @@ +# Reflects + +`reflects` Provide extends reflect util functions. + +- some features + +## Install + +```bash +go get github.com/gookit/goutil/reflects +``` + +## Go docs + +- [Go docs](https://pkg.go.dev/github.com/gookit/goutil/reflects) + +## Usage + + +## Testings + +```shell +go test -v ./reflects/... +``` + +Test limit by regexp: + +```shell +go test -v -run ^TestSetByKeys ./reflects/... +``` diff --git a/vendor/github.com/gookit/goutil/reflects/check.go b/vendor/github.com/gookit/goutil/reflects/check.go new file mode 100644 index 00000000..c7e5c039 --- /dev/null +++ b/vendor/github.com/gookit/goutil/reflects/check.go @@ -0,0 +1,110 @@ +package reflects + +import ( + "bytes" + "reflect" +) + +// HasChild check. eg: array, slice, map, struct +func HasChild(v reflect.Value) bool { + switch v.Kind() { + case reflect.Array, reflect.Slice, reflect.Map, reflect.Struct: + return true + } + return false +} + +// IsNil reflect value +func IsNil(v reflect.Value) bool { + switch v.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + return v.IsNil() + default: + return false + } +} + +// IsFunc value +func IsFunc(val any) bool { + if val == nil { + return false + } + return reflect.TypeOf(val).Kind() == reflect.Func +} + +// IsEqual determines if two objects are considered equal. +// +// TIP: cannot compare function type +func IsEqual(src, dst any) bool { + if src == nil || dst == nil { + return src == dst + } + + bs1, ok := src.([]byte) + if !ok { + return reflect.DeepEqual(src, dst) + } + + bs2, ok := dst.([]byte) + if !ok { + return false + } + + if bs1 == nil || bs2 == nil { + return bs1 == nil && bs2 == nil + } + return bytes.Equal(bs1, bs2) +} + +// IsEmpty reflect value check +func IsEmpty(v reflect.Value) bool { + switch v.Kind() { + case reflect.Invalid: + return true + case reflect.String, reflect.Array: + return v.Len() == 0 + case reflect.Map, reflect.Slice: + return v.Len() == 0 || v.IsNil() + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr, reflect.Func: + return v.IsNil() + } + + return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) +} + +// IsEmptyValue reflect value check. +// Difference the IsEmpty(), if value is ptr, will check real elem. +// +// From src/pkg/encoding/json/encode.go. +func IsEmptyValue(v reflect.Value) bool { + switch v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + if v.IsNil() { + return true + } + return IsEmptyValue(v.Elem()) + case reflect.Func: + return v.IsNil() + case reflect.Invalid: + return true + } + return false +} diff --git a/vendor/github.com/gookit/goutil/reflects/conv.go b/vendor/github.com/gookit/goutil/reflects/conv.go new file mode 100644 index 00000000..16fa6fe9 --- /dev/null +++ b/vendor/github.com/gookit/goutil/reflects/conv.go @@ -0,0 +1,161 @@ +package reflects + +import ( + "fmt" + "reflect" + "strconv" + + "github.com/gookit/goutil/comdef" + "github.com/gookit/goutil/internal/comfunc" + "github.com/gookit/goutil/mathutil" + "github.com/gookit/goutil/strutil" +) + +// BaseTypeVal convert custom type or intX,uintX,floatX to generic base type. +// +// intX/unitX => int64 +// floatX => float64 +// string => string +// +// returns int64,string,float or error +func BaseTypeVal(v reflect.Value) (value any, err error) { + v = reflect.Indirect(v) + + switch v.Kind() { + case reflect.String: + value = v.String() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + value = v.Int() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + value = int64(v.Uint()) // always return int64 + case reflect.Float32, reflect.Float64: + value = v.Float() + default: + err = comdef.ErrConvType + } + return +} + +// ValueByType create reflect.Value by give reflect.Type +func ValueByType(val any, typ reflect.Type) (rv reflect.Value, err error) { + if typ.Kind() <= reflect.Float64 { + return ValueByKind(val, typ.Kind()) + } + + // check type. like map, slice + newRv := reflect.ValueOf(val) + if newRv.Type() == typ { + return newRv, nil + } + + err = comdef.ErrConvType + return +} + +// ValueByKind create reflect.Value by give reflect.Kind +// +// TIPs: +// +// Only support kind: string, bool, intX, uintX, floatX +func ValueByKind(val any, kind reflect.Kind) (rv reflect.Value, err error) { + switch kind { + case reflect.Int: + if dstV, err1 := mathutil.ToInt(val); err1 == nil { + rv = reflect.ValueOf(dstV) + } + case reflect.Int8: + if dstV, err1 := mathutil.ToInt(val); err1 == nil { + rv = reflect.ValueOf(int8(dstV)) + } + case reflect.Int16: + if dstV, err1 := mathutil.ToInt(val); err1 == nil { + rv = reflect.ValueOf(int16(dstV)) + } + case reflect.Int32: + if dstV, err1 := mathutil.ToInt(val); err1 == nil { + rv = reflect.ValueOf(int32(dstV)) + } + case reflect.Int64: + if dstV, err1 := mathutil.ToInt64(val); err1 == nil { + rv = reflect.ValueOf(dstV) + } + case reflect.Uint: + if dstV, err1 := mathutil.ToUint(val); err1 == nil { + rv = reflect.ValueOf(uint(dstV)) + } + case reflect.Uint8: + if dstV, err1 := mathutil.ToUint(val); err1 == nil { + rv = reflect.ValueOf(uint8(dstV)) + } + case reflect.Uint16: + if dstV, err1 := mathutil.ToUint(val); err1 == nil { + rv = reflect.ValueOf(uint16(dstV)) + } + case reflect.Uint32: + if dstV, err1 := mathutil.ToUint(val); err1 == nil { + rv = reflect.ValueOf(uint32(dstV)) + } + case reflect.Uint64: + if dstV, err1 := mathutil.ToUint(val); err1 == nil { + rv = reflect.ValueOf(dstV) + } + case reflect.Float32: + if dstV, err1 := mathutil.ToFloat(val); err1 == nil { + rv = reflect.ValueOf(float32(dstV)) + } + case reflect.Float64: + if dstV, err1 := mathutil.ToFloat(val); err1 == nil { + rv = reflect.ValueOf(dstV) + } + case reflect.String: + if dstV, err1 := strutil.ToString(val); err1 == nil { + rv = reflect.ValueOf(dstV) + } + case reflect.Bool: + if bl, err := comfunc.ToBool(val); err == nil { + rv = reflect.ValueOf(bl) + } + } + + if !rv.IsValid() { + err = comdef.ErrConvType + } + return +} + +// String convert +func String(rv reflect.Value) string { + s, _ := ValToString(rv, false) + return s +} + +// ToString convert +func ToString(rv reflect.Value) (str string, err error) { + return ValToString(rv, true) +} + +// ValToString convert handle +func ValToString(rv reflect.Value, defaultAsErr bool) (str string, err error) { + rv = Indirect(rv) + switch rv.Kind() { + case reflect.Invalid: + str = "" + case reflect.Bool: + str = strconv.FormatBool(rv.Bool()) + case reflect.String: + str = rv.String() + case reflect.Float32, reflect.Float64: + str = strconv.FormatFloat(rv.Float(), 'f', -1, 64) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + str = strconv.FormatInt(rv.Int(), 10) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + str = strconv.FormatUint(rv.Uint(), 10) + default: + if defaultAsErr { + err = comdef.ErrConvType + } else { + str = fmt.Sprint(rv.Interface()) + } + } + return +} diff --git a/vendor/github.com/gookit/goutil/reflects/reflects.go b/vendor/github.com/gookit/goutil/reflects/reflects.go new file mode 100644 index 00000000..2cbdae9c --- /dev/null +++ b/vendor/github.com/gookit/goutil/reflects/reflects.go @@ -0,0 +1,2 @@ +// Package reflects Provide extends reflect util functions. +package reflects diff --git a/vendor/github.com/gookit/goutil/reflects/type.go b/vendor/github.com/gookit/goutil/reflects/type.go new file mode 100644 index 00000000..06470963 --- /dev/null +++ b/vendor/github.com/gookit/goutil/reflects/type.go @@ -0,0 +1,71 @@ +package reflects + +import "reflect" + +// BKind base data kind type +type BKind uint + +// base kinds +const ( + // Int for all intX types + Int = BKind(reflect.Int) + // Uint for all uintX types + Uint = BKind(reflect.Uint) + // Float for all floatX types + Float = BKind(reflect.Float32) + // Array for array,slice types + Array = BKind(reflect.Array) + // Complex for all complexX types + Complex = BKind(reflect.Complex64) +) + +// ToBaseKind convert reflect.Kind to base kind +func ToBaseKind(kind reflect.Kind) BKind { + return ToBKind(kind) +} + +// ToBKind convert reflect.Kind to base kind +func ToBKind(kind reflect.Kind) BKind { + switch kind { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return Int + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return Uint + case reflect.Float32, reflect.Float64: + return Float + case reflect.Complex64, reflect.Complex128: + return Complex + case reflect.Array, reflect.Slice: + return Array + default: + // like: string, map, struct, ptr, func, interface ... + return BKind(kind) + } +} + +// Type struct +type Type interface { + reflect.Type + // BaseKind value + BaseKind() BKind +} + +type xType struct { + reflect.Type + baseKind BKind +} + +// TypeOf value +func TypeOf(v any) Type { + rftTyp := reflect.TypeOf(v) + + return &xType{ + Type: rftTyp, + baseKind: ToBKind(rftTyp.Kind()), + } +} + +// BaseKind value +func (t *xType) BaseKind() BKind { + return t.baseKind +} diff --git a/vendor/github.com/gookit/goutil/reflects/util.go b/vendor/github.com/gookit/goutil/reflects/util.go new file mode 100644 index 00000000..41b21e81 --- /dev/null +++ b/vendor/github.com/gookit/goutil/reflects/util.go @@ -0,0 +1,141 @@ +package reflects + +import ( + "fmt" + "reflect" + "strconv" +) + +// Elem returns the value that the interface v contains +// or that the pointer v points to. +func Elem(v reflect.Value) reflect.Value { + if v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface { + return v.Elem() + } + + // otherwise, will return self + return v +} + +// Indirect like reflect.Indirect(), but can also indirect reflect.Interface +func Indirect(v reflect.Value) reflect.Value { + if v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface { + return v.Elem() + } + + // otherwise, will return self + return v +} + +// Len get reflect value length +func Len(v reflect.Value) int { + v = reflect.Indirect(v) + + // (u)int use width. + switch v.Kind() { + case reflect.String: + return len([]rune(v.String())) + case reflect.Map, reflect.Array, reflect.Chan, reflect.Slice: + return v.Len() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return len(strconv.FormatInt(int64(v.Uint()), 10)) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return len(strconv.FormatInt(v.Int(), 10)) + case reflect.Float32, reflect.Float64: + return len(fmt.Sprint(v.Interface())) + } + + // cannot get length + return -1 +} + +// SliceSubKind get sub-elem kind of the array, slice, variadic-var. +// +// Usage: +// +// SliceSubKind(reflect.TypeOf([]string{"abc"})) // reflect.String +func SliceSubKind(typ reflect.Type) reflect.Kind { + if typ.Kind() == reflect.Slice { + return typ.Elem().Kind() + } + return reflect.Invalid +} + +// SetValue to a reflect.Value +func SetValue(rv reflect.Value, val any) error { + // get real type of the ptr value + if rv.Kind() == reflect.Ptr { + // init if is nil + if rv.IsNil() { + elemTyp := rv.Type().Elem() + rv.Set(reflect.New(elemTyp)) + } + + // use elem for set value + rv = reflect.Indirect(rv) + } + + rv1, err := ValueByType(val, rv.Type()) + if err != nil { + return err + } + + rv.Set(rv1) + return nil +} + +// FlatFunc custom collect handle func +type FlatFunc func(path string, val reflect.Value) + +// FlatMap process tree map to flat key-value map. +// +// Examples: +// +// {"top": {"sub": "value", "sub2": "value2"} } +// -> +// {"top.sub": "value", "top.sub2": "value2" } +func FlatMap(rv reflect.Value, fn FlatFunc) { + if fn == nil { + return + } + + if rv.Kind() != reflect.Map { + panic("only allow flat map data") + } + flatMap(rv, fn, "") +} + +func flatMap(rv reflect.Value, fn FlatFunc, parent string) { + for _, key := range rv.MapKeys() { + path := String(key) + if parent != "" { + path = parent + "." + path + } + + fv := Indirect(rv.MapIndex(key)) + switch fv.Kind() { + case reflect.Map: + flatMap(fv, fn, path) + case reflect.Array, reflect.Slice: + flatSlice(fv, fn, path) + default: + fn(path, fv) + } + } +} + +func flatSlice(rv reflect.Value, fn FlatFunc, parent string) { + for i := 0; i < rv.Len(); i++ { + path := parent + "[" + strconv.Itoa(i) + "]" + fv := Indirect(rv.Index(i)) + + switch fv.Kind() { + case reflect.Map: + flatMap(fv, fn, path) + case reflect.Array, reflect.Slice: + flatSlice(fv, fn, path) + default: + fn(path, fv) + } + } +} diff --git a/vendor/github.com/gookit/goutil/reflects/value.go b/vendor/github.com/gookit/goutil/reflects/value.go new file mode 100644 index 00000000..3b360ada --- /dev/null +++ b/vendor/github.com/gookit/goutil/reflects/value.go @@ -0,0 +1,109 @@ +package reflects + +import "reflect" + +// Value struct +type Value struct { + reflect.Value + baseKind BKind +} + +// Wrap the give value +func Wrap(rv reflect.Value) Value { + return Value{ + Value: rv, + baseKind: ToBKind(rv.Kind()), + } +} + +// ValueOf the give value +func ValueOf(v any) Value { + if rv, ok := v.(reflect.Value); ok { + return Wrap(rv) + } + + rv := reflect.ValueOf(v) + return Value{ + Value: rv, + baseKind: ToBKind(rv.Kind()), + } +} + +// Indirect value. alias of the reflect.Indirect() +func (v Value) Indirect() Value { + if v.Kind() != reflect.Ptr { + return v + } + + elem := v.Value.Elem() + return Value{ + Value: elem, + baseKind: ToBKind(elem.Kind()), + } +} + +// Elem returns the value that the interface v contains or that the pointer v points to. +// +// TIP: not like reflect.Value.Elem. otherwise, will return self. +func (v Value) Elem() Value { + if v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface { + elem := v.Value.Elem() + + return Value{ + Value: elem, + baseKind: ToBKind(elem.Kind()), + } + } + + // otherwise, will return self + return v +} + +// Type of value. +func (v Value) Type() Type { + return &xType{ + Type: v.Value.Type(), + baseKind: v.baseKind, + } +} + +// BKind value +func (v Value) BKind() BKind { + return v.baseKind +} + +// BaseKind value +func (v Value) BaseKind() BKind { + return v.baseKind +} + +// HasChild check. eg: array, slice, map, struct +func (v Value) HasChild() bool { + switch v.Kind() { + case reflect.Array, reflect.Slice, reflect.Map, reflect.Struct: + return true + } + return false +} + +// Int value. if is uintX will convert to int64 +func (v Value) Int() int64 { + switch v.baseKind { + case Uint: + return int64(v.Value.Uint()) + case Int: + return v.Value.Int() + } + panic(&reflect.ValueError{Method: "reflect.Value.Int", Kind: v.Kind()}) +} + +// Uint value. if is intX will convert to uint64 +func (v Value) Uint() uint64 { + switch v.baseKind { + case Uint: + return v.Value.Uint() + case Int: + return uint64(v.Value.Int()) + } + panic(&reflect.ValueError{Method: "reflect.Value.Uint", Kind: v.Kind()}) +} diff --git a/vendor/github.com/gookit/goutil/stdio/README.md b/vendor/github.com/gookit/goutil/stdio/README.md new file mode 100644 index 00000000..5a8370c6 --- /dev/null +++ b/vendor/github.com/gookit/goutil/stdio/README.md @@ -0,0 +1,29 @@ +# Stdio + +`stdio` provide some standard IO util functions. + +## Install + +```bash +go get github.com/gookit/goutil/stdio +``` + +## Go docs + +- [Go docs](https://pkg.go.dev/github.com/gookit/goutil/stdio) + +## Usage + +Please see tests. + +## Testings + +```shell +go test -v ./stdio/... +``` + +Test limit by regexp: + +```shell +go test -v -run ^TestSetByKeys ./stdio/... +``` diff --git a/vendor/github.com/gookit/goutil/stdio/ioutil.go b/vendor/github.com/gookit/goutil/stdio/ioutil.go new file mode 100644 index 00000000..64b0861e --- /dev/null +++ b/vendor/github.com/gookit/goutil/stdio/ioutil.go @@ -0,0 +1,41 @@ +package stdio + +import ( + "fmt" + "io" + "strings" +) + +// QuietFprint to writer, will ignore error +func QuietFprint(w io.Writer, ss ...string) { + _, _ = fmt.Fprint(w, strings.Join(ss, "")) +} + +// QuietFprintf to writer, will ignore error +func QuietFprintf(w io.Writer, tpl string, vs ...any) { + _, _ = fmt.Fprintf(w, tpl, vs...) +} + +// QuietFprintln to writer, will ignore error +func QuietFprintln(w io.Writer, ss ...string) { + _, _ = fmt.Fprintln(w, strings.Join(ss, "")) +} + +// QuietWriteString to writer, will ignore error +func QuietWriteString(w io.Writer, ss ...string) { + _, _ = io.WriteString(w, strings.Join(ss, "")) +} + +// DiscardReader anything from the reader +func DiscardReader(src io.Reader) { + _, _ = io.Copy(io.Discard, src) +} + +// MustReadReader read contents from io.Reader, will panic on error +func MustReadReader(r io.Reader) []byte { + bs, err := io.ReadAll(r) + if err != nil { + panic(err) + } + return bs +} diff --git a/vendor/github.com/gookit/goutil/stdio/stdio.go b/vendor/github.com/gookit/goutil/stdio/stdio.go new file mode 100644 index 00000000..8a0146e8 --- /dev/null +++ b/vendor/github.com/gookit/goutil/stdio/stdio.go @@ -0,0 +1,2 @@ +// Package stdio provide some standard IO util functions. +package stdio diff --git a/vendor/github.com/gookit/goutil/stdio/writer.go b/vendor/github.com/gookit/goutil/stdio/writer.go new file mode 100644 index 00000000..76070cbb --- /dev/null +++ b/vendor/github.com/gookit/goutil/stdio/writer.go @@ -0,0 +1,45 @@ +package stdio + +import ( + "fmt" + "io" +) + +// WriteWrapper warp io.Writer support more operate methods. +type WriteWrapper struct { + Out io.Writer +} + +// NewWriteWrapper instance +func NewWriteWrapper(w io.Writer) *WriteWrapper { + return &WriteWrapper{Out: w} +} + +// Write bytes data +func (w *WriteWrapper) Write(p []byte) (n int, err error) { + return w.Out.Write(p) +} + +// Writef data to output +func (w *WriteWrapper) Writef(tpl string, vs ...any) (n int, err error) { + return fmt.Fprintf(w.Out, tpl, vs...) +} + +// WriteByte data +func (w *WriteWrapper) WriteByte(c byte) error { + _, err := w.Out.Write([]byte{c}) + return err +} + +// WriteString data +func (w *WriteWrapper) WriteString(s string) (n int, err error) { + return w.Out.Write([]byte(s)) +} + +// String get write data string +func (w *WriteWrapper) String() string { + if sw, ok := w.Out.(fmt.Stringer); ok { + return sw.String() + } + return "" +} diff --git a/vendor/github.com/gookit/goutil/stdutil/README.md b/vendor/github.com/gookit/goutil/stdutil/README.md new file mode 100644 index 00000000..a554d468 --- /dev/null +++ b/vendor/github.com/gookit/goutil/stdutil/README.md @@ -0,0 +1,28 @@ +# Std Utils + +- some features + +## Install + +```bash +go get github.com/gookit/goutil/stdutils +``` + +## Go docs + +- [Go docs](https://pkg.go.dev/github.com/gookit/goutil/stdutils) + +## Usage + + +## Testings + +```shell +go test -v ./stdutils/... +``` + +Test limit by regexp: + +```shell +go test -v -run ^TestSetByKeys ./stdutils/... +``` diff --git a/vendor/github.com/gookit/goutil/stdutil/chan.go b/vendor/github.com/gookit/goutil/stdutil/chan.go index 4e3aaf2f..084835f7 100644 --- a/vendor/github.com/gookit/goutil/stdutil/chan.go +++ b/vendor/github.com/gookit/goutil/stdutil/chan.go @@ -1,6 +1,8 @@ package stdutil import ( + "context" + "fmt" "io" "os" "os/signal" @@ -10,7 +12,7 @@ import ( // WaitCloseSignals for some huang program. func WaitCloseSignals(closer io.Closer) error { signals := make(chan os.Signal, 1) - signal.Notify(signals, os.Interrupt, os.Kill, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) + signal.Notify(signals, os.Interrupt, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) <-signals return closer.Close() @@ -26,3 +28,36 @@ func Go(f func() error) error { return <-ch } + +// SignalHandler returns an actor, i.e. an execute and interrupt func, that +// terminates with SignalError when the process receives one of the provided +// signals, or the parent context is canceled. +// +// from https://github.com/oklog/run/blob/master/actors.go +func SignalHandler(ctx context.Context, signals ...os.Signal) (execute func() error, interrupt func(error)) { + ctx, cancel := context.WithCancel(ctx) + return func() error { + c := make(chan os.Signal, 1) + signal.Notify(c, signals...) + defer signal.Stop(c) + select { + case sig := <-c: + return SignalError{Signal: sig} + case <-ctx.Done(): + return ctx.Err() + } + }, func(error) { + cancel() + } +} + +// SignalError is returned by the signal handler's execute function +// when it terminates due to a received signal. +type SignalError struct { + Signal os.Signal +} + +// Error implements the error interface. +func (e SignalError) Error() string { + return fmt.Sprintf("received signal %s", e.Signal) +} diff --git a/vendor/github.com/gookit/goutil/stdutil/check.go b/vendor/github.com/gookit/goutil/stdutil/check.go index 8e28d6fa..d1a167b9 100644 --- a/vendor/github.com/gookit/goutil/stdutil/check.go +++ b/vendor/github.com/gookit/goutil/stdutil/check.go @@ -1,54 +1,135 @@ package stdutil import ( - "fmt" "reflect" - "strconv" + "strings" + + "github.com/gookit/goutil/reflects" ) -// IsEquals(s, d interface{}) bool -// IsContains(v, sub interface{}) bool +// IsNil value check +func IsNil(v any) bool { + if v == nil { + return true + } + return reflects.IsNil(reflect.ValueOf(v)) +} -// ValueIsEmpty check -func ValueIsEmpty(v reflect.Value) bool { - switch v.Kind() { - case reflect.Invalid: +// IsEmpty value check +func IsEmpty(v any) bool { + if v == nil { return true - case reflect.String, reflect.Array: - return v.Len() == 0 - case reflect.Map, reflect.Slice: - return v.Len() == 0 || v.IsNil() - case reflect.Bool: - return !v.Bool() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return v.Int() == 0 - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return v.Uint() == 0 - case reflect.Float32, reflect.Float64: - return v.Float() == 0 - case reflect.Interface, reflect.Ptr: - return v.IsNil() - } - - return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) -} - -// ValueLen get value length + } + return reflects.IsEmpty(reflect.ValueOf(v)) +} + +// IsFunc value +func IsFunc(val any) bool { + if val == nil { + return false + } + return reflect.TypeOf(val).Kind() == reflect.Func +} + +// IsEqual determines if two objects are considered equal. +// +// TIP: cannot compare function type +func IsEqual(src, dst any) bool { + if src == nil || dst == nil { + return src == dst + } + + // cannot compare function type + if IsFunc(src) || IsFunc(dst) { + return false + } + return reflects.IsEqual(src, dst) +} + +// Contains try loop over the data check if the data includes the element. +// alias of the IsContains +// +// TIP: only support types: string, map, array, slice +// +// map - check key exists +// string - check sub-string exists +// array,slice - check sub-element exists +func Contains(data, elem any) bool { + _, found := CheckContains(data, elem) + return found +} + +// IsContains try loop over the data check if the data includes the element. +// +// TIP: only support types: string, map, array, slice +// +// map - check key exists +// string - check sub-string exists +// array,slice - check sub-element exists +func IsContains(data, elem any) bool { + _, found := CheckContains(data, elem) + return found +} + +// CheckContains try loop over the data check if the data includes the element. +// +// TIP: only support types: string, map, array, slice +// +// map - check key exists +// string - check sub-string exists +// array,slice - check sub-element exists +// +// return (false, false) if impossible. +// return (true, false) if element was not found. +// return (true, true) if element was found. +func CheckContains(data, elem any) (valid, found bool) { + dataRv := reflect.ValueOf(data) + dataRt := reflect.TypeOf(data) + if dataRt == nil { + return false, false + } + + dataKind := dataRt.Kind() + + // string + if dataKind == reflect.String { + return true, strings.Contains(dataRv.String(), reflect.ValueOf(elem).String()) + } + + // map + if dataKind == reflect.Map { + mapKeys := dataRv.MapKeys() + for i := 0; i < len(mapKeys); i++ { + if reflects.IsEqual(mapKeys[i].Interface(), elem) { + return true, true + } + } + return true, false + } + + // array, slice - other return false + if dataKind != reflect.Slice && dataKind != reflect.Array { + return false, false + } + + for i := 0; i < dataRv.Len(); i++ { + if reflects.IsEqual(dataRv.Index(i).Interface(), elem) { + return true, true + } + } + return true, false +} + +// ValueIsEmpty reflect value check. +// +// Deprecated: please use reflects.IsEmpty() +func ValueIsEmpty(v reflect.Value) bool { + return reflects.IsEmpty(v) +} + +// ValueLen get reflect value length +// +// Deprecated: please use reflects.Len() func ValueLen(v reflect.Value) int { - k := v.Kind() - - // (u)int use width. - switch k { - case reflect.Map, reflect.Array, reflect.Chan, reflect.Slice, reflect.String: - return v.Len() - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return len(strconv.FormatInt(int64(v.Uint()), 10)) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return len(strconv.FormatInt(v.Int(), 10)) - case reflect.Float32, reflect.Float64: - return len(fmt.Sprint(v.Interface())) - } - - // cannot get length - return -1 + return reflects.Len(v) } diff --git a/vendor/github.com/gookit/goutil/stdutil/conv.go b/vendor/github.com/gookit/goutil/stdutil/conv.go new file mode 100644 index 00000000..bd5e68da --- /dev/null +++ b/vendor/github.com/gookit/goutil/stdutil/conv.go @@ -0,0 +1,50 @@ +package stdutil + +import ( + "reflect" + + "github.com/gookit/goutil/reflects" + "github.com/gookit/goutil/strutil" +) + +// ToString always convert value to string, will ignore error +func ToString(v any) string { + s, _ := strutil.AnyToString(v, false) + return s +} + +// MustString convert value(basic type) to string, will panic on convert a complex type. +func MustString(v any) string { + s, err := strutil.AnyToString(v, true) + if err != nil { + panic(err) + } + return s +} + +// TryString try to convert a value to string +func TryString(v any) (string, error) { + return strutil.AnyToString(v, true) +} + +// BaseTypeVal convert custom type or intX,uintX,floatX to generic base type. +// +// intX/unitX => int64 +// floatX => float64 +// string => string +// +// returns int64,string,float or error +func BaseTypeVal(val any) (value any, err error) { + return reflects.BaseTypeVal(reflect.ValueOf(val)) +} + +// BaseTypeVal2 convert custom type or intX,uintX,floatX to generic base type. +// +// intX/unitX => int64 +// floatX => float64 +// string => string +// +// returns int64,string,float or error +func BaseTypeVal2(v reflect.Value) (value any, err error) { + return reflects.BaseTypeVal(v) +} diff --git a/vendor/github.com/gookit/goutil/stdutil/convert.go b/vendor/github.com/gookit/goutil/stdutil/convert.go deleted file mode 100644 index 76a693f9..00000000 --- a/vendor/github.com/gookit/goutil/stdutil/convert.go +++ /dev/null @@ -1,66 +0,0 @@ -package stdutil - -import ( - "errors" - "reflect" - - "github.com/gookit/goutil/strutil" -) - -var ( - ErrConvertFail = errors.New("convert value type is failure") -) - -// ToString always convert value to string -func ToString(v interface{}) string { - s, _ := strutil.AnyToString(v, false) - return s -} - -// MustString convert value(basic type) to string, will panic on convert a complex type. -func MustString(v interface{}) string { - s, err := strutil.AnyToString(v, true) - if err != nil { - panic(err) - } - return s -} - -// TryString try to convert a value to string -func TryString(v interface{}) (string, error) { - return strutil.AnyToString(v, true) -} - -// BaseTypeVal2 convert custom type or intX,uintX,floatX to generic base type. -// -// intX/unitX => int64 -// floatX => float64 -// string => string -// -// returns int64,string,float or error -func BaseTypeVal2(v reflect.Value) (value interface{}, err error) { - switch v.Kind() { - case reflect.String: - value = v.String() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - value = v.Int() - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - value = int64(v.Uint()) // always return int64 - case reflect.Float32, reflect.Float64: - value = v.Float() - default: - err = ErrConvertFail - } - return -} - -// BaseTypeVal convert custom type or intX,uintX,floatX to generic base type. -// -// intX/unitX => int64 -// floatX => float64 -// string => string -// -// returns int64,string,float or error -func BaseTypeVal(val interface{}) (value interface{}, err error) { - return BaseTypeVal2(reflect.ValueOf(val)) -} diff --git a/vendor/github.com/gookit/goutil/stdutil/gofunc.go b/vendor/github.com/gookit/goutil/stdutil/gofunc.go index 11684a13..6a2be9c2 100644 --- a/vendor/github.com/gookit/goutil/stdutil/gofunc.go +++ b/vendor/github.com/gookit/goutil/stdutil/gofunc.go @@ -58,9 +58,10 @@ func (ffn *FullFcName) String() string { // FuncName get full func name, contains pkg path. // // eg: -// // OUTPUT: github.com/gookit/goutil/stdutil.PanicIf +// +// // OUTPUT: github.com/gookit/goutil/stdutil.PanicIf // stdutil.FuncName(stdutil.PkgName) -func FuncName(fn interface{}) string { +func FuncName(fn any) string { return runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name() } @@ -73,6 +74,7 @@ func CutFuncName(fullFcName string) (pkgPath, shortFnName string) { // PkgName get current package name // // Usage: +// // fullFcName := stdutil.FuncName(fn) // pgkName := stdutil.PkgName(fullFcName) func PkgName(fullFcName string) string { diff --git a/vendor/github.com/gookit/goutil/stdutil/stack.go b/vendor/github.com/gookit/goutil/stdutil/stack.go index 5d6c6270..701d1be7 100644 --- a/vendor/github.com/gookit/goutil/stdutil/stack.go +++ b/vendor/github.com/gookit/goutil/stdutil/stack.go @@ -7,6 +7,7 @@ import ( "strings" ) +// some commonly consts var ( DefStackLen = 10000 MaxStackLen = 100000 @@ -40,9 +41,10 @@ func GetCallStacks(all bool) []byte { // GetCallerInfo get caller func name and with base filename and line. // // returns: +// // github.com/gookit/goutil/stdutil_test.someFunc2(),stack_test.go:26 func GetCallerInfo(skip int) string { - skip += 1 // ignore current func + skip++ // ignore current func cs := GetCallersInfo(skip, skip+1) if len(cs) > 0 { @@ -54,7 +56,7 @@ func GetCallerInfo(skip int) string { // SimpleCallersInfo returns an array of strings containing // the func name, file and line number of each stack frame leading. func SimpleCallersInfo(skip, num int) []string { - skip += 1 // ignore current func + skip++ // ignore current func return GetCallersInfo(skip, skip+num) } @@ -78,7 +80,7 @@ func GetCallersInfo(skip, max int) []string { break } - // This is a huge edge case, but it will panic if this is the case, see #180 + // This is a huge edge case, but it will panic if this is the case if file == "" { break } @@ -88,7 +90,7 @@ func GetCallersInfo(skip, max int) []string { break } - if strings.Index(file, "/") >= 0 { + if strings.ContainsRune(file, '/') { name = fc.Name() file = path.Base(file) // eg: github.com/gookit/goutil/stdutil_test.someFunc2(),stack_test.go:26 diff --git a/vendor/github.com/gookit/goutil/stdutil/stdutil.go b/vendor/github.com/gookit/goutil/stdutil/stdutil.go index 3d3f8622..a0382afe 100644 --- a/vendor/github.com/gookit/goutil/stdutil/stdutil.go +++ b/vendor/github.com/gookit/goutil/stdutil/stdutil.go @@ -3,8 +3,12 @@ package stdutil import ( "fmt" + "runtime" ) +// DiscardE discard error +func DiscardE(_ error) {} + // PanicIfErr if error is not empty func PanicIfErr(err error) { if err != nil { @@ -20,6 +24,11 @@ func PanicIf(err error) { } // Panicf format panic message use fmt.Sprintf -func Panicf(format string, v ...interface{}) { +func Panicf(format string, v ...any) { panic(fmt.Sprintf(format, v...)) } + +// GoVersion get go runtime version. eg: "1.18.2" +func GoVersion() string { + return runtime.Version()[2:] +} diff --git a/vendor/github.com/gookit/goutil/stdutil/stream.go b/vendor/github.com/gookit/goutil/stdutil/stream.go deleted file mode 100644 index a596dc3b..00000000 --- a/vendor/github.com/gookit/goutil/stdutil/stream.go +++ /dev/null @@ -1,2 +0,0 @@ -package stdutil - diff --git a/vendor/github.com/gookit/goutil/stdutil/value.go b/vendor/github.com/gookit/goutil/stdutil/value.go index 3727440c..05bf0a51 100644 --- a/vendor/github.com/gookit/goutil/stdutil/value.go +++ b/vendor/github.com/gookit/goutil/stdutil/value.go @@ -1,95 +1,8 @@ package stdutil import ( - "github.com/gookit/goutil/mathutil" - "github.com/gookit/goutil/strutil" + "github.com/gookit/goutil/structs" ) -// Value data store -type Value struct { - // V value - V interface{} -} - -// Reset value -func (v *Value) Reset() { - v.V = nil -} - -// Val get -func (v Value) Val() interface{} { - return v.V -} - -// Int value -func (v Value) Int() int { - if v.V == nil { - return 0 - } - - return mathutil.MustInt(v.V) -} - -// Int64 value -func (v Value) Int64() int64 { - if v.V == nil { - return 0 - } - - return mathutil.MustInt64(v.V) -} - -// Bool value -func (v Value) Bool() bool { - if v.V == nil { - return false - } - - if bl, ok := v.V.(bool); ok { - return bl - } - - if str, ok := v.V.(string); ok { - return strutil.MustBool(str) - } - return false -} - -// Float64 value -func (v Value) Float64() float64 { - if v.V == nil { - return 0 - } - - return mathutil.MustFloat(v.V) -} - -// String value -func (v Value) String() string { - if v.V == nil { - return "" - } - - if str, ok := v.V.(string); ok { - return str - } - - return strutil.MustString(v.V) -} - -// Strings value -func (v Value) Strings() (ss []string) { - if v.V == nil { - return - } - - if ss, ok := v.V.([]string); ok { - return ss - } - return -} - -// IsEmpty value -func (v Value) IsEmpty() bool { - return v.V == nil -} +// Value data store, is alias of structs.Value +type Value = structs.Value diff --git a/vendor/github.com/gookit/goutil/structs/README.md b/vendor/github.com/gookit/goutil/structs/README.md new file mode 100644 index 00000000..d75da8bf --- /dev/null +++ b/vendor/github.com/gookit/goutil/structs/README.md @@ -0,0 +1,221 @@ +# Structs + +Provide some extends util functions for struct. eg: convert, tag parse, struct data init + +- `structs.Aliases` - implemented a simple string alias map. +- Convert a struct to `map[string]any` data +- Quickly init struct default values by field "default" tag. +- Quickly set struct field values by map data +- Parse a struct and collect tags, and parse tag value +- And more util functions ... + +## Install + +```shell +go get github.com/gookit/goutil/structs +``` + +## Go docs + +- [Go docs](https://pkg.go.dev/github.com/gookit/goutil/structs) + +## Usage + +### Convert to map + +`structs.ToMap()` can be quickly convert a `struct` value to `map[string]any` + +**Examples**: + +```go + type User1 struct { + Name string `json:"name"` + Age int `json:"age"` + city string + } + + u1 := &User1{ + Name: "inhere", + Age: 34, + city: "somewhere", + } + + mp := structs.ToMap(u1) + dump.P(mp) +``` + +**Output**: + +```shell +map[string]interface {} { #len=2 + "age": int(34), + "name": string("inhere"), #len=6 +}, +``` + +### Init default values + +`structs.InitDefaults` Quickly init struct default value by field "default" tag. + +**Examples**: + +```go +type Extra struct { + City string `default:"chengdu"` + Github string `default:"https://github.com/inhere"` +} +type User struct { + Name string `default:"inhere"` + Age int `default:"30"` + Extra Extra +} + +u := &User{} +_ = structs.InitDefaults(u, &structs.InitOptions{}) +dump.P(u) +``` + +**Output**: + +```shell +&structs_test.User { + Name: string("inhere"), #len=6 + Age: int(30), + Extra: structs_test.Extra { + City: string("chengdu"), #len=7 + Github: string("https://github.com/inhere"), #len=25 + }, +}, +``` + +### Set values from map + +```go +data := map[string]interface{}{ + "name": "inhere", + "age": 234, + "tags": []string{"php", "go"}, + "city": "chengdu", +} + +type User struct { + Name string `json:"name"` + Age int `json:"age"` + Tags []string `json:"tags"` + City string `json:"city"` +} + +u := &User{} +err := structs.SetValues(u, data) +dump.P(u) +``` + +**Output**: + +```shell +&structs_test.User { + Name: string("inhere"), #len=6 + Age: int(234), + Tags: []string [ #len=2 + string("php"), #len=3 + string("go"), #len=2 + ], + City: string("chengdu"), #len=7 +}, +``` + +### Tags collect and parse + +Parse a struct for collect tags, and parse tag value + +**Examples**: + +```go +// eg: "desc;required;default;shorts" +type MyCmd struct { + Name string `flag:"set your name;false;INHERE;n"` +} + +c := &MyCmd{} +p := structs.NewTagParser("flag") + +sepStr := ";" +defines := []string{"desc", "required", "default", "shorts"} +p.ValueFunc = structs.ParseTagValueDefine(sepStr, defines) + +goutil.MustOK(p.Parse(c)) +dump.P(p.Tags()) +``` + +Output: + +```shell +map[string]maputil.SMap { #len=1 + "Name": maputil.SMap { #len=1 + "flag": string("set your name;false;INHERE;n"), #len=28 + }, +}, +``` + +**Parse tag value** + +```go +info, _ := p.Info("Name", "flag") +dump.P(info) +``` + +Output: + +```shell +maputil.SMap { #len=4 + "desc": string("set your name"), #len=13 + "required": string("false"), #len=5 + "default": string("INHERE"), #len=6 + "shorts": string("n"), #len=1 +}, +``` + +## Functions API + +```go +func InitDefaults(ptr any, optFns ...InitOptFunc) error +func MustToMap(st any, optFns ...MapOptFunc) map[string]interface{} +func ParseReflectTags(rt reflect.Type, tagNames []string) (map[string]maputil.SMap, error) +func ParseTagValueDefault(field, tagVal string) (mp maputil.SMap, err error) +func ParseTagValueNamed(field, tagVal string, keys ...string) (mp maputil.SMap, err error) +func ParseTags(st any, tagNames []string) (map[string]maputil.SMap, error) +func SetValues(ptr any, data map[string]any, optFns ...SetOptFunc) error +func StructToMap(st any, optFns ...MapOptFunc) (map[string]interface{}, error) +func ToMap(st any, optFns ...MapOptFunc) map[string]interface{} +func TryToMap(st any, optFns ...MapOptFunc) (map[string]interface{}, error) +type Aliases struct{ ... } + func NewAliases(checker func(alias string)) *Aliases +type Data struct{ ... } + func NewData() *Data +type InitOptFunc func(opt *InitOptions) +type InitOptions struct{ ... } +type LiteData struct{ ... } +type MapOptFunc func(opt *MapOptions) +type MapOptions struct{ ... } +type SMap struct{ ... } +type SetOptFunc func(opt *SetOptions) +type SetOptions struct{ ... } +type TagParser struct{ ... } + func NewTagParser(tagNames ...string) *TagParser +type TagValFunc func(field, tagVal string) (maputil.SMap, error) + func ParseTagValueDefine(sep string, defines []string) TagValFunc +type Value struct{ ... } + func NewValue(val any) *Value +``` + +## Testings + +```shell +go test -v ./structs/... +``` + +Test limit by regexp: + +```shell +go test -v -run ^TestSetByKeys ./structs/... +``` diff --git a/vendor/github.com/gookit/goutil/structs/alias.go b/vendor/github.com/gookit/goutil/structs/alias.go new file mode 100644 index 00000000..52880561 --- /dev/null +++ b/vendor/github.com/gookit/goutil/structs/alias.go @@ -0,0 +1,67 @@ +package structs + +import "fmt" + +// Aliases implemented a simple string alias map. +type Aliases struct { + mapping map[string]string + // Checker custom add alias name checker func + Checker func(alias string) // should return bool OR error ?? +} + +// NewAliases create +func NewAliases(checker func(alias string)) *Aliases { + return &Aliases{Checker: checker} +} + +// AddAlias to the Aliases +func (as *Aliases) AddAlias(real, alias string) { + if as.mapping == nil { + as.mapping = make(map[string]string) + } + + if as.Checker != nil { + as.Checker(alias) + } + + if rn, ok := as.mapping[alias]; ok { + panic(fmt.Sprintf("The alias '%s' is already used by '%s'", alias, rn)) + } + + as.mapping[alias] = real +} + +// AddAliases to the Aliases +func (as *Aliases) AddAliases(real string, aliases []string) { + for _, a := range aliases { + as.AddAlias(real, a) + } +} + +// AddAliasMap to the Aliases +func (as *Aliases) AddAliasMap(alias2real map[string]string) { + for a, r := range alias2real { + as.AddAlias(r, a) + } +} + +// HasAlias in the Aliases +func (as *Aliases) HasAlias(alias string) bool { + if _, ok := as.mapping[alias]; ok { + return true + } + return false +} + +// ResolveAlias by given name. +func (as *Aliases) ResolveAlias(alias string) string { + if name, ok := as.mapping[alias]; ok { + return name + } + return alias +} + +// Mapping get all aliases mapping +func (as *Aliases) Mapping() map[string]string { + return as.mapping +} diff --git a/vendor/github.com/gookit/goutil/structs/convert.go b/vendor/github.com/gookit/goutil/structs/convert.go new file mode 100644 index 00000000..3535f89b --- /dev/null +++ b/vendor/github.com/gookit/goutil/structs/convert.go @@ -0,0 +1,106 @@ +package structs + +import ( + "errors" + "reflect" +) + +// ToMap quickly convert structs to map by reflect +func ToMap(st any, optFns ...MapOptFunc) map[string]any { + mp, _ := StructToMap(st, optFns...) + return mp +} + +// MustToMap alis of TryToMap, but will panic on error +func MustToMap(st any, optFns ...MapOptFunc) map[string]any { + mp, err := StructToMap(st, optFns...) + if err != nil { + panic(err) + } + return mp +} + +// TryToMap simple convert structs to map by reflect +func TryToMap(st any, optFns ...MapOptFunc) (map[string]any, error) { + return StructToMap(st, optFns...) +} + +const defaultFieldTag = "json" + +// MapOptions struct +type MapOptions struct { + TagName string +} + +// MapOptFunc define +type MapOptFunc func(opt *MapOptions) + +// StructToMap quickly convert structs to map[string]any by reflect. +// Can custom export field name by tag `json` or custom tag +func StructToMap(st any, optFns ...MapOptFunc) (map[string]any, error) { + mp := make(map[string]any) + if st == nil { + return mp, nil + } + + obj := reflect.ValueOf(st) + if obj.Kind() == reflect.Ptr { + obj = obj.Elem() + } + + if obj.Kind() != reflect.Struct { + return mp, errors.New("must be an struct") + } + + opt := &MapOptions{TagName: defaultFieldTag} + for _, fn := range optFns { + fn(opt) + } + + mp, err := structToMap(obj, opt.TagName) + return mp, err +} + +func structToMap(obj reflect.Value, tagName string) (map[string]any, error) { + refType := obj.Type() + mp := make(map[string]any) + + for i := 0; i < obj.NumField(); i++ { + ft := refType.Field(i) + name := ft.Name + // skip don't exported field + if name[0] >= 'a' && name[0] <= 'z' { + continue + } + + tagVal, ok := ft.Tag.Lookup(tagName) + if ok && tagVal != "" { + sMap, err := ParseTagValueDefault(name, tagVal) + if err != nil { + return nil, err + } + + name = sMap.Default("name", name) + // un-exported field + if name == "" { + continue + } + } + + field := obj.Field(i) + if field.Kind() == reflect.Struct { + sub, err := structToMap(field, tagName) + if err != nil { + return nil, err + } + mp[name] = sub + continue + } + + if field.CanInterface() { + mp[name] = field.Interface() + } + } + + return mp, nil +} diff --git a/vendor/github.com/gookit/goutil/structs/data.go b/vendor/github.com/gookit/goutil/structs/data.go new file mode 100644 index 00000000..63702110 --- /dev/null +++ b/vendor/github.com/gookit/goutil/structs/data.go @@ -0,0 +1,175 @@ +package structs + +import ( + "sync" + + "github.com/gookit/goutil/internal/comfunc" + "github.com/gookit/goutil/maputil" + "github.com/gookit/goutil/mathutil" + "github.com/gookit/goutil/strutil" +) + +// LiteData simple map[string]any struct. no lock +type LiteData struct { + data map[string]any +} + +// Data get all +func (d *LiteData) Data() map[string]any { + return d.data +} + +// SetData set all data +func (d *LiteData) SetData(data map[string]any) { + d.data = data +} + +// Value get from data +func (d *LiteData) Value(key string) any { + return d.data[key] +} + +// GetVal get from data +func (d *LiteData) GetVal(key string) any { + return d.data[key] +} + +// StrValue get from data +func (d *LiteData) StrValue(key string) string { + return strutil.QuietString(d.data[key]) +} + +// IntVal get from data +func (d *LiteData) IntVal(key string) int { + return mathutil.QuietInt(d.data[key]) +} + +// SetValue to data +func (d *LiteData) SetValue(key string, val any) { + if d.data == nil { + d.data = make(map[string]any) + } + d.data[key] = val +} + +// ResetData all data +func (d *LiteData) ResetData() { + d.data = nil +} + +/************************************************************* + * data struct and allow enable lock + *************************************************************/ + +// Data struct, allow enable lock TODO +type Data struct { + sync.RWMutex + enableLock bool + // data store + data map[string]any +} + +// NewData create +func NewData() *Data { + return &Data{ + data: make(map[string]any), + } +} + +// EnableLock for operate data +func (d *Data) EnableLock() *Data { + d.enableLock = true + return d +} + +// Data get all +func (d *Data) Data() map[string]any { + return d.data +} + +// SetData set all data +func (d *Data) SetData(data map[string]any) { + if !d.enableLock { + d.data = data + return + } + + d.RLock() + d.data = data + d.RUnlock() +} + +// DataLen of data +func (d *Data) DataLen() int { + return len(d.data) +} + +// ResetData all data +func (d *Data) ResetData() { + d.data = make(map[string]any) +} + +// Set value to data +func (d *Data) Set(key string, val any) { + d.SetValue(key, val) +} + +// SetValue to data +func (d *Data) SetValue(key string, val any) { + if d.enableLock { + d.Lock() + defer d.Unlock() + } + + d.data[key] = val +} + +// Value get from data +func (d *Data) Value(key string) (val any, ok bool) { + if d.enableLock { + d.RLock() + defer d.RUnlock() + } + + val, ok = d.data[key] + return +} + +// Get val from data +func (d *Data) Get(key string) any { + return d.GetVal(key) +} + +// GetVal get from data +func (d *Data) GetVal(key string) any { + if d.enableLock { + d.RLock() + defer d.RUnlock() + } + + return d.data[key] +} + +// StrVal get from data +func (d *Data) StrVal(key string) string { + return strutil.QuietString(d.GetVal(key)) +} + +// IntVal get from data +func (d *Data) IntVal(key string) int { + return mathutil.QuietInt(d.GetVal(key)) +} + +// BoolVal get from data +func (d *Data) BoolVal(key string) bool { + val, ok := d.Value(key) + if !ok { + return false + } + return comfunc.Bool(val) +} + +// String format data +func (d *Data) String() string { + return maputil.ToString(d.data) +} diff --git a/vendor/github.com/gookit/goutil/structs/setval.go b/vendor/github.com/gookit/goutil/structs/setval.go new file mode 100644 index 00000000..6461540f --- /dev/null +++ b/vendor/github.com/gookit/goutil/structs/setval.go @@ -0,0 +1,224 @@ +package structs + +import ( + "errors" + "fmt" + "reflect" + + "github.com/gookit/goutil/internal/comfunc" + "github.com/gookit/goutil/reflects" +) + +const defaultInitTag = "default" + +// InitOptFunc define +type InitOptFunc func(opt *InitOptions) + +// InitOptions struct +type InitOptions struct { + // TagName default value tag name. tag: default + TagName string + // ParseEnv var name on default value. eg: `default:"${APP_ENV}"` + // + // default: false + ParseEnv bool + // ValueHook before set value hook TODO + ValueHook func(val string) any +} + +// InitDefaults init struct default value by field "default" tag. +// +// TIPS: +// +// Only support init set: string, bool, intX, uintX, floatX +// +// Example: +// +// type User1 struct { +// Name string `default:"inhere"` +// Age int32 `default:"30"` +// } +// +// u1 := &User1{} +// err = structs.InitDefaults(u1) +// fmt.Printf("%+v\n", u1) +// // Output: {Name:inhere Age:30} +func InitDefaults(ptr any, optFns ...InitOptFunc) error { + rv := reflect.ValueOf(ptr) + if rv.Kind() != reflect.Ptr { + return errors.New("must be provider an pointer value") + } + + rv = rv.Elem() + if rv.Kind() != reflect.Struct { + return errors.New("must be provider an struct value") + } + + opt := &InitOptions{TagName: defaultInitTag} + for _, fn := range optFns { + fn(opt) + } + + return initDefaults(rv, opt) +} + +func initDefaults(rv reflect.Value, opt *InitOptions) error { + rt := rv.Type() + + for i := 0; i < rt.NumField(); i++ { + ft := rt.Field(i) + // skip don't exported field + if ft.Name[0] >= 'a' && ft.Name[0] <= 'z' { + continue + } + + fv := rv.Field(i) + if fv.Kind() == reflect.Struct { + if err := initDefaults(fv, opt); err != nil { + return err + } + continue + } + + // skip on field has value + if !fv.IsZero() { + continue + } + + tagVal := ft.Tag.Get(opt.TagName) + if err := initDefaultValue(fv, tagVal, opt.ParseEnv); err != nil { + return err + } + } + + return nil +} + +func initDefaultValue(fv reflect.Value, val string, parseEnv bool) error { + if val == "" || !fv.CanSet() { + return nil + } + + // parse env var + if parseEnv { + val = comfunc.ParseEnvVar(val, nil) + } + + // set value + return reflects.SetValue(fv, val) +} + +/************************************************************* + * load values to a struct + *************************************************************/ + +// SetOptFunc define +type SetOptFunc func(opt *SetOptions) + +// SetOptions for set values to struct +type SetOptions struct { + // FieldTagName get field name for read value. default tag: json + FieldTagName string + // ValueHook before set value hook TODO + ValueHook func(val any) any + + // ParseDefault init default value by DefaultValTag tag value. + // default: false + // + // see InitDefaults() + ParseDefault bool + // DefaultValTag name. tag: default + DefaultValTag string + // ParseDefaultEnv parse env var on default tag. eg: `default:"${APP_ENV}"` + // + // default: false + ParseDefaultEnv bool +} + +// SetValues set data values to struct ptr +// +// TIPS: +// +// Only support set: string, bool, intX, uintX, floatX +func SetValues(ptr any, data map[string]any, optFns ...SetOptFunc) error { + rv := reflect.ValueOf(ptr) + if rv.Kind() != reflect.Ptr { + return errors.New("must be provider an pointer value") + } + + rv = rv.Elem() + if rv.Kind() != reflect.Struct { + return errors.New("must be provider an struct value") + } + + opt := &SetOptions{ + FieldTagName: "json", + DefaultValTag: defaultInitTag, + } + + for _, fn := range optFns { + fn(opt) + } + return setValues(rv, data, opt) +} + +func setValues(rv reflect.Value, data map[string]any, opt *SetOptions) error { + if data == nil || len(data) == 0 { + return nil + } + + rt := rv.Type() + + for i := 0; i < rt.NumField(); i++ { + ft := rt.Field(i) + name := ft.Name + // skip don't exported field + if name[0] >= 'a' && name[0] <= 'z' { + continue + } + + // get field name + tagVal, ok := ft.Tag.Lookup(opt.FieldTagName) + if ok { + info, err := ParseTagValueDefault(name, tagVal) + if err != nil { + return err + } + + name = info.Get("name") + } + + fv := rv.Field(i) + val, ok := data[name] + + // set field value by default tag. + if !ok && fv.IsZero() { + defVal := ft.Tag.Get(opt.DefaultValTag) + + if err := initDefaultValue(fv, defVal, opt.ParseDefaultEnv); err != nil { + return err + } + continue + } + + // field is struct + if fv.Kind() == reflect.Struct { + asMp, ok := val.(map[string]any) + if !ok { + return fmt.Errorf("field is struct, must provide map data value") + } + + if err := setValues(fv, asMp, opt); err != nil { + return err + } + continue + } + + // set field value + if err := reflects.SetValue(fv, val); err != nil { + return err + } + } + + return nil +} diff --git a/vendor/github.com/gookit/goutil/structs/smap.go b/vendor/github.com/gookit/goutil/structs/smap.go new file mode 100644 index 00000000..ada140bd --- /dev/null +++ b/vendor/github.com/gookit/goutil/structs/smap.go @@ -0,0 +1,6 @@ +package structs + +// SMap simple map[string]string struct. +type SMap struct { + data map[string]string +} diff --git a/vendor/github.com/gookit/goutil/structs/structs.go b/vendor/github.com/gookit/goutil/structs/structs.go new file mode 100644 index 00000000..fb09db0c --- /dev/null +++ b/vendor/github.com/gookit/goutil/structs/structs.go @@ -0,0 +1,7 @@ +// Package structs Provide some extends util functions for struct. eg: tag parse, struct init, value set +package structs + +// MapStruct simple copy src struct value to dst struct +// func MapStruct(srcSt, dstSt any) { +// // TODO +// } diff --git a/vendor/github.com/gookit/goutil/structs/tags.go b/vendor/github.com/gookit/goutil/structs/tags.go new file mode 100644 index 00000000..b6118998 --- /dev/null +++ b/vendor/github.com/gookit/goutil/structs/tags.go @@ -0,0 +1,263 @@ +package structs + +import ( + "errors" + "fmt" + "reflect" + "strings" + + "github.com/gookit/goutil/arrutil" + "github.com/gookit/goutil/maputil" + "github.com/gookit/goutil/strutil" +) + +// ErrNotAnStruct error +// var emptyStringMap = make(maputil.SMap) +var ErrNotAnStruct = errors.New("must input an struct value") + +// ParseTags for parse struct tags. +func ParseTags(st any, tagNames []string) (map[string]maputil.SMap, error) { + p := NewTagParser(tagNames...) + + if err := p.Parse(st); err != nil { + return nil, err + } + return p.Tags(), nil +} + +// ParseReflectTags parse struct tags info. +func ParseReflectTags(rt reflect.Type, tagNames []string) (map[string]maputil.SMap, error) { + p := NewTagParser(tagNames...) + + if err := p.ParseType(rt); err != nil { + return nil, err + } + return p.Tags(), nil +} + +// TagValFunc handle func +type TagValFunc func(field, tagVal string) (maputil.SMap, error) + +// TagParser struct +type TagParser struct { + // TagNames want parsed tag names. + TagNames []string + // ValueFunc tag value parse func. + ValueFunc TagValFunc + + // key: field name + // value: tag map {tag-name: value string.} + tags map[string]maputil.SMap +} + +// Tags map data for struct fields +func (p *TagParser) Tags() map[string]maputil.SMap { + return p.tags +} + +// NewTagParser instance +func NewTagParser(tagNames ...string) *TagParser { + return &TagParser{ + TagNames: tagNames, + ValueFunc: ParseTagValueDefault, + } +} + +// Parse an struct value +func (p *TagParser) Parse(st any) error { + rv := reflect.ValueOf(st) + if rv.Kind() == reflect.Ptr && !rv.IsNil() { + rv = rv.Elem() + } + + return p.ParseType(rv.Type()) +} + +// ParseType parse a struct type value +func (p *TagParser) ParseType(rt reflect.Type) error { + if rt.Kind() != reflect.Struct { + return ErrNotAnStruct + } + + // key is field name. + p.tags = make(map[string]maputil.SMap) + return p.parseType(rt, "") +} + +func (p *TagParser) parseType(rt reflect.Type, parent string) error { + for i := 0; i < rt.NumField(); i++ { + sf := rt.Field(i) + + // skip don't exported field + name := sf.Name + if name[0] >= 'a' && name[0] <= 'z' { + continue + } + + smp := make(maputil.SMap) + for _, tagName := range p.TagNames { + // eg: `json:"age"` + // eg: "name=int0;shorts=i;required=true;desc=int option message" + tagVal := sf.Tag.Get(tagName) + if tagVal == "" { + continue + } + + smp[tagName] = tagVal + } + + pathKey := name + if parent != "" { + pathKey = parent + "." + name + } + + p.tags[pathKey] = smp + + ft := sf.Type + if ft.Kind() == reflect.Ptr { + ft = ft.Elem() + } + + // field is struct. + if ft.Kind() == reflect.Struct { + err := p.parseType(ft, pathKey) + if err != nil { + return err + } + } + } + return nil +} + +// Info parse the give field, returns tag value info. +// +// info, err := p.Info("Name", "json") +// exportField := info.Get("name") +func (p *TagParser) Info(field, tag string) (maputil.SMap, error) { + field = strutil.UpperFirst(field) + fTags, ok := p.tags[field] + if !ok { + return nil, fmt.Errorf("field %q not found", field) + } + + val, ok := fTags.Value(tag) + if !ok { + return make(maputil.SMap), nil + } + + // parse tag value + return p.ValueFunc(field, val) +} + +/************************************************************* + * some built in tag value parse func + *************************************************************/ + +// ParseTagValueDefault parse like json tag value. +// +// see json.Marshal(): +// +// // JSON as key "myName", skipped if empty. +// Field int `json:"myName,omitempty"` +// +// // Field appears in JSON as key "Field" (the default), but skipped if empty. +// Field int `json:",omitempty"` +// +// // Field is ignored by this package. +// Field int `json:"-"` +// +// // Field appears in JSON as key "-". +// Field int `json:"-,"` +// +// Int64String int64 `json:",string"` +// +// Returns: +// +// { +// "name": "myName", // maybe is empty, on tag value is "-" +// "omitempty": "true", +// "string": "true", +// // ... more custom bool settings. +// } +func ParseTagValueDefault(field, tagVal string) (mp maputil.SMap, err error) { + ss := strutil.SplitTrimmed(tagVal, ",") + ln := len(ss) + if ln == 0 || tagVal == "," { + return maputil.SMap{"name": field}, nil + } + + mp = make(maputil.SMap, ln) + if ln == 1 { + // valid field name + if ss[0] != "-" { + mp["name"] = ss[0] + } + return + } + + // ln > 1 + mp["name"] = ss[0] + // other settings: omitempty, string + for _, key := range ss[1:] { + mp[key] = "true" + } + return +} + +// ParseTagValueDefine parse tag value string by given defines. +// +// Examples: +// +// eg: "desc;required;default;shorts" +// type MyStruct { +// Age int `flag:"int option message;;a,b"` +// } +// sepStr := ";" +// defines := []string{"desc", "required", "default", "shorts"} +func ParseTagValueDefine(sep string, defines []string) TagValFunc { + defNum := len(defines) + + return func(field, tagVal string) (maputil.SMap, error) { + ss := strutil.SplitNTrimmed(tagVal, sep, defNum) + ln := len(ss) + mp := make(maputil.SMap, ln) + if ln == 0 { + return mp, nil + } + + for i, val := range ss { + key := defines[i] + mp[key] = val + } + return mp, nil + } +} + +// ParseTagValueNamed parse k-v tag value string. it's like INI format contents. +// +// eg: "name=int0;shorts=i;required=true;desc=int option message" +func ParseTagValueNamed(field, tagVal string, keys ...string) (mp maputil.SMap, err error) { + ss := strutil.Split(tagVal, ";") + ln := len(ss) + if ln == 0 { + return + } + + mp = make(maputil.SMap, ln) + for _, s := range ss { + if !strings.ContainsRune(s, '=') { + err = fmt.Errorf("parse tag error on field '%s': must match `KEY=VAL`", field) + return + } + + kvNodes := strings.SplitN(s, "=", 2) + key, val := kvNodes[0], strings.TrimSpace(kvNodes[1]) + if len(keys) > 0 && !arrutil.StringsHas(keys, key) { + err = fmt.Errorf("parse tag error on field '%s': invalid key name '%s'", field, key) + return + } + + mp[key] = val + } + return +} diff --git a/vendor/github.com/gookit/goutil/structs/value.go b/vendor/github.com/gookit/goutil/structs/value.go new file mode 100644 index 00000000..481e8a32 --- /dev/null +++ b/vendor/github.com/gookit/goutil/structs/value.go @@ -0,0 +1,142 @@ +package structs + +import ( + "github.com/gookit/goutil/arrutil" + "github.com/gookit/goutil/comdef" + "github.com/gookit/goutil/mathutil" + "github.com/gookit/goutil/strutil" +) + +// Value data store +type Value struct { + // V value + V any +} + +// NewValue instance. +func NewValue(val any) *Value { + return &Value{ + V: val, + } +} + +// Set value +func (v *Value) Set(val any) { + v.V = val +} + +// Reset value +func (v *Value) Reset() { + v.V = nil +} + +// Val get +func (v *Value) Val() any { + return v.V +} + +// Int value get +func (v *Value) Int() int { + if v.V == nil { + return 0 + } + return mathutil.QuietInt(v.V) +} + +// Int64 value +func (v *Value) Int64() int64 { + if v.V == nil { + return 0 + } + return mathutil.QuietInt64(v.V) +} + +// Bool value +func (v *Value) Bool() bool { + if v.V == nil { + return false + } + + if bl, ok := v.V.(bool); ok { + return bl + } + + if str, ok := v.V.(string); ok { + return strutil.QuietBool(str) + } + return false +} + +// Float64 value +func (v *Value) Float64() float64 { + if v.V == nil { + return 0 + } + return mathutil.QuietFloat(v.V) +} + +// String value +func (v *Value) String() string { + if v.V == nil { + return "" + } + + if str, ok := v.V.(string); ok { + return str + } + return strutil.QuietString(v.V) +} + +// Strings value +func (v *Value) Strings() (ss []string) { + if v.V == nil { + return + } + + if ss, ok := v.V.([]string); ok { + return ss + } + if str, ok := v.V.(string); ok { + return strutil.Split(str, comdef.DefaultSep) + } + return +} + +// SplitToStrings split string value to strings +func (v *Value) SplitToStrings(sep ...string) (ss []string) { + if v.V == nil { + return + } + + if str, ok := v.V.(string); ok { + return strutil.Split(str, sepStr(sep)) + } + return +} + +// SplitToInts split string value to []int +func (v *Value) SplitToInts(sep ...string) (ss []int) { + if v.V == nil { + return + } + + if str, ok := v.V.(string); ok { + ints, err := arrutil.StringsToInts(strutil.Split(str, sepStr(sep))) + if err == nil { + return ints + } + } + return +} + +// IsEmpty value +func (v *Value) IsEmpty() bool { + return v.V == nil +} + +func sepStr(seps []string) string { + if len(seps) > 0 { + return seps[0] + } + return comdef.DefaultSep +} diff --git a/vendor/github.com/gookit/goutil/strutil/README.md b/vendor/github.com/gookit/goutil/strutil/README.md index 12cad644..e72b363c 100644 --- a/vendor/github.com/gookit/goutil/strutil/README.md +++ b/vendor/github.com/gookit/goutil/strutil/README.md @@ -1,6 +1,6 @@ # String Util -This is an go string operate util package. +This is a go string operate util package. - Github: https://github.com/gookit/goutil/strutil - GoDoc: https://godoc.org/github.com/gookit/goutil/strutil @@ -8,7 +8,7 @@ This is an go string operate util package. ## Install ```bash -go get github.com/gookit/goutil/dump +go get github.com/gookit/goutil/strutil ``` ## Usage @@ -23,47 +23,170 @@ ints, err := strutil.ToIntSlice("1,2,3") ## Functions -```text +```go +func AddSlashes(s string) string +func AnyToString(val interface{}, defaultAsErr bool) (str string, err error) +func B32Decode(str string) string +func B32Encode(str string) string +func B64Decode(str string) string func B64Encode(str string) string +func Base64(str string) string func Bool(s string) (bool, error) +func Byte2str(b []byte) string +func Byte2string(b []byte) string +func BytePos(s string, bt byte) int func Camel(s string, sep ...string) string func CamelCase(s string, sep ...string) string +func Compare(s1, s2, op string) bool +func Cut(s, sep string) (before string, after string, found bool) +func EscapeHTML(s string) string +func EscapeJS(s string) string func FilterEmail(s string) string func GenMd5(src interface{}) string +func HasAllSubs(s string, subs []string) bool +func HasOnePrefix(s string, prefixes []string) bool +func HasOneSub(s string, subs []string) bool +func HasPrefix(s string, prefix string) bool +func HasSuffix(s string, suffix string) bool +func Implode(sep string, ss ...string) string +func Indent(s, prefix string) string +func IndentBytes(b, prefix []byte) []byte +func Int(s string) (int, error) +func Int64(s string) int64 +func Int64OrErr(s string) (int64, error) +func Int64OrPanic(s string) int64 +func IntOrPanic(s string) int +func Ints(s string, sep ...string) []int +func IsAlphaNum(c uint8) bool +func IsAlphabet(char uint8) bool +func IsBlank(s string) bool +func IsBlankBytes(bs []byte) bool +func IsEmpty(s string) bool +func IsEndOf(s, suffix string) bool +func IsNotBlank(s string) bool +func IsNumChar(c byte) bool +func IsNumeric(s string) bool +func IsSpace(c byte) bool +func IsSpaceRune(r rune) bool +func IsStartOf(s, prefix string) bool +func IsStartsOf(s string, prefixes []string) bool +func IsSymbol(r rune) bool +func IsValidUtf8(s string) bool +func IsVersion(s string) bool +func Join(sep string, ss ...string) string +func JoinList(sep string, ss []string) string +func LTrim(s string, cutSet ...string) string +func Lower(s string) string func LowerFirst(s string) string func Lowercase(s string) string +func Ltrim(s string, cutSet ...string) string +func MD5(src interface{}) string func Md5(src interface{}) string +func MicroTimeHexID() string +func MicroTimeID() string func MustBool(s string) bool +func MustCut(s, sep string) (before string, after string) +func MustInt(s string) int +func MustInt64(s string) int64 func MustString(in interface{}) string +func MustToTime(s string, layouts ...string) time.Time +func NoCaseEq(s, t string) bool func PadLeft(s, pad string, length int) string func PadRight(s, pad string, length int) string func Padding(s, pad string, length int, pos uint8) string func PrettyJSON(v interface{}) (string, error) +func QuietBool(s string) bool +func QuietInt(s string) int +func QuietInt64(s string) int64 +func QuietString(in interface{}) string +func Quote(s string) string +func RTrim(s string, cutSet ...string) string func RandomBytes(length int) ([]byte, error) +func RandomChars(ln int) string +func RandomCharsV2(ln int) string +func RandomCharsV3(ln int) string func RandomString(length int) (string, error) func RenderTemplate(input string, data interface{}, fns template.FuncMap, isFile ...bool) string +func RenderText(input string, data interface{}, fns template.FuncMap, isFile ...bool) string func Repeat(s string, times int) string +func RepeatBytes(char byte, times int) (chars []byte) func RepeatRune(char rune, times int) (chars []rune) func Replaces(str string, pairs map[string]string) string +func Rtrim(s string, cutSet ...string) string +func RuneCount(s string) int +func RuneIsLower(c rune) bool +func RuneIsUpper(c rune) bool +func RuneIsWord(c rune) bool +func RunePos(s string, ru rune) int +func RuneWidth(r rune) int func Similarity(s, t string, rate float32) (float32, bool) -func Snake(s string, sep ...string) string func SnakeCase(s string, sep ...string) string func Split(s, sep string) (ss []string) +func SplitInlineComment(val string) (string, string) +func SplitN(s, sep string, n int) (ss []string) +func SplitNTrimmed(s, sep string, n int) (ss []string) +func SplitNValid(s, sep string, n int) (ss []string) +func SplitTrimmed(s, sep string) (ss []string) +func SplitValid(s, sep string) (ss []string) +func StrPos(s, sub string) int func String(val interface{}) (string, error) +func StringOrErr(val interface{}) (string, error) +func Strings(s string, sep ...string) []string +func StripSlashes(s string) string func Substr(s string, pos, length int) string +func TextSplit(s string, w int) []string +func TextTruncate(s string, w int, tail string) string +func TextWidth(s string) int +func TextWrap(s string, w int) string +func Title(s string) string func ToArray(s string, sep ...string) []string func ToBool(s string) (bool, error) +func ToBytes(s string) (b []byte) +func ToDuration(s string) (time.Duration, error) +func ToInt(s string) (int, error) +func ToInt64(s string) (int64, error) func ToIntSlice(s string, sep ...string) (ints []int, err error) func ToInts(s string, sep ...string) ([]int, error) func ToSlice(s string, sep ...string) []string -func ToString(val interface{}) (str string, err error) +func ToString(val interface{}) (string, error) +func ToStrings(s string, sep ...string) []string func ToTime(s string, layouts ...string) (t time.Time, err error) func Trim(s string, cutSet ...string) string +func TrimCut(s, sep string) (string, string) func TrimLeft(s string, cutSet ...string) string func TrimRight(s string, cutSet ...string) string func URLDecode(s string) string func URLEncode(s string) string +func Unquote(s string) string +func Upper(s string) string func UpperFirst(s string) string func UpperWord(s string) string func Uppercase(s string) string +func Utf8Len(s string) int +func Utf8Split(s string, w int) []string +func Utf8Truncate(s string, w int, tail string) string +func Utf8Width(s string) (size int) +func Utf8len(s string) int +func VersionCompare(v1, v2, op string) bool +func WidthWrap(s string, w int) string +func WrapTag(s, tag string) string +``` + +## Code Check & Testing + +```bash +gofmt -w -l ./ +golint ./... +``` + +**Testing**: + +```shell +go test -v ./strutil/... +``` + +**Test limit by regexp**: + +```shell +go test -v -run ^TestSetByKeys ./strutil/... ``` diff --git a/vendor/github.com/gookit/goutil/strutil/bytes.go b/vendor/github.com/gookit/goutil/strutil/bytes.go index 6b6610d5..fd1f5497 100644 --- a/vendor/github.com/gookit/goutil/strutil/bytes.go +++ b/vendor/github.com/gookit/goutil/strutil/bytes.go @@ -1,11 +1,76 @@ package strutil +import ( + "bytes" + "fmt" + "strings" +) + +// Buffer wrap and extends the bytes.Buffer +type Buffer struct { + bytes.Buffer +} + +// NewBuffer instance +func NewBuffer() *Buffer { + return &Buffer{} +} + +// WriteAny type value to buffer +func (b *Buffer) WriteAny(vs ...any) { + for _, v := range vs { + _, _ = b.Buffer.WriteString(fmt.Sprint(v)) + } +} + +// QuietWriteByte to buffer +func (b *Buffer) QuietWriteByte(c byte) { + _ = b.WriteByte(c) +} + +// QuietWritef write message to buffer +func (b *Buffer) QuietWritef(tpl string, vs ...any) { + _, _ = b.WriteString(fmt.Sprintf(tpl, vs...)) +} + +// Writeln write message to buffer with newline +func (b *Buffer) Writeln(ss ...string) { + b.QuietWriteln(ss...) +} + +// QuietWriteln write message to buffer with newline +func (b *Buffer) QuietWriteln(ss ...string) { + _, _ = b.WriteString(strings.Join(ss, "")) + _ = b.WriteByte('\n') +} + +// QuietWriteString to buffer +func (b *Buffer) QuietWriteString(ss ...string) { + _, _ = b.WriteString(strings.Join(ss, "")) +} + +// MustWriteString to buffer +func (b *Buffer) MustWriteString(ss ...string) { + _, err := b.WriteString(strings.Join(ss, "")) + if err != nil { + panic(err) + } +} + +// ResetAndGet buffer string. +func (b *Buffer) ResetAndGet() string { + s := b.String() + b.Reset() + return s +} + // ByteChanPool struct // // Usage: +// // bp := strutil.NewByteChanPool(500, 1024, 1024) -// buf:=bp.Get() -// defer bp.Put(buf) +// buf:=bp.Get() +// defer bp.Put(buf) // // use buf do something ... // // refer https://www.flysnow.org/2020/08/21/golang-chan-byte-pool.html diff --git a/vendor/github.com/gookit/goutil/strutil/check.go b/vendor/github.com/gookit/goutil/strutil/check.go index 9d54cea7..194dd641 100644 --- a/vendor/github.com/gookit/goutil/strutil/check.go +++ b/vendor/github.com/gookit/goutil/strutil/check.go @@ -1,25 +1,32 @@ package strutil import ( + "regexp" "strings" "unicode" "unicode/utf8" ) -var ( - Equal = strings.EqualFold - HasPrefix = strings.HasPrefix - HasSuffix = strings.HasSuffix -) +// Equal check, alias of strings.EqualFold +var Equal = strings.EqualFold -// refer from github.com/yuin/goldmark/util -var spaceTable = [256]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +// NoCaseEq check two strings is equals and case-insensitivity +func NoCaseEq(s, t string) bool { + return strings.EqualFold(s, t) +} -// IsNumeric returns true if the given character is a numeric, otherwise false. -func IsNumeric(c byte) bool { +// IsNumChar returns true if the given character is a numeric, otherwise false. +func IsNumChar(c byte) bool { return c >= '0' && c <= '9' } +var numReg = regexp.MustCompile(`^\d+$`) + +// IsNumeric returns true if the given string is a numeric, otherwise false. +func IsNumeric(s string) bool { + return numReg.MatchString(s) +} + // IsAlphabet char func IsAlphabet(char uint8) bool { // A 65 -> Z 90 @@ -31,7 +38,6 @@ func IsAlphabet(char uint8) bool { if char >= 'a' && char <= 'z' { return true } - return false } @@ -50,11 +56,6 @@ func BytePos(s string, bt byte) int { return strings.IndexByte(s, bt) } -// RunePos alias of the strings.IndexRune -func RunePos(s string, ru rune) int { - return strings.IndexRune(s, ru) -} - // HasOneSub substr in the given string. func HasOneSub(s string, subs []string) bool { for _, sub := range subs { @@ -76,61 +77,47 @@ func HasAllSubs(s string, subs []string) bool { } // IsStartsOf alias of the HasOnePrefix -func IsStartsOf(s string, subs []string) bool { - return HasOnePrefix(s, subs) +func IsStartsOf(s string, prefixes []string) bool { + return HasOnePrefix(s, prefixes) } // HasOnePrefix the string start withs one of the subs -func HasOnePrefix(s string, subs []string) bool { - for _, sub := range subs { - if strings.HasPrefix(s, sub) { +func HasOnePrefix(s string, prefixes []string) bool { + for _, prefix := range prefixes { + if strings.HasPrefix(s, prefix) { return true } } return false } -// IsStartOf alias of the strings.HasPrefix -func IsStartOf(s, sub string) bool { - return strings.HasPrefix(s, sub) -} +// HasPrefix substr in the given string. +func HasPrefix(s string, prefix string) bool { return strings.HasPrefix(s, prefix) } -// IsEndOf alias of the strings.HasSuffix -func IsEndOf(s, sub string) bool { - return strings.HasSuffix(s, sub) -} +// IsStartOf alias of the strings.HasPrefix +func IsStartOf(s, prefix string) bool { return strings.HasPrefix(s, prefix) } -// Len of the string -func Len(s string) int { - return len(s) -} +// HasSuffix substr in the given string. +func HasSuffix(s string, suffix string) bool { return strings.HasSuffix(s, suffix) } -// Utf8len of the string -func Utf8len(s string) int { - return utf8.RuneCount([]byte(s)) -} +// IsEndOf alias of the strings.HasSuffix +func IsEndOf(s, suffix string) bool { return strings.HasSuffix(s, suffix) } -// ValidUtf8String check -func ValidUtf8String(s string) bool { - return utf8.ValidString(s) -} +// IsValidUtf8 valid utf8 string check +func IsValidUtf8(s string) bool { return utf8.ValidString(s) } // ----- refer from github.com/yuin/goldmark/util +// refer from github.com/yuin/goldmark/util +var spaceTable = [256]int8{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + // IsSpace returns true if the given character is a space, otherwise false. func IsSpace(c byte) bool { return spaceTable[c] == 1 } -// IsSpaceRune returns true if the given rune is a space, otherwise false. -func IsSpaceRune(r rune) bool { - return r <= 256 && IsSpace(byte(r)) || unicode.IsSpace(r) -} - // IsEmpty returns true if the given string is empty. -func IsEmpty(s string) bool { - return len(s) == 0 -} +func IsEmpty(s string) bool { return len(s) == 0 } // IsBlank returns true if the given string is all space characters. func IsBlank(s string) bool { @@ -156,3 +143,33 @@ func IsBlankBytes(bs []byte) bool { func IsSymbol(r rune) bool { return unicode.IsSymbol(r) } + +var verRegex = regexp.MustCompile(`^[0-9][\d.]+(-\w+)?$`) + +// IsVersion number. eg: 1.2.0 +func IsVersion(s string) bool { + return verRegex.MatchString(s) +} + +// Compare for two string. +func Compare(s1, s2, op string) bool { + return VersionCompare(s1, s2, op) +} + +// VersionCompare for two version string. +func VersionCompare(v1, v2, op string) bool { + switch op { + case ">", "gt": + return v1 > v2 + case "<", "lt": + return v1 < v2 + case ">=", "gte": + return v1 >= v2 + case "<=", "lte": + return v1 <= v2 + case "!=", "ne", "neq": + return v1 != v2 + default: // eq + return v1 == v2 + } +} diff --git a/vendor/github.com/gookit/goutil/strutil/convert.go b/vendor/github.com/gookit/goutil/strutil/convert.go index 6a68279a..5ae183a2 100644 --- a/vendor/github.com/gookit/goutil/strutil/convert.go +++ b/vendor/github.com/gookit/goutil/strutil/convert.go @@ -11,11 +11,13 @@ import ( "time" "unsafe" + "github.com/gookit/goutil/comdef" + "github.com/gookit/goutil/internal/comfunc" "github.com/gookit/goutil/mathutil" ) var ( - ErrConvertFail = errors.New("convert data type is failure") + ErrDateLayout = errors.New("invalid date layout string") ErrInvalidParam = errors.New("invalid input parameter") // some regex for convert string. @@ -28,46 +30,87 @@ var ( ) // Internal func refers: -// strconv.Quote() // strconv.QuoteRune() // strconv.QuoteToASCII() // strconv.AppendQuote() // strconv.AppendQuoteRune() -// Join alias of strings.Join -func Join(sep string, ss ...string) string { - return strings.Join(ss, sep) +// Quote alias of strings.Quote +func Quote(s string) string { return strconv.Quote(s) } + +// Unquote remove start and end quotes by single-quote or double-quote +func Unquote(s string) string { + ln := len(s) + if ln < 2 { + return s + } + + qs, qe := s[0], s[ln-1] + + var valid bool + if qs == '"' && qe == '"' { + valid = true + } else if qs == '\'' && qe == '\'' { + valid = true + } + + if valid { + s = s[1 : ln-1] // exclude quotes + } + // strconv.Unquote cannot unquote single-quote + // if ns, err := strconv.Unquote(s); err == nil { + // return ns + // } + return s } +// Join alias of strings.Join +func Join(sep string, ss ...string) string { return strings.Join(ss, sep) } + +// JoinList alias of strings.Join +func JoinList(sep string, ss []string) string { return strings.Join(ss, sep) } + // Implode alias of strings.Join -func Implode(sep string, ss ...string) string { - return strings.Join(ss, sep) -} +func Implode(sep string, ss ...string) string { return strings.Join(ss, sep) } /************************************************************* * convert value to string *************************************************************/ // String convert val to string -func String(val interface{}) (string, error) { +func String(val any) (string, error) { return AnyToString(val, true) } -// MustString convert value to string -func MustString(in interface{}) string { +// QuietString convert value to string, will ignore error +func QuietString(in any) string { val, _ := AnyToString(in, false) return val } +// MustString convert value to string, TODO will panic on error +func MustString(in any) string { + val, _ := AnyToString(in, false) + return val +} + +// StringOrErr convert value to string, return error on failed +func StringOrErr(val any) (string, error) { + return AnyToString(val, true) +} + // ToString convert value to string -func ToString(val interface{}) (string, error) { +func ToString(val any) (string, error) { return AnyToString(val, true) } // AnyToString convert value to string. // -// if defaultAsErr is False, will use fmt.Sprint convert complex type -func AnyToString(val interface{}, defaultAsErr bool) (str string, err error) { +// if defaultAsErr: +// +// False will use fmt.Sprint convert complex type +// True will return error on fail. +func AnyToString(val any, defaultAsErr bool) (str string, err error) { if val == nil { return } @@ -82,7 +125,7 @@ func AnyToString(val interface{}, defaultAsErr bool) (str string, err error) { case int32: // same as `rune` str = strconv.Itoa(int(value)) case int64: - str = strconv.Itoa(int(value)) + str = strconv.FormatInt(value, 10) case uint: str = strconv.FormatUint(uint64(value), 10) case uint8: @@ -109,7 +152,7 @@ func AnyToString(val interface{}, defaultAsErr bool) (str string, err error) { str = value.String() default: if defaultAsErr { - err = ErrConvertFail + err = comdef.ErrConvType } else { str = fmt.Sprint(value) } @@ -149,27 +192,24 @@ func ToBytes(s string) (b []byte) { // ToBool convert string to bool func ToBool(s string) (bool, error) { - return Bool(s) + return comfunc.StrToBool(s) } -// MustBool convert. +// QuietBool convert to bool, will ignore error +func QuietBool(s string) bool { + val, _ := comfunc.StrToBool(strings.TrimSpace(s)) + return val +} + +// MustBool convert, will panic on error func MustBool(s string) bool { - val, _ := Bool(strings.TrimSpace(s)) + val, _ := comfunc.StrToBool(strings.TrimSpace(s)) return val } -// Bool parse string to bool +// Bool parse string to bool. like strconv.ParseBool() func Bool(s string) (bool, error) { - // return strconv.ParseBool(Trim(s)) - lower := strings.ToLower(s) - switch lower { - case "1", "on", "yes", "true": - return true, nil - case "0", "off", "no", "false": - return false, nil - } - - return false, fmt.Errorf("'%s' cannot convert to bool", s) + return comfunc.StrToBool(s) } /************************************************************* @@ -181,12 +221,18 @@ func Int(s string) (int, error) { return strconv.Atoi(strings.TrimSpace(s)) } -// ToInt convert string to int +// ToInt convert string to int, return error on fail func ToInt(s string) (int, error) { return strconv.Atoi(strings.TrimSpace(s)) } -// MustInt convert string to int +// QuietInt convert string to int, will ignore error +func QuietInt(s string) int { + val, _ := ToInt(s) + return val +} + +// MustInt convert string to int, will panic on error func MustInt(s string) int { val, _ := ToInt(s) return val @@ -201,22 +247,58 @@ func IntOrPanic(s string) int { return val } +// Int64 convert string to int, will ignore error +func Int64(s string) int64 { + val, _ := Int64OrErr(s) + return val +} + +// QuietInt64 convert string to int, will ignore error +func QuietInt64(s string) int64 { + val, _ := Int64OrErr(s) + return val +} + +// ToInt64 convert string to int, return error on fail +func ToInt64(s string) (int64, error) { + return strconv.ParseInt(s, 10, 0) +} + +// Int64OrErr convert string to int, return error on fail +func Int64OrErr(s string) (int64, error) { + return strconv.ParseInt(s, 10, 0) +} + +// MustInt64 convert value to int, will panic on error +func MustInt64(s string) int64 { + return Int64OrPanic(s) +} + +// Int64OrPanic convert value to int, will panic on error +func Int64OrPanic(s string) int64 { + val, err := strconv.ParseInt(s, 10, 0) + if err != nil { + panic(err) + } + return val +} + /************************************************************* * convert string value to int/string slice, time.Time *************************************************************/ -// Ints alias of the ToIntSlice() +// Ints alias of the ToIntSlice(). default sep is comma(,) func Ints(s string, sep ...string) []int { ints, _ := ToIntSlice(s, sep...) return ints } -// ToInts alias of the ToIntSlice() -func ToInts(s string, sep ...string) ([]int, error) { - return ToIntSlice(s, sep...) -} +// ToInts alias of the ToIntSlice(). default sep is comma(,) +func ToInts(s string, sep ...string) ([]int, error) { return ToIntSlice(s, sep...) } // ToIntSlice split string to slice and convert item to int. +// +// Default sep is comma(,) func ToIntSlice(s string, sep ...string) (ints []int, err error) { ss := ToSlice(s, sep...) for _, item := range ss { @@ -231,26 +313,19 @@ func ToIntSlice(s string, sep ...string) (ints []int, err error) { } // ToArray alias of the ToSlice() -func ToArray(s string, sep ...string) []string { - return ToSlice(s, sep...) -} +func ToArray(s string, sep ...string) []string { return ToSlice(s, sep...) } // Strings alias of the ToSlice() -func Strings(s string, sep ...string) []string { - return ToSlice(s, sep...) -} +func Strings(s string, sep ...string) []string { return ToSlice(s, sep...) } // ToStrings alias of the ToSlice() -func ToStrings(s string, sep ...string) []string { - return ToSlice(s, sep...) -} +func ToStrings(s string, sep ...string) []string { return ToSlice(s, sep...) } // ToSlice split string to array. func ToSlice(s string, sep ...string) []string { if len(sep) > 0 { return Split(s, sep[0]) } - return Split(s, ",") } @@ -268,46 +343,78 @@ func MustToTime(s string, layouts ...string) time.Time { return t } +// auto match use some commonly layouts. +// key is layout length. +var layoutMap = map[int][]string{ + 6: {"200601", "060102", time.Kitchen}, + 8: {"20060102"}, + 10: {"2006-01-02"}, + 13: {"2006-01-02 15"}, + 15: {time.Stamp}, + 16: {"2006-01-02 15:04"}, + 19: {"2006-01-02 15:04:05", time.RFC822, time.StampMilli}, + 20: {"2006-01-02 15:04:05Z"}, + 21: {time.RFC822Z}, + 22: {time.StampMicro}, + 24: {time.ANSIC}, + 25: {time.RFC3339, time.StampNano}, + // 26: {time.Layout}, // must go >= 1.19 + 28: {time.UnixDate}, + 29: {time.RFC1123}, + 30: {time.RFC850}, + 31: {time.RFC1123Z}, + 35: {time.RFC3339Nano}, +} + // ToTime convert date string to time.Time func ToTime(s string, layouts ...string) (t time.Time, err error) { - var layout string - if len(layouts) > 0 { // custom layout - layout = layouts[0] - if layout == "" { - err = ErrInvalidParam - return - } - } else { // auto match layout. - switch len(s) { - case 8: - layout = "20060102" - case 10: - layout = "2006-01-02" - case 13: - layout = "2006-01-02 15" - case 16: - layout = "2006-01-02 15:04" - case 19: - layout = "2006-01-02 15:04:05" - case 20: // time.RFC3339 - layout = "2006-01-02T15:04:05Z07:00" - default: - err = ErrInvalidParam - return + // custom layout + if len(layouts) > 0 { + if len(layouts[0]) > 0 { + return time.Parse(layouts[0], s) } - // has 'T' eg: "2006-01-02T15:04:05" - if strings.ContainsRune(s, 'T') { - layout = strings.Replace(layout, " ", "T", -1) + err = ErrDateLayout + return + } + + // auto match use some commonly layouts. + strLn := len(s) + maybeLayouts, ok := layoutMap[strLn] + if !ok { + err = ErrInvalidParam + return + } + + var hasAlphaT bool + if pos := strings.IndexByte(s, 'T'); pos > 0 && pos < 12 { + hasAlphaT = true + } + + hasSlashR := strings.IndexByte(s, '/') > 0 + for _, layout := range maybeLayouts { + // date string has "T". eg: "2006-01-02T15:04:05" + if hasAlphaT { + layout = strings.Replace(layout, " ", "T", 1) } - // eg: "2006/01/02 15:04:05" - if strings.ContainsRune(s, '/') { + // date string has "/". eg: "2006/01/02 15:04:05" + if hasSlashR { layout = strings.Replace(layout, "-", "/", -1) } + + t, err = time.Parse(layout, s) + if err == nil { + return + } } - t, err = time.Parse(layout, s) // t, err = time.ParseInLocation(layout, s, time.Local) return } + +// ToDuration parses a duration string. such as "300ms", "-1.5h" or "2h45m". +// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". +func ToDuration(s string) (time.Duration, error) { + return time.ParseDuration(s) +} diff --git a/vendor/github.com/gookit/goutil/strutil/encode.go b/vendor/github.com/gookit/goutil/strutil/encode.go index 1db025cd..a102e188 100644 --- a/vendor/github.com/gookit/goutil/strutil/encode.go +++ b/vendor/github.com/gookit/goutil/strutil/encode.go @@ -1,27 +1,98 @@ package strutil import ( + "bytes" + "crypto/md5" + "encoding/base32" "encoding/base64" + "encoding/hex" + "fmt" "net/url" "strings" "text/template" ) -var ( - // EscapeJS escape javascript string - EscapeJS = template.JSEscapeString - // EscapeHTML escape html string - EscapeHTML = template.HTMLEscapeString -) +// +// -------------------- escape -------------------- +// -// Base64 encode -func Base64(str string) string { - return base64.StdEncoding.EncodeToString([]byte(str)) +// EscapeJS escape javascript string +func EscapeJS(s string) string { + return template.JSEscapeString(s) } -// B64Encode base64 encode -func B64Encode(str string) string { - return base64.StdEncoding.EncodeToString([]byte(str)) +// EscapeHTML escape html string +func EscapeHTML(s string) string { + return template.HTMLEscapeString(s) +} + +// AddSlashes add slashes for the string. +func AddSlashes(s string) string { + if ln := len(s); ln == 0 { + return "" + } + + var buf bytes.Buffer + for _, char := range s { + switch char { + case '\'', '"', '\\': + buf.WriteRune('\\') + } + buf.WriteRune(char) + } + + return buf.String() +} + +// StripSlashes strip slashes for the string. +func StripSlashes(s string) string { + ln := len(s) + if ln == 0 { + return "" + } + + var skip bool + var buf bytes.Buffer + + for i, char := range s { + if skip { + skip = false + } else if char == '\\' { + if i+1 < ln && s[i+1] == '\\' { + skip = true + } + continue + } + buf.WriteRune(char) + } + + return buf.String() +} + +// +// -------------------- encode -------------------- +// + +// Md5 Generate a 32-bit md5 string +func Md5(src any) string { + return hex.EncodeToString(Md5Bytes(src)) +} + +// MD5 Generate a 32-bit md5 string +func MD5(src any) string { return Md5(src) } + +// GenMd5 Generate a 32-bit md5 string +func GenMd5(src any) string { return Md5(src) } + +// Md5Bytes Generate a 32-bit md5 bytes +func Md5Bytes(src any) []byte { + h := md5.New() + if s, ok := src.(string); ok { + h.Write([]byte(s)) + } else { + h.Write([]byte(fmt.Sprint(src))) + } + return h.Sum(nil) } // URLEncode encode url string. @@ -29,7 +100,6 @@ func URLEncode(s string) string { if pos := strings.IndexRune(s, '?'); pos > -1 { // escape query data return s[0:pos+1] + url.QueryEscape(s[pos+1:]) } - return s } @@ -44,3 +114,89 @@ func URLDecode(s string) string { return s } + +// +// -------------------- base encode -------------------- +// + +// B32Encode base32 encode +func B32Encode(str string) string { + return base32.StdEncoding.EncodeToString([]byte(str)) +} + +// B32Decode base32 decode +func B32Decode(str string) string { + dec, _ := base32.StdEncoding.DecodeString(str) + return string(dec) +} + +// B64Encode base64 encode +func B64Encode(str string) string { + return base64.StdEncoding.EncodeToString([]byte(str)) +} + +// B64EncodeBytes base64 encode +func B64EncodeBytes(src []byte) []byte { + buf := make([]byte, base64.StdEncoding.EncodedLen(len(src))) + base64.StdEncoding.Encode(buf, src) + + return buf +} + +// B64Decode base64 decode +func B64Decode(str string) string { + dec, _ := base64.StdEncoding.DecodeString(str) + return string(dec) +} + +// B64DecodeBytes base64 decode +func B64DecodeBytes(str string) []byte { + dbuf := make([]byte, base64.StdEncoding.DecodedLen(len(str))) + n, _ := base64.StdEncoding.Decode(dbuf, []byte(str)) + + return dbuf[:n] +} + +// BaseEncoder interface +type BaseEncoder interface { + Encode(dst []byte, src []byte) + EncodeToString(src []byte) string + Decode(dst []byte, src []byte) (n int, err error) + DecodeString(s string) ([]byte, error) +} + +// BaseType for base encoding +type BaseType uint8 + +// types for base encoding +const ( + BaseTypeStd BaseType = iota + BaseTypeHex + BaseTypeURL + BaseTypeRawStd + BaseTypeRawURL +) + +// Encoding instance +func Encoding(base int, typ BaseType) BaseEncoder { + if base == 32 { + switch typ { + case BaseTypeHex: + return base32.HexEncoding + default: + return base32.StdEncoding + } + } + + // base 64 + switch typ { + case BaseTypeURL: + return base64.URLEncoding + case BaseTypeRawURL: + return base64.RawURLEncoding + case BaseTypeRawStd: + return base64.RawStdEncoding + default: + return base64.StdEncoding + } +} diff --git a/vendor/github.com/gookit/goutil/strutil/filter.go b/vendor/github.com/gookit/goutil/strutil/filter.go index f44684d6..c80f6643 100644 --- a/vendor/github.com/gookit/goutil/strutil/filter.go +++ b/vendor/github.com/gookit/goutil/strutil/filter.go @@ -50,7 +50,6 @@ func TrimRight(s string, cutSet ...string) string { if ln == 1 { return strings.TrimRight(s, cutSet[0]) } - return strings.TrimRight(s, strings.Join(cutSet, "")) } diff --git a/vendor/github.com/gookit/goutil/strutil/format.go b/vendor/github.com/gookit/goutil/strutil/format.go index ec78cdfe..05150f5a 100644 --- a/vendor/github.com/gookit/goutil/strutil/format.go +++ b/vendor/github.com/gookit/goutil/strutil/format.go @@ -6,15 +6,22 @@ import ( "unicode" ) -// Title Some alias methods. -var ( - Title = strings.ToTitle -) - /************************************************************* * change string case *************************************************************/ +// methods aliases +var ( + UpWords = UpperWord + LoFirst = LowerFirst + UpFirst = UpperFirst + + Snake = SnakeCase +) + +// Title alias of the strings.ToTitle() +func Title(s string) string { return strings.ToTitle(s) } + // Lower alias of the strings.ToLower() func Lower(s string) string { return strings.ToLower(s) } @@ -42,25 +49,25 @@ func UpperWord(s string) string { i := 0 rs := []rune(s) - if runeIsLowerChar(rs[i]) { + if RuneIsLower(rs[i]) { buf = append(buf, []byte(string(unicode.ToUpper(rs[i])))...) } else { buf = append(buf, []byte(string(rs[i]))...) } for j := i + 1; j < len(rs); j++ { - if !runeIsWord(rs[i]) && runeIsWord(rs[j]) { + if !RuneIsWord(rs[i]) && RuneIsWord(rs[j]) { inWord = false } - if runeIsLowerChar(rs[j]) && !inWord { + if RuneIsLower(rs[j]) && !inWord { buf = append(buf, []byte(string(unicode.ToUpper(rs[j])))...) inWord = true } else { buf = append(buf, []byte(string(rs[j]))...) } - if runeIsWord(rs[j]) { + if RuneIsWord(rs[j]) { inWord = true } @@ -78,10 +85,10 @@ func LowerFirst(s string) string { rs := []rune(s) f := rs[0] + if 'A' <= f && f <= 'Z' { return string(unicode.ToLower(f)) + string(rs[1:]) } - return s } @@ -93,18 +100,13 @@ func UpperFirst(s string) string { rs := []rune(s) f := rs[0] + if 'a' <= f && f <= 'z' { return string(unicode.ToUpper(f)) + string(rs[1:]) } - return s } -// Snake alias of the SnakeCase -func Snake(s string, sep ...string) string { - return SnakeCase(s, sep...) -} - // SnakeCase convert. eg "RangePrice" -> "range_price" func SnakeCase(s string, sep ...string) string { sepChar := "_" @@ -112,23 +114,23 @@ func SnakeCase(s string, sep ...string) string { sepChar = sep[0] } - newStr := toSnakeReg.ReplaceAllStringFunc(s, func(s string) string { + str := toSnakeReg.ReplaceAllStringFunc(s, func(s string) string { return sepChar + LowerFirst(s) }) - return strings.TrimLeft(newStr, sepChar) + return strings.TrimLeft(str, sepChar) } // Camel alias of the CamelCase -func Camel(s string, sep ...string) string { - return CamelCase(s, sep...) -} +func Camel(s string, sep ...string) string { return CamelCase(s, sep...) } // CamelCase convert string to camel case. +// // Support: -// "range_price" -> "rangePrice" -// "range price" -> "rangePrice" -// "range-price" -> "rangePrice" +// +// "range_price" -> "rangePrice" +// "range price" -> "rangePrice" +// "range-price" -> "rangePrice" func CamelCase(s string, sep ...string) string { sepChar := "_" if len(sep) > 0 { @@ -152,14 +154,33 @@ func CamelCase(s string, sep ...string) string { }) } -func runeIsWord(c rune) bool { - return runeIsLowerChar(c) || runeIsUpperChar(c) -} +// +// Indent format multi line text +// from package: github.com/kr/text +// -func runeIsLowerChar(c rune) bool { - return 'a' <= c && c <= 'z' +// Indent inserts prefix at the beginning of each non-empty line of s. The +// end-of-line marker is NL. +func Indent(s, prefix string) string { + return string(IndentBytes([]byte(s), []byte(prefix))) } -func runeIsUpperChar(c rune) bool { - return 'A' <= c && c <= 'Z' +// IndentBytes inserts prefix at the beginning of each non-empty line of b. +// The end-of-line marker is NL. +func IndentBytes(b, prefix []byte) []byte { + if len(b) == 0 { + return b + } + + bol := true + res := make([]byte, 0, len(b)+len(prefix)*4) + + for _, c := range b { + if bol && c != '\n' { + res = append(res, prefix...) + } + res = append(res, c) + bol = c == '\n' + } + return res } diff --git a/vendor/github.com/gookit/goutil/strutil/id.go b/vendor/github.com/gookit/goutil/strutil/id.go index 7a0256d3..053ba10d 100644 --- a/vendor/github.com/gookit/goutil/strutil/id.go +++ b/vendor/github.com/gookit/goutil/strutil/id.go @@ -8,8 +8,9 @@ import ( ) // global id: +// // https://github.com/rs/xid -// https://github.com/satori/go.uuid +// https://github.com/satori/go.uuid var ( DefMinInt = 1000 DefMaxInt = 9999 diff --git a/vendor/github.com/gookit/goutil/strutil/padding.go b/vendor/github.com/gookit/goutil/strutil/padding.go new file mode 100644 index 00000000..97a06b4d --- /dev/null +++ b/vendor/github.com/gookit/goutil/strutil/padding.go @@ -0,0 +1,168 @@ +package strutil + +import ( + "fmt" + "strings" +) + +// PosFlag type +type PosFlag uint8 + +// Position for padding/resize string +const ( + PosLeft PosFlag = iota + PosRight + PosMiddle +) + +/************************************************************* + * String padding operation + *************************************************************/ + +// Padding a string. +func Padding(s, pad string, length int, pos PosFlag) string { + diff := len(s) - length + if diff >= 0 { // do not need padding. + return s + } + + if pad == "" || pad == " " { + mark := "" + if pos == PosRight { // to right + mark = "-" + } + + // padding left: "%7s", padding right: "%-7s" + tpl := fmt.Sprintf("%s%d", mark, length) + return fmt.Sprintf(`%`+tpl+`s`, s) + } + + if pos == PosRight { // to right + return s + Repeat(pad, -diff) + } + return Repeat(pad, -diff) + s +} + +// PadLeft a string. +func PadLeft(s, pad string, length int) string { + return Padding(s, pad, length, PosLeft) +} + +// PadRight a string. +func PadRight(s, pad string, length int) string { + return Padding(s, pad, length, PosRight) +} + +// Resize a string by given length and align settings. padding space. +func Resize(s string, length int, align PosFlag) string { + diff := len(s) - length + if diff >= 0 { // do not need padding. + return s + } + + if align == PosMiddle { + strLn := len(s) + padLn := (length - strLn) / 2 + padStr := string(make([]byte, padLn)) + + if diff := length - padLn*2; diff > 0 { + s += " " + } + return padStr + s + padStr + } + + return Padding(s, " ", length, align) +} + +// PadChars padding a rune/byte to want length and with position flag +func PadChars[T byte | rune](cs []T, pad T, length int, pos PosFlag) []T { + ln := len(cs) + if ln >= length { + ns := make([]T, length) + copy(ns, cs[:length]) + return ns + } + + idx := length - ln + ns := make([]T, length) + ps := RepeatChars(pad, idx) + if pos == PosRight { + copy(ns, cs) + copy(ns[idx:], ps) + } else { // to left + copy(ns[:idx], ps) + copy(ns[idx:], cs) + } + + return ns +} + +// PadBytes padding a byte to want length and with position flag +func PadBytes(bs []byte, pad byte, length int, pos PosFlag) []byte { + return PadChars(bs, pad, length, pos) +} + +// PadBytesLeft a byte to want length +func PadBytesLeft(bs []byte, pad byte, length int) []byte { + return PadChars(bs, pad, length, PosLeft) +} + +// PadBytesRight a byte to want length +func PadBytesRight(bs []byte, pad byte, length int) []byte { + return PadChars(bs, pad, length, PosRight) +} + +// PadRunes padding a rune to want length and with position flag +func PadRunes(rs []rune, pad rune, length int, pos PosFlag) []rune { + return PadChars(rs, pad, length, pos) +} + +// PadRunesLeft a rune to want length +func PadRunesLeft(rs []rune, pad rune, length int) []rune { + return PadChars(rs, pad, length, PosLeft) +} + +// PadRunesRight a rune to want length +func PadRunesRight(rs []rune, pad rune, length int) []rune { + return PadChars(rs, pad, length, PosRight) +} + +/************************************************************* + * String repeat operation + *************************************************************/ + +// Repeat a string +func Repeat(s string, times int) string { + if times <= 0 { + return "" + } + if times == 1 { + return s + } + + ss := make([]string, 0, times) + for i := 0; i < times; i++ { + ss = append(ss, s) + } + + return strings.Join(ss, "") +} + +// RepeatRune repeat a rune char. +func RepeatRune(char rune, times int) []rune { + return RepeatChars(char, times) +} + +// RepeatBytes repeat a byte char. +func RepeatBytes(char byte, times int) []byte { + return RepeatChars(char, times) +} + +// RepeatChars repeat a byte char. +func RepeatChars[T byte | rune](char T, times int) []T { + chars := make([]T, 0, times) + for i := 0; i < times; i++ { + chars = append(chars, char) + } + return chars +} diff --git a/vendor/github.com/gookit/goutil/strutil/random.go b/vendor/github.com/gookit/goutil/strutil/random.go index ac44581e..562039b2 100644 --- a/vendor/github.com/gookit/goutil/strutil/random.go +++ b/vendor/github.com/gookit/goutil/strutil/random.go @@ -1,38 +1,22 @@ package strutil import ( - "crypto/md5" "crypto/rand" "encoding/base64" - "encoding/hex" - "fmt" mathRand "math/rand" "time" ) +// some consts string chars const ( + Numbers = "0123456789" AlphaBet = "abcdefghijklmnopqrstuvwxyz" + AlphaBet1 = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz" AlphaNum = "abcdefghijklmnopqrstuvwxyz0123456789" AlphaNum2 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + AlphaNum3 = "0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz" ) -// Md5 Generate a 32-bit md5 string -func Md5(src interface{}) string { - return GenMd5(src) -} - -// GenMd5 Generate a 32-bit md5 string -func GenMd5(src interface{}) string { - h := md5.New() - if s, ok := src.(string); ok { - h.Write([]byte(s)) - } else { - h.Write([]byte(fmt.Sprint(src))) - } - - return hex.EncodeToString(h.Sum(nil)) -} - // RandomChars generate give length random chars at `a-z` func RandomChars(ln int) string { cs := make([]byte, ln) @@ -86,12 +70,13 @@ func RandomBytes(length int) ([]byte, error) { // RandomString generate. // Example: -// // this will give us a 44 byte, base64 encoded output -// token, err := RandomString(32) -// if err != nil { -// // Serve an appropriately vague error to the -// // user, but log the details internally. -// } +// +// // this will give us a 44 byte, base64 encoded output +// token, err := RandomString(32) +// if err != nil { +// // Serve an appropriately vague error to the +// // user, but log the details internally. +// } func RandomString(length int) (string, error) { b, err := RandomBytes(length) return base64.URLEncoding.EncodeToString(b), err diff --git a/vendor/github.com/gookit/goutil/strutil/runes.go b/vendor/github.com/gookit/goutil/strutil/runes.go new file mode 100644 index 00000000..41754f56 --- /dev/null +++ b/vendor/github.com/gookit/goutil/strutil/runes.go @@ -0,0 +1,227 @@ +package strutil + +import ( + "strings" + "unicode" + "unicode/utf8" + + "golang.org/x/text/width" +) + +// RuneIsWord char: a-zA-Z +func RuneIsWord(c rune) bool { + return RuneIsLower(c) || RuneIsUpper(c) +} + +// RuneIsLower char +func RuneIsLower(c rune) bool { + return 'a' <= c && c <= 'z' +} + +// RuneIsUpper char +func RuneIsUpper(c rune) bool { + return 'A' <= c && c <= 'Z' +} + +// RunePos alias of the strings.IndexRune +func RunePos(s string, ru rune) int { return strings.IndexRune(s, ru) } + +// IsSpaceRune returns true if the given rune is a space, otherwise false. +func IsSpaceRune(r rune) bool { + return r <= 256 && IsSpace(byte(r)) || unicode.IsSpace(r) +} + +// Utf8Len count of the string +func Utf8Len(s string) int { return utf8.RuneCountInString(s) } + +// Utf8len of the string +func Utf8len(s string) int { return utf8.RuneCountInString(s) } + +// RuneCount of the string +func RuneCount(s string) int { return len([]rune(s)) } + +// RuneWidth of the rune. +// +// Example: +// +// RuneWidth('你') // 2 +// RuneWidth('a') // 1 +// RuneWidth('\n') // 0 +func RuneWidth(r rune) int { + p := width.LookupRune(r) + k := p.Kind() + + // eg: "\n" + if k == width.Neutral { + return 0 + } + + if k == width.EastAsianFullwidth || k == width.EastAsianWide || k == width.EastAsianAmbiguous { + return 2 + } + return 1 +} + +// TextWidth utf8 string width. alias of RunesWidth() +func TextWidth(s string) int { return Utf8Width(s) } + +// Utf8Width utf8 string width. alias of RunesWidth +func Utf8Width(s string) int { return RunesWidth([]rune(s)) } + +// RunesWidth utf8 runes string width. +// +// Examples: +// +// str := "hi,你好" +// +// strutil.Utf8Width(str) => 7 +// len(str) => 9 +// len([]rune(str)) = utf8.RuneCountInString(s) => 5 +func RunesWidth(rs []rune) (w int) { + if len(rs) == 0 { + return + } + + for _, runeVal := range rs { + w += RuneWidth(runeVal) + } + return w +} + +// TextTruncate alias of the Utf8Truncate() +func TextTruncate(s string, w int, tail string) string { return Utf8Truncate(s, w, tail) } + +// Utf8Truncate a string with given width. +func Utf8Truncate(s string, w int, tail string) string { + if sw := Utf8Width(s); sw <= w { + return s + } + + i := 0 + r := []rune(s) + w -= TextWidth(tail) + + tmpW := 0 + for ; i < len(r); i++ { + cw := RuneWidth(r[i]) + if tmpW+cw > w { + break + } + tmpW += cw + } + return string(r[0:i]) + tail +} + +// TextSplit alias of the Utf8Split() +func TextSplit(s string, w int) []string { return Utf8Split(s, w) } + +// Utf8Split split a string by width. +func Utf8Split(s string, w int) []string { + sw := Utf8Width(s) + if sw <= w { + return []string{s} + } + + tmpW := 0 + tmpS := "" + + ss := make([]string, 0, sw/w+1) + for _, r := range s { + rw := RuneWidth(r) + if tmpW+rw == w { + tmpS += string(r) + ss = append(ss, tmpS) + + tmpW, tmpS = 0, "" // reset + continue + } + + if tmpW+rw > w { + ss = append(ss, tmpS) + + // append to next line. + tmpW, tmpS = rw, string(r) + continue + } + + tmpW += rw + tmpS += string(r) + } + + if tmpW > 0 { + ss = append(ss, tmpS) + } + return ss +} + +// TextWrap a string by "\n" +func TextWrap(s string, w int) string { return WidthWrap(s, w) } + +// WidthWrap a string by "\n" +func WidthWrap(s string, w int) string { + tmpW := 0 + out := "" + + for _, r := range s { + cw := RuneWidth(r) + if r == '\n' { + out += string(r) + tmpW = 0 + continue + } + + if tmpW+cw > w { + out += "\n" + tmpW = 0 + out += string(r) + tmpW += cw + continue + } + + out += string(r) + tmpW += cw + } + return out +} + +// WordWrap text string and limit width. +func WordWrap(s string, w int) string { + tmpW := 0 + out := "" + + for _, sub := range strings.Split(s, " ") { + cw := TextWidth(sub) + if tmpW+cw > w { + if tmpW != 0 { + out += "\n" + } + + tmpW = 0 + out += sub + tmpW += cw + continue + } + + out += sub + tmpW += cw + } + return out +} + +// Runes data slice +type Runes []rune + +// Padding a rune to want length and with position +func (rs Runes) Padding(pad rune, length int, pos PosFlag) []rune { + return PadChars(rs, pad, length, pos) +} + +// PadLeft a rune to want length +func (rs Runes) PadLeft(pad rune, length int) []rune { + return rs.Padding(pad, length, PosLeft) +} + +// PadRight a rune to want length +func (rs Runes) PadRight(pad rune, length int) []rune { + return rs.Padding(pad, length, PosRight) +} diff --git a/vendor/github.com/gookit/goutil/strutil/similar_find.go b/vendor/github.com/gookit/goutil/strutil/similar_find.go index 6ca0ecda..94c4fe18 100644 --- a/vendor/github.com/gookit/goutil/strutil/similar_find.go +++ b/vendor/github.com/gookit/goutil/strutil/similar_find.go @@ -2,7 +2,8 @@ package strutil // SimilarComparator definition // links: -// https://github.com/mkideal/cli/blob/master/fuzzy.go +// +// https://github.com/mkideal/cli/blob/master/fuzzy.go type SimilarComparator struct { src, dst string } @@ -14,8 +15,9 @@ func NewComparator(src, dst string) *SimilarComparator { // Similar by minDifferRate // Usage: -// c := NewComparator("hello", "he") -// rate, ok :c.Similar(0.3) +// +// c := NewComparator("hello", "he") +// rate, ok :c.Similar(0.3) func (c *SimilarComparator) Similar(minDifferRate float32) (float32, bool) { dist := c.editDistance([]byte(c.src), []byte(c.dst)) differRate := dist / float32(max(len(c.src), len(c.dst))+4) @@ -68,7 +70,8 @@ func max(x, y int) int { // Similarity calc for two string. // Usage: -// rate, ok := Similarity("hello", "he") +// +// rate, ok := Similarity("hello", "he") func Similarity(s, t string, rate float32) (float32, bool) { return NewComparator(s, t).Similar(rate) } diff --git a/vendor/github.com/gookit/goutil/strutil/split.go b/vendor/github.com/gookit/goutil/strutil/split.go index 6527268b..bc423528 100644 --- a/vendor/github.com/gookit/goutil/strutil/split.go +++ b/vendor/github.com/gookit/goutil/strutil/split.go @@ -20,10 +20,16 @@ func MustCut(s, sep string) (before string, after string) { return } -// SplitValid string to slice. will filter empty string node. +// TrimCut always returns two substring and trim space for items. +func TrimCut(s, sep string) (string, string) { + before, after, _ := Cut(s, sep) + return strings.TrimSpace(before), strings.TrimSpace(after) +} + +// SplitValid string to slice. will trim each item and filter empty string node. func SplitValid(s, sep string) (ss []string) { return Split(s, sep) } -// Split string to slice. will filter empty string node. +// Split string to slice. will trim each item and filter empty string node. func Split(s, sep string) (ss []string) { if s = strings.TrimSpace(s); s == "" { return @@ -106,3 +112,15 @@ func Substr(s string, pos, length int) string { return string(runes[pos:stopIdx]) } + +// SplitInlineComment for a text string. +func SplitInlineComment(val string) (string, string) { + if pos := strings.IndexByte(val, '#'); pos > -1 { + return strings.TrimRight(val[0:pos], " "), val[pos:] + } + + if pos := strings.Index(val, "//"); pos > -1 { + return strings.TrimRight(val[0:pos], " "), val[pos:] + } + return val, "" +} diff --git a/vendor/github.com/gookit/goutil/strutil/str.go b/vendor/github.com/gookit/goutil/strutil/str.go deleted file mode 100644 index 4dfb84e8..00000000 --- a/vendor/github.com/gookit/goutil/strutil/str.go +++ /dev/null @@ -1,36 +0,0 @@ -package strutil - -import "strings" - -// Str string -type Str string - -// IsStartBy prefix -func (s Str) IsStartBy(sub string) bool { - return strings.HasPrefix(string(s), sub) -} - -// IsEndBy suffix -func (s Str) IsEndBy(sub string) bool { - return strings.HasSuffix(string(s), sub) -} - -// Bytes string to bytes -func (s Str) Bytes() []byte { - return []byte(s) -} - -// Get string -func (s Str) Get() string { - return string(s) -} - -// String string -func (s Str) String() string { - return string(s) -} - -// TrimSpace string -func (s Str) TrimSpace() Str { - return Str(strings.TrimSpace(string(s))) -} diff --git a/vendor/github.com/gookit/goutil/strutil/strutil.go b/vendor/github.com/gookit/goutil/strutil/strutil.go index 5a08c3e9..ea1f69d5 100644 --- a/vendor/github.com/gookit/goutil/strutil/strutil.go +++ b/vendor/github.com/gookit/goutil/strutil/strutil.go @@ -9,91 +9,13 @@ import ( "text/template" ) -// Position for padding string -const ( - PosLeft uint8 = iota - PosRight -) - -/************************************************************* - * String padding operation - *************************************************************/ - -// Padding a string. -func Padding(s, pad string, length int, pos uint8) string { - diff := len(s) - length - if diff >= 0 { // do not need padding. - return s - } - - if pad == "" || pad == " " { - mark := "" - if pos == PosRight { // to right - mark = "-" - } - - // padding left: "%7s", padding right: "%-7s" - tpl := fmt.Sprintf("%s%d", mark, length) - return fmt.Sprintf(`%`+tpl+`s`, s) - } - - if pos == PosRight { // to right - return s + Repeat(pad, -diff) - } - - return Repeat(pad, -diff) + s -} - -// PadLeft a string. -func PadLeft(s, pad string, length int) string { - return Padding(s, pad, length, PosLeft) -} - -// PadRight a string. -func PadRight(s, pad string, length int) string { - return Padding(s, pad, length, PosRight) -} - -/************************************************************* - * String repeat operation - *************************************************************/ - -// Repeat a string -func Repeat(s string, times int) string { - if times < 2 { - return s - } - - var ss []string - for i := 0; i < times; i++ { - ss = append(ss, s) - } - - return strings.Join(ss, "") -} - -// RepeatRune repeat a rune char. -func RepeatRune(char rune, times int) (chars []rune) { - for i := 0; i < times; i++ { - chars = append(chars, char) - } - return -} - -// RepeatBytes repeat a byte char. -func RepeatBytes(char byte, times int) (chars []byte) { - for i := 0; i < times; i++ { - chars = append(chars, char) - } - return -} - // Replaces replace multi strings // -// pairs: {old1: new1, old2: new2, ...} +// pairs: {old1: new1, old2: new2, ...} // // Can also use: -// strings.NewReplacer("old1", "new1", "old2", "new2").Replace(str) +// +// strings.NewReplacer("old1", "new1", "old2", "new2").Replace(str) func Replaces(str string, pairs map[string]string) string { ss := make([]string, len(pairs)*2) for old, newVal := range pairs { @@ -105,18 +27,18 @@ func Replaces(str string, pairs map[string]string) string { // PrettyJSON get pretty Json string // Deprecated: please use fmtutil.PrettyJSON() or jsonutil.Pretty() instead it -func PrettyJSON(v interface{}) (string, error) { +func PrettyJSON(v any) (string, error) { out, err := json.MarshalIndent(v, "", " ") return string(out), err } // RenderTemplate render text template -func RenderTemplate(input string, data interface{}, fns template.FuncMap, isFile ...bool) string { +func RenderTemplate(input string, data any, fns template.FuncMap, isFile ...bool) string { return RenderText(input, data, fns, isFile...) } // RenderText render text template -func RenderText(input string, data interface{}, fns template.FuncMap, isFile ...bool) string { +func RenderText(input string, data any, fns template.FuncMap, isFile ...bool) string { t := template.New("simple-text") t.Funcs(template.FuncMap{ // don't escape content @@ -140,7 +62,7 @@ func RenderText(input string, data interface{}, fns template.FuncMap, isFile ... }, }) - // custom add template functions + // add custom template functions if len(fns) > 0 { t.Funcs(fns) } @@ -156,6 +78,13 @@ func RenderText(input string, data interface{}, fns template.FuncMap, isFile ... if err := t.Execute(buf, data); err != nil { panic(err) } - return buf.String() } + +// WrapTag for given string. +func WrapTag(s, tag string) string { + if s == "" { + return s + } + return fmt.Sprintf("<%s>%s", tag, s, tag) +} diff --git a/vendor/github.com/gookit/goutil/strutil/value.go b/vendor/github.com/gookit/goutil/strutil/value.go new file mode 100644 index 00000000..7e4bdc25 --- /dev/null +++ b/vendor/github.com/gookit/goutil/strutil/value.go @@ -0,0 +1,85 @@ +package strutil + +import "strings" + +// Value string +type Value string + +// StrVal string. alias of Value +type StrVal = Value + +// Set value +func (s *Value) Set(val string) error { + *s = Value(val) + return nil +} + +// IsEmpty check +func (s *Value) IsEmpty() bool { + return string(*s) == "" +} + +// IsStartWith prefix +func (s *Value) IsStartWith(sub string) bool { + return strings.HasPrefix(string(*s), sub) +} + +// HasPrefix prefix +func (s *Value) HasPrefix(sub string) bool { + return strings.HasPrefix(string(*s), sub) +} + +// IsEndWith suffix +func (s *Value) IsEndWith(sub string) bool { + return strings.HasSuffix(string(*s), sub) +} + +// HasSuffix suffix +func (s *Value) HasSuffix(sub string) bool { + return strings.HasSuffix(string(*s), sub) +} + +// Bytes string to bytes +func (s *Value) Bytes() []byte { + return []byte(*s) +} + +// Val string +func (s *Value) Val() string { + return string(*s) +} + +// Int convert +func (s *Value) Int() int { + return QuietInt(string(*s)) +} + +// Int64 convert +func (s *Value) Int64() int64 { + return QuietInt64(string(*s)) +} + +// Bool convert +func (s *Value) Bool() bool { + return QuietBool(string(*s)) +} + +// Value string +func (s *Value) String() string { + return string(*s) +} + +// Split string +func (s *Value) Split(sep string) []string { + return strings.Split(string(*s), sep) +} + +// SplitN string +func (s *Value) SplitN(sep string, n int) []string { + return strings.SplitN(string(*s), sep, n) +} + +// TrimSpace string and return new +func (s *Value) TrimSpace() Value { + return Value(strings.TrimSpace(string(*s))) +} diff --git a/vendor/github.com/gookit/goutil/sysutil/README.md b/vendor/github.com/gookit/goutil/sysutil/README.md new file mode 100644 index 00000000..79c2b49e --- /dev/null +++ b/vendor/github.com/gookit/goutil/sysutil/README.md @@ -0,0 +1,140 @@ +# System Utils + +Provide some system util functions. eg: sysenv, exec, user, process + +- quick exec a command line string. +- quick build a command for run + +## Install + +```bash +go get github.com/gookit/goutil/sysutil +``` + +## Usage + +```go +out, err := sysutil.ExecCmd("ls", []string{"-al"}) +``` + +## Clipboard + +Package `clipboard` provide a simple clipboard read and write operations. + +### Install + +```bash +go get github.com/gookit/goutil/sysutil/clipboard +``` + +### Usage + +Examples: + +```go +src := "hello, this is clipboard" +err = clipboard.WriteString(src) +assert.NoErr(t, err) + +// str: "hello, this is clipboard" +str, err = clipboard.ReadString() +assert.NoErr(t, err) +assert.NotEmpty(t, str) +assert.Eq(t, src, str) +``` + +## Cmd run + +### Install + +```bash +go get github.com/gookit/goutil/sysutil/cmdr +``` +### Usage + +**Cmd Builder**: + +```go +c := cmdr.NewCmd("ls"). + WithArg("-l"). + WithArgs([]string{"-h"}). + AddArg("-a"). + AddArgf("%s", "./") + +c.OnBefore(func(c *cmdr.Cmd) { + assert.Eq(t, "ls -l -h -a ./", c.Cmdline()) +}) + +out := c.SafeOutput() +fmt.Println(out) +``` + +**Batch Cmd Tasks**: + +Can use `cmdr.Runner` run multi cmd tasks at once. + +```go +buf := new(bytes.Buffer) +rr := cmdr.NewRunner() + +rr.Add(&cmdr.Task{ + ID: "task1", + Cmd: cmdr.NewCmd("id", "-F").WithOutput(buf, buf), +}) +rr.AddCmd(cmdr.NewCmd("ls").AddArgs([]string{"-l", "-h"}).WithOutput(buf, buf)) + +err = rr.Run() +``` + +### Functions API + +```go +func BinDir() string +func BinFile() string +func ChangeUserByName(newUname string) (err error) +func ChangeUserUidGid(newUid int, newGid int) (err error) +func CurrentShell(onlyName bool) (path string) +func CurrentUser() *user.User +func EnvPaths() []string +func ExecCmd(binName string, args []string, workDir ...string) (string, error) +func ExecLine(cmdLine string, workDir ...string) (string, error) +func Executable(binName string) (string, error) +func ExpandPath(path string) string +func FindExecutable(binName string) (string, error) +func FlushExec(bin string, args ...string) error +func GoVersion() string +func HasExecutable(binName string) bool +func HasShellEnv(shell string) bool +func HomeDir() string +func Hostname() string +func IsConsole(out io.Writer) bool +func IsDarwin() bool +func IsLinux() bool +func IsMSys() bool +func IsMac() bool +func IsShellSpecialVar(c uint8) bool +func IsTerminal(fd uintptr) bool +func IsWin() bool +func IsWindows() bool +func Kill(pid int, signal syscall.Signal) error +func LoginUser() *user.User +func MustFindUser(uname string) *user.User +func NewCmd(bin string, args ...string) *cmdr.Cmd +func OpenBrowser(URL string) error +func ProcessExists(pid int) bool +func QuickExec(cmdLine string, workDir ...string) (string, error) +func SearchPath(keywords string) []string +func ShellExec(cmdLine string, shells ...string) (string, error) +func StdIsTerminal() bool +func UHomeDir() string +func UserCacheDir(subPath string) string +func UserConfigDir(subPath string) string +func UserDir(subPath string) string +func UserHomeDir() string +func Workdir() string +type CallerInfo struct{ ... } + func CallersInfos(skip, num int, filters ...func(file string, fc *runtime.Func) bool) []*CallerInfo +type GoInfo struct{ ... } + func OsGoInfo() (*GoInfo, error) + func ParseGoVersion(line string) (*GoInfo, error) +``` diff --git a/vendor/github.com/gookit/goutil/sysutil/cmdr/cmd.go b/vendor/github.com/gookit/goutil/sysutil/cmdr/cmd.go new file mode 100644 index 00000000..b3f09d2b --- /dev/null +++ b/vendor/github.com/gookit/goutil/sysutil/cmdr/cmd.go @@ -0,0 +1,388 @@ +package cmdr + +import ( + "context" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + + "github.com/gookit/color" + "github.com/gookit/goutil" + "github.com/gookit/goutil/arrutil" + "github.com/gookit/goutil/internal/comfunc" +) + +// Cmd struct +type Cmd struct { + *exec.Cmd + // Name of the command + Name string + // inited bool + + // BeforeRun hook + BeforeRun func(c *Cmd) + // AfterRun hook + AfterRun func(c *Cmd, err error) +} + +// WrapGoCmd instance +func WrapGoCmd(cmd *exec.Cmd) *Cmd { + return &Cmd{Cmd: cmd} +} + +// NewGitCmd instance +func NewGitCmd(subCmd string, args ...string) *Cmd { + return NewCmd("git", subCmd).AddArgs(args) +} + +// NewCmd instance +// +// see exec.Command +func NewCmd(bin string, args ...string) *Cmd { + return &Cmd{ + Cmd: exec.Command(bin, args...), + } +} + +// CmdWithCtx create new instance with context. +// +// see exec.CommandContext +func CmdWithCtx(ctx context.Context, bin string, args ...string) *Cmd { + return &Cmd{ + Cmd: exec.CommandContext(ctx, bin, args...), + } +} + +// PrintCmdline on before exec +func PrintCmdline(c *Cmd) { + color.Yellowln(">", c.Cmdline()) +} + +// ------------------------------------------------- +// config the command +// ------------------------------------------------- + +// Config the command +func (c *Cmd) Config(fn func(c *Cmd)) *Cmd { + fn(c) + return c +} + +// OnBefore exec add hook +func (c *Cmd) OnBefore(fn func(c *Cmd)) *Cmd { + c.BeforeRun = fn + return c +} + +// OnAfter exec add hook +func (c *Cmd) OnAfter(fn func(c *Cmd, err error)) *Cmd { + c.AfterRun = fn + return c +} + +// WithBin name returns the current object +func (c *Cmd) WithBin(name string) *Cmd { + c.Args[0] = name + c.lookPath(name) + return c +} + +func (c *Cmd) lookPath(name string) { + if filepath.Base(name) == name { + lp, err := exec.LookPath(name) + if lp != "" { + // Update cmd.Path even if err is non-nil. + // If err is ErrDot (especially on Windows), lp may include a resolved + // extension (like .exe or .bat) that should be preserved. + c.Path = lp + } + if err != nil { + goutil.Panicf("look %q path error: %s", name, err.Error()) + } + } +} + +// WithGoCmd and returns the current instance. +func (c *Cmd) WithGoCmd(ec *exec.Cmd) *Cmd { + c.Cmd = ec + return c +} + +// WithWorkDir returns the current object +func (c *Cmd) WithWorkDir(dir string) *Cmd { + c.Dir = dir + return c +} + +// WorkDirOnNot set, returns the current object +func (c *Cmd) WorkDirOnNot(dir string) *Cmd { + if c.Dir == "" { + c.Dir = dir + } + return c +} + +// OutputToStd output to OS stdout and error +func (c *Cmd) OutputToStd() *Cmd { + c.Stdout = os.Stdout + c.Stderr = os.Stderr + return c +} + +// WithStdin returns the current argument +func (c *Cmd) WithStdin(in io.Reader) *Cmd { + c.Stdin = in + return c +} + +// WithOutput returns the current instance +func (c *Cmd) WithOutput(out, errOut io.Writer) *Cmd { + c.Stdout = out + if errOut != nil { + c.Stderr = errOut + } + return c +} + +// WithAnyArgs add args and returns the current object. +func (c *Cmd) WithAnyArgs(args ...any) *Cmd { + c.Args = append(c.Args, arrutil.SliceToStrings(args)...) + return c +} + +// AddArg add args and returns the current object +func (c *Cmd) AddArg(args ...string) *Cmd { return c.WithArg(args...) } + +// WithArg add args and returns the current object. alias of the WithArg() +func (c *Cmd) WithArg(args ...string) *Cmd { + c.Args = append(c.Args, args...) + return c +} + +// AddArgf add args and returns the current object. alias of the WithArgf() +func (c *Cmd) AddArgf(format string, args ...any) *Cmd { + return c.WithArgf(format, args...) +} + +// WithArgf add arg and returns the current object +func (c *Cmd) WithArgf(format string, args ...any) *Cmd { + c.Args = append(c.Args, fmt.Sprintf(format, args...)) + return c +} + +// ArgIf add arg and returns the current object +func (c *Cmd) ArgIf(arg string, exprOk bool) *Cmd { + if exprOk { + c.Args = append(c.Args, arg) + } + return c +} + +// WithArgIf add arg and returns the current object +func (c *Cmd) WithArgIf(arg string, exprOk bool) *Cmd { + return c.ArgIf(arg, exprOk) +} + +// AddArgs for the git. alias of WithArgs() +func (c *Cmd) AddArgs(args []string) *Cmd { return c.WithArgs(args) } + +// WithArgs for the git +func (c *Cmd) WithArgs(args []string) *Cmd { + if len(args) > 0 { + c.Args = append(c.Args, args...) + } + return c +} + +// WithArgsIf add arg and returns the current object +func (c *Cmd) WithArgsIf(args []string, exprOk bool) *Cmd { + if exprOk && len(args) > 0 { + c.Args = append(c.Args, args...) + } + return c +} + +// ------------------------------------------------- +// helper command +// ------------------------------------------------- + +// IDString of the command +func (c *Cmd) IDString() string { + if c.Name != "" { + return c.Name + } + return c.BinOrPath() +} + +// BinName of the command +func (c *Cmd) BinName() string { + if len(c.Args) > 0 { + return c.Args[0] + } + return "" +} + +// BinOrPath of the command +func (c *Cmd) BinOrPath() string { + if len(c.Args) > 0 { + return c.Args[0] + } + return c.Path +} + +// OnlyArgs of the command, not contains bin name. +func (c *Cmd) OnlyArgs() (ss []string) { + if len(c.Args) > 1 { + return c.Args[1:] + } + return +} + +// ResetArgs for command, but will keep bin name. +func (c *Cmd) ResetArgs() { + if len(c.Args) > 0 { + c.Args = c.Args[0:1] + } else { + c.Args = c.Args[:0] + } +} + +// Cmdline to command line +func (c *Cmd) Cmdline() string { + return comfunc.Cmdline(c.Args) +} + +// Copy new instance from current command, with new args. +func (c *Cmd) Copy(args ...string) *Cmd { + nc := *c + + // copy bin name. + if len(c.Args) > 0 { + nc.Args = append([]string{c.Args[0]}, args...) + } else { + nc.Args = args + } + + return &nc +} + +// GoCmd get exec.Cmd +func (c *Cmd) GoCmd() *exec.Cmd { return c.Cmd } + +// ------------------------------------------------- +// run command +// ------------------------------------------------- + +// Success run and return whether success +func (c *Cmd) Success() bool { + return c.Run() == nil +} + +// SafeLines run and return output as lines +func (c *Cmd) SafeLines() []string { + ss, _ := c.OutputLines() + return ss +} + +// OutputLines run and return output as lines +func (c *Cmd) OutputLines() ([]string, error) { + out, err := c.Output() + if err != nil { + return nil, err + } + return OutputLines(out), err +} + +// SafeOutput run and return output +func (c *Cmd) SafeOutput() string { + out, err := c.Output() + if err != nil { + return "" + } + return out +} + +// Output run and return output +func (c *Cmd) Output() (string, error) { + if c.BeforeRun != nil { + c.BeforeRun(c) + } + + output, err := c.Cmd.Output() + + if c.AfterRun != nil { + c.AfterRun(c, err) + } + return string(output), err +} + +// CombinedOutput run and return output, will combine stderr and stdout output +func (c *Cmd) CombinedOutput() (string, error) { + if c.BeforeRun != nil { + c.BeforeRun(c) + } + + output, err := c.Cmd.CombinedOutput() + + if c.AfterRun != nil { + c.AfterRun(c, err) + } + return string(output), err +} + +// MustRun a command. will panic on error +func (c *Cmd) MustRun() { + if err := c.Run(); err != nil { + panic(err) + } +} + +// FlushRun runs command and flush output to stdout +func (c *Cmd) FlushRun() error { + c.OutputToStd() + return c.Run() +} + +// Run runs command +func (c *Cmd) Run() error { + if c.BeforeRun != nil { + c.BeforeRun(c) + } + + // do running + err := c.Cmd.Run() + + if c.AfterRun != nil { + c.AfterRun(c, err) + } + return err + + // if IsWindows() { + // return c.Spawn() + // } + // return c.Exec() +} + +// Spawn runs command with spawn(3) +// func (c *Cmd) Spawn() error { +// return c.Cmd.Run() +// } +// +// // Exec runs command with exec(3) +// // Note that Windows doesn't support exec(3): http://golang.org/src/pkg/syscall/exec_windows.go#L339 +// func (c *Cmd) Exec() error { +// binary, err := exec.LookPath(c.Path) +// if err != nil { +// return &exec.Error{ +// Name: c.Path, +// Err: errorx.Newf("%s not found in the system", c.Path), +// } +// } +// +// args := []string{binary} +// args = append(args, c.Args...) +// +// return syscall.Exec(binary, args, os.Environ()) +// } diff --git a/vendor/github.com/gookit/goutil/sysutil/cmdr/cmdr.go b/vendor/github.com/gookit/goutil/sysutil/cmdr/cmdr.go new file mode 100644 index 00000000..695d9867 --- /dev/null +++ b/vendor/github.com/gookit/goutil/sysutil/cmdr/cmdr.go @@ -0,0 +1,21 @@ +// Package cmdr Provide for quick build and run a cmd, batch run multi cmd tasks +package cmdr + +import "strings" + +// OutputLines split output to lines +func OutputLines(output string) []string { + output = strings.TrimSuffix(output, "\n") + if output == "" { + return nil + } + return strings.Split(output, "\n") +} + +// FirstLine from command output +func FirstLine(output string) string { + if i := strings.Index(output, "\n"); i >= 0 { + return output[0:i] + } + return output +} diff --git a/vendor/github.com/gookit/goutil/sysutil/cmdr/runner.go b/vendor/github.com/gookit/goutil/sysutil/cmdr/runner.go new file mode 100644 index 00000000..c01ffd97 --- /dev/null +++ b/vendor/github.com/gookit/goutil/sysutil/cmdr/runner.go @@ -0,0 +1,248 @@ +package cmdr + +import ( + "fmt" + + "github.com/gookit/color" + "github.com/gookit/goutil/arrutil" + "github.com/gookit/goutil/errorx" + "github.com/gookit/goutil/maputil" + "github.com/gookit/goutil/mathutil" +) + +// Task struct +type Task struct { + err error + index int + + // ID for task + ID string + Cmd *Cmd + + // BeforeRun hook + BeforeRun func(t *Task) + PrevCond func(prev *Task) bool +} + +func NewTask(cmd *Cmd) *Task { + return &Task{ + Cmd: cmd, + } +} + +// get task id by cmd.Name +func (t *Task) ensureID(idx int) { + t.index = idx + if t.ID != "" { + return + } + + id := t.Cmd.IDString() + if t.Cmd.Name == "" { + id += mathutil.String(idx) + } + t.ID = id +} + +// Run command +func (t *Task) Run() error { + if t.BeforeRun != nil { + t.BeforeRun(t) + } + + t.err = t.Cmd.Run() + return t.err +} + +// Err get +func (t *Task) Err() error { + return t.err +} + +// Index get +func (t *Task) Index() int { + return t.index +} + +// Cmdline get +func (t *Task) Cmdline() string { + return t.Cmd.Cmdline() +} + +// IsSuccess of task +func (t *Task) IsSuccess() bool { + return t.err == nil +} + +// Runner use for batch run multi task commands +type Runner struct { + prev *Task + // task name to index + idMap map[string]int + tasks []*Task + // Errs on run tasks, key is Task.ID + Errs errorx.ErrMap + + // TODO Concurrent run + // common workdir + // wordDir string + + // Params for add custom params + Params maputil.Map + + // DryRun dry run all commands + DryRun bool + // IgnoreErr continue on error + IgnoreErr bool + // BeforeRun hooks on each task. return false to skip current task. + BeforeRun func(r *Runner, t *Task) bool + // AfterRun hook on each task. return false to stop running. + AfterRun func(r *Runner, t *Task) bool +} + +// NewRunner instance with config func +func NewRunner(fns ...func(rr *Runner)) *Runner { + rr := &Runner{ + idMap: make(map[string]int, 0), + tasks: make([]*Task, 0), + Errs: make(errorx.ErrMap), + Params: make(maputil.Map), + } + + for _, fn := range fns { + fn(rr) + } + return rr +} + +// Add multitask at once +func (r *Runner) Add(tasks ...*Task) *Runner { + for _, task := range tasks { + r.AddTask(task) + } + return r +} + +// AddTask add one task +func (r *Runner) AddTask(task *Task) *Runner { + if task.Cmd == nil { + panic("task command cannot be empty") + } + + idx := len(r.tasks) + task.ensureID(idx) + + // TODO check id repeat + r.idMap[task.ID] = idx + r.tasks = append(r.tasks, task) + return r +} + +// AddCmd commands +func (r *Runner) AddCmd(cmds ...*Cmd) *Runner { + for _, cmd := range cmds { + r.AddTask(&Task{Cmd: cmd}) + } + return r +} + +// GitCmd quick a git command task +func (r *Runner) GitCmd(subCmd string, args ...string) *Runner { + r.AddTask(&Task{ + Cmd: NewGitCmd(subCmd, args...), + }) + return r +} + +// CmdWithArgs a command task +func (r *Runner) CmdWithArgs(cmdName string, args ...string) *Runner { + r.AddTask(&Task{ + Cmd: NewCmd(cmdName, args...), + }) + return r +} + +// CmdWithAnys a command task +func (r *Runner) CmdWithAnys(cmdName string, args ...any) *Runner { + r.AddTask(&Task{ + Cmd: NewCmd(cmdName, arrutil.SliceToStrings(args)...), + }) + return r +} + +// Run all tasks +func (r *Runner) Run() error { + // do run tasks + for i, task := range r.tasks { + if r.BeforeRun != nil && !r.BeforeRun(r, task) { + continue + } + + if r.prev != nil && task.PrevCond != nil && !task.PrevCond(r.prev) { + continue + } + + if r.DryRun { + color.Infof("DRY-RUN: task #%d execute completed\n", i+1) + continue + } + + if !r.RunTask(task) { + break + } + } + + if len(r.Errs) == 0 { + return nil + } + return r.Errs +} + +// RunTask command +func (r *Runner) RunTask(task *Task) (goon bool) { + // do running + if err := task.Run(); err != nil { + r.Errs[task.ID] = err + color.Errorf("Task #%d run error: %s\n", task.Index()+1, err) + + // not ignore error, stop. + if !r.IgnoreErr { + return false + } + } + + if r.AfterRun != nil && !r.AfterRun(r, task) { + return false + } + + // store prev + r.prev = task + return true +} + +// Len of tasks +func (r *Runner) Len() int { + return len(r.tasks) +} + +// TaskIDs get +func (r *Runner) TaskIDs() []string { + ss := make([]string, 0, len(r.idMap)) + for id := range r.idMap { + ss = append(ss, id) + } + return ss +} + +// Prev task instance after running +func (r *Runner) Prev() *Task { + return r.prev +} + +// Task get by id name +func (r *Runner) Task(id string) (*Task, error) { + if idx, ok := r.idMap[id]; ok { + return r.tasks[idx], nil + } + return nil, fmt.Errorf("task %q is not exists", id) +} diff --git a/vendor/github.com/gookit/goutil/sysutil/exec.go b/vendor/github.com/gookit/goutil/sysutil/exec.go index 190fd70e..3dc8226f 100644 --- a/vendor/github.com/gookit/goutil/sysutil/exec.go +++ b/vendor/github.com/gookit/goutil/sysutil/exec.go @@ -5,8 +5,19 @@ import ( "os/exec" "github.com/gookit/goutil/cliutil/cmdline" + "github.com/gookit/goutil/sysutil/cmdr" ) +// NewCmd instance +func NewCmd(bin string, args ...string) *cmdr.Cmd { + return cmdr.NewCmd(bin, args...) +} + +// FlushExec instance +func FlushExec(bin string, args ...string) error { + return cmdr.NewCmd(bin, args...).FlushRun() +} + // QuickExec quick exec an simple command line func QuickExec(cmdLine string, workDir ...string) (string, error) { return ExecLine(cmdLine, workDir...) @@ -26,9 +37,11 @@ func ExecLine(cmdLine string, workDir ...string) (string, error) { return string(bs), err } -// ExecCmd an command and return output. +// ExecCmd a command and return output. +// // Usage: -// ExecCmd("ls", []string{"-al"}) +// +// ExecCmd("ls", []string{"-al"}) func ExecCmd(binName string, args []string, workDir ...string) (string, error) { // create a new Cmd instance cmd := exec.Command(binName, args...) @@ -40,8 +53,7 @@ func ExecCmd(binName string, args []string, workDir ...string) (string, error) { return string(bs), err } -// ShellExec exec command by shell -// cmdStr eg. "ls -al" +// ShellExec exec command by shell cmdLine. eg: "ls -al" func ShellExec(cmdLine string, shells ...string) (string, error) { // shell := "/bin/sh" shell := "sh" @@ -50,38 +62,11 @@ func ShellExec(cmdLine string, shells ...string) (string, error) { } var out bytes.Buffer - cmd := exec.Command(shell, "-c", cmdLine) cmd.Stdout = &out if err := cmd.Run(); err != nil { return "", err } - return out.String(), nil } - -// FindExecutable in the system -// -// Usage: -// FindExecutable("bash") -func FindExecutable(binName string) (string, error) { - return exec.LookPath(binName) -} - -// Executable find in the system -// -// Usage: -// Executable("bash") -func Executable(binName string) (string, error) { - return exec.LookPath(binName) -} - -// HasExecutable in the system -// -// Usage: -// HasExecutable("bash") -func HasExecutable(binName string) bool { - _, err := exec.LookPath(binName) - return err == nil -} diff --git a/vendor/github.com/gookit/goutil/sysutil/process/pidfile.go b/vendor/github.com/gookit/goutil/sysutil/process/pidfile.go new file mode 100644 index 00000000..e225c8b4 --- /dev/null +++ b/vendor/github.com/gookit/goutil/sysutil/process/pidfile.go @@ -0,0 +1,70 @@ +package process + +import ( + "os" + + "github.com/gookit/goutil/fsutil" + "github.com/gookit/goutil/mathutil" + "github.com/gookit/goutil/strutil" +) + +// PidFile struct +type PidFile struct { + pid int + file string + // body string +} + +// NewPidFile instance +func NewPidFile(file string) *PidFile { + return &PidFile{ + file: file, + } +} + +// Exists of th pid file +func (pf *PidFile) Exists() bool { + return fsutil.FileExist(pf.file) +} + +// File path +func (pf *PidFile) File() string { + return pf.file +} + +// PID value +func (pf *PidFile) PID() int { + if pf.pid > 0 { + return pf.pid + } + + if fsutil.FileExist(pf.file) { + bts, err := os.ReadFile(pf.file) + if err == nil { + pf.pid = strutil.QuietInt(string(bts)) + } + } + + return pf.pid +} + +// String PID value string +func (pf *PidFile) String() string { + return mathutil.String(pf.pid) +} + +// SetPID value +func (pf *PidFile) SetPID(val int) int { + pf.pid = val + return pf.pid +} + +// Save PID value to file +func (pf *PidFile) Save() error { + if pf.pid < 1 { + return nil + } + + _, err := fsutil.PutContents(pf.file, pf.String()) + return err +} diff --git a/vendor/github.com/gookit/goutil/sysutil/process/process.go b/vendor/github.com/gookit/goutil/sysutil/process/process.go index 0c22bee4..6e839f1d 100644 --- a/vendor/github.com/gookit/goutil/sysutil/process/process.go +++ b/vendor/github.com/gookit/goutil/sysutil/process/process.go @@ -1,3 +1,4 @@ +// Package process Provide some process handle util functions package process import "os" diff --git a/vendor/github.com/gookit/goutil/sysutil/process/process_darwin.go b/vendor/github.com/gookit/goutil/sysutil/process/process_darwin.go index d8e49e03..187ce94c 100644 --- a/vendor/github.com/gookit/goutil/sysutil/process/process_darwin.go +++ b/vendor/github.com/gookit/goutil/sysutil/process/process_darwin.go @@ -1,3 +1,4 @@ +//go:build darwin // +build darwin package process diff --git a/vendor/github.com/gookit/goutil/sysutil/process/process_unix.go b/vendor/github.com/gookit/goutil/sysutil/process/process_unix.go index 08d46ed4..407a013b 100644 --- a/vendor/github.com/gookit/goutil/sysutil/process/process_unix.go +++ b/vendor/github.com/gookit/goutil/sysutil/process/process_unix.go @@ -1,3 +1,4 @@ +//go:build !windows && !darwin // +build !windows,!darwin package process diff --git a/vendor/github.com/gookit/goutil/sysutil/stack.go b/vendor/github.com/gookit/goutil/sysutil/stack.go new file mode 100644 index 00000000..7d4f8224 --- /dev/null +++ b/vendor/github.com/gookit/goutil/sysutil/stack.go @@ -0,0 +1,64 @@ +package sysutil + +import ( + "runtime" + "strconv" +) + +// CallerInfo struct +type CallerInfo struct { + PC uintptr + Fc *runtime.Func + File string + Line int +} + +// String convert +func (ci *CallerInfo) String() string { + return ci.File + ":" + strconv.Itoa(ci.Line) +} + +// CallersInfos returns an array of the CallerInfo. +// +// Usage: +// +// cs := sysutil.CallersInfos(3, 2) +// for _, ci := range cs { +// fc := runtime.FuncForPC(pc) +// // maybe need check fc = nil +// fnName = fc.Name() +// } +func CallersInfos(skip, num int, filters ...func(file string, fc *runtime.Func) bool) []*CallerInfo { + filterLn := len(filters) + callers := make([]*CallerInfo, 0, num) + for i := skip; i < skip+num; i++ { + pc, file, line, ok := runtime.Caller(i) + if !ok { + // The breaks below failed to terminate the loop, and we ran off the + // end of the call stack. + break + } + + fc := runtime.FuncForPC(pc) + if fc == nil { + continue + } + + if filterLn > 0 && filters[0] != nil { + // filter - return false for skip + if !filters[0](file, fc) { + continue + } + } + + // collecting + callers = append(callers, &CallerInfo{ + PC: pc, + Fc: fc, + File: file, + Line: line, + }) + } + + return callers +} diff --git a/vendor/github.com/gookit/goutil/sysutil/sysenv.go b/vendor/github.com/gookit/goutil/sysutil/sysenv.go index 55c8d69f..8e5303f0 100644 --- a/vendor/github.com/gookit/goutil/sysutil/sysenv.go +++ b/vendor/github.com/gookit/goutil/sysutil/sysenv.go @@ -3,54 +3,26 @@ package sysutil import ( "io" "os" + "os/exec" "path/filepath" - "runtime" "strings" "syscall" - "github.com/mattn/go-isatty" + "github.com/gookit/goutil/internal/comfunc" + "golang.org/x/term" ) -// Hostname is alias of os.Hostname, but ignore error -func Hostname() string { - name, _ := os.Hostname() - return name -} - -// IsWin system. linux windows darwin -func IsWin() bool { - return runtime.GOOS == "windows" -} - -// IsWindows system. linux windows darwin -func IsWindows() bool { - return runtime.GOOS == "windows" -} - -// IsMac system -func IsMac() bool { - return runtime.GOOS == "darwin" -} - -// IsLinux system -func IsLinux() bool { - return runtime.GOOS == "linux" -} - // IsMSys msys(MINGW64) env,不一定支持颜色 func IsMSys() bool { // "MSYSTEM=MINGW64" - if len(os.Getenv("MSYSTEM")) > 0 { - return true - } - - return false + return len(os.Getenv("MSYSTEM")) > 0 } // IsConsole check out is in stderr/stdout/stdin // // Usage: -// sysutil.IsConsole(os.Stdout) +// +// sysutil.IsConsole(os.Stdout) func IsConsole(out io.Writer) bool { o, ok := out.(*os.File) if !ok { @@ -66,9 +38,11 @@ func IsConsole(out io.Writer) bool { // IsTerminal isatty check // // Usage: -// sysutil.IsTerminal(os.Stdout.Fd()) +// +// sysutil.IsTerminal(os.Stdout.Fd()) func IsTerminal(fd uintptr) bool { - return isatty.IsTerminal(fd) + // return isatty.IsTerminal(fd) // "github.com/mattn/go-isatty" + return term.IsTerminal(int(fd)) } // StdIsTerminal os.Stdout is terminal @@ -76,37 +50,26 @@ func StdIsTerminal() bool { return IsTerminal(os.Stdout.Fd()) } -var curShell string +// Hostname is alias of os.Hostname, but ignore error +func Hostname() string { + name, _ := os.Hostname() + return name +} // CurrentShell get current used shell env file. +// // eg "/bin/zsh" "/bin/bash". // if onlyName=true, will return "zsh", "bash" func CurrentShell(onlyName bool) (path string) { - var err error - if curShell == "" { - path, err = ShellExec("echo $SHELL") - if err != nil { - return "" - } - - path = strings.TrimSpace(path) - // cache result - curShell = path - } else { - path = curShell - } - - if onlyName && len(path) > 0 { - path = filepath.Base(path) - } - return + return comfunc.CurrentShell(onlyName) } // HasShellEnv has shell env check. // // Usage: -// HasShellEnv("sh") -// HasShellEnv("bash") +// +// HasShellEnv("sh") +// HasShellEnv("bash") func HasShellEnv(shell string) bool { // can also use: "echo $0" out, err := ShellExec("echo OK", shell) @@ -126,3 +89,61 @@ func IsShellSpecialVar(c uint8) bool { } return false } + +// EnvPaths get and split $PATH to []string +func EnvPaths() []string { + return filepath.SplitList(os.Getenv("PATH")) +} + +// FindExecutable in the system +// +// Usage: +// +// sysutil.FindExecutable("bash") +func FindExecutable(binName string) (string, error) { + return exec.LookPath(binName) +} + +// Executable find in the system +// +// Usage: +// +// sysutil.Executable("bash") +func Executable(binName string) (string, error) { + return exec.LookPath(binName) +} + +// HasExecutable in the system +// +// Usage: +// +// HasExecutable("bash") +func HasExecutable(binName string) bool { + _, err := exec.LookPath(binName) + return err == nil +} + +// SearchPath search executable files in the system $PATH +// +// Usage: +// +// sysutil.SearchPath("go") +func SearchPath(keywords string) []string { + path := os.Getenv("PATH") + ptn := "*" + keywords + "*" + + list := make([]string, 0) + for _, dir := range filepath.SplitList(path) { + if dir == "" { + // Unix shell semantics: path element "" means "." + dir = "." + } + + matches, err := filepath.Glob(filepath.Join(dir, ptn)) + if err == nil && len(matches) > 0 { + list = append(list, matches...) + } + } + + return list +} diff --git a/vendor/github.com/gookit/goutil/sysutil/sysgo.go b/vendor/github.com/gookit/goutil/sysutil/sysgo.go new file mode 100644 index 00000000..4bafcdc3 --- /dev/null +++ b/vendor/github.com/gookit/goutil/sysutil/sysgo.go @@ -0,0 +1,76 @@ +package sysutil + +import ( + "errors" + "regexp" + "runtime" + "strings" +) + +// GoVersion get go runtime version. eg: "1.18.2" +func GoVersion() string { + return runtime.Version()[2:] +} + +// GoInfo define +// +// On os by: +// +// go env GOVERSION GOOS GOARCH +// go version // "go version go1.19 darwin/amd64" +type GoInfo struct { + Version string + GoOS string + Arch string +} + +// match "go version go1.19 darwin/amd64" +var goVerRegex = regexp.MustCompile(`\sgo([\d.]+)\s(\w+)/(\w+)`) + +// ParseGoVersion get info by parse `go version` results. +// +// Examples: +// +// line, err := sysutil.ExecLine("go version") +// if err != nil { +// return err +// } +// +// info, err := sysutil.ParseGoVersion() +// dump.P(info) +func ParseGoVersion(line string) (*GoInfo, error) { + // eg: [" go1.19 darwin/amd64", "1.19", "darwin", "amd64"] + lines := goVerRegex.FindStringSubmatch(line) + if len(lines) != 4 { + return nil, errors.New("returns go info is not full") + } + + info := &GoInfo{} + info.Version = strings.TrimPrefix(lines[1], "go") + info.GoOS = lines[2] + info.Arch = lines[3] + + return info, nil +} + +// OsGoInfo fetch and parse +func OsGoInfo() (*GoInfo, error) { + cmdArgs := []string{"env", "GOVERSION", "GOOS", "GOARCH"} + line, err := ExecCmd("go", cmdArgs) + if err != nil { + return nil, err + } + + lines := strings.Split(strings.TrimSpace(line), "\n") + + if len(lines) != len(cmdArgs)-1 { + return nil, errors.New("returns go info is not full") + } + + info := &GoInfo{} + info.Version = strings.TrimPrefix(lines[0], "go") + info.GoOS = lines[1] + info.Arch = lines[2] + + return info, nil +} diff --git a/vendor/github.com/gookit/goutil/sysutil/sysutil.go b/vendor/github.com/gookit/goutil/sysutil/sysutil.go index 13efbbdb..5be3456e 100644 --- a/vendor/github.com/gookit/goutil/sysutil/sysutil.go +++ b/vendor/github.com/gookit/goutil/sysutil/sysutil.go @@ -1,3 +1,4 @@ +// Package sysutil provide some system util functions. eg: sysenv, exec, user, process package sysutil import ( diff --git a/vendor/github.com/gookit/goutil/sysutil/sysutil_nonwin.go b/vendor/github.com/gookit/goutil/sysutil/sysutil_nonwin.go index b7115f41..d7446ede 100644 --- a/vendor/github.com/gookit/goutil/sysutil/sysutil_nonwin.go +++ b/vendor/github.com/gookit/goutil/sysutil/sysutil_nonwin.go @@ -3,7 +3,36 @@ package sysutil -import "syscall" +import ( + "os/exec" + "runtime" + "syscall" +) + +// IsWin system. linux windows darwin +func IsWin() bool { + return false +} + +// IsWindows system. linux windows darwin +func IsWindows() bool { + return false +} + +// IsMac system +func IsMac() bool { + return runtime.GOOS == "darwin" +} + +// IsDarwin system +func IsDarwin() bool { + return runtime.GOOS == "darwin" +} + +// IsLinux system +func IsLinux() bool { + return runtime.GOOS == "linux" +} // Kill a process by pid func Kill(pid int, signal syscall.Signal) error { @@ -14,3 +43,26 @@ func Kill(pid int, signal syscall.Signal) error { func ProcessExists(pid int) bool { return nil == syscall.Kill(pid, 0) } + +// OpenBrowser Open browser URL +// +// Mac: +// +// open 'https://github.com/inhere' +// +// Linux: +// +// xdg-open URL +// x-www-browser 'https://github.com/inhere' +// +// Windows: +// +// cmd /c start https://github.com/inhere +func OpenBrowser(URL string) error { + bin := "x-www-browser" + if IsDarwin() { + bin = "open" + } + + return exec.Command(bin, URL).Run() +} diff --git a/vendor/github.com/gookit/goutil/sysutil/sysutil_windows.go b/vendor/github.com/gookit/goutil/sysutil/sysutil_windows.go index b26a79bf..b53cdc3a 100644 --- a/vendor/github.com/gookit/goutil/sysutil/sysutil_windows.go +++ b/vendor/github.com/gookit/goutil/sysutil/sysutil_windows.go @@ -5,11 +5,37 @@ package sysutil import ( "errors" + "os/exec" "syscall" "github.com/gookit/goutil/sysutil/process" ) +// IsWin system. linux windows darwin +func IsWin() bool { + return true +} + +// IsWindows system. linux windows darwin +func IsWindows() bool { + return true +} + +// IsMac system +func IsMac() bool { + return false +} + +// IsDarwin system +func IsDarwin() bool { + return false +} + +// IsLinux system +func IsLinux() bool { + return false +} + // Kill a process by pid func Kill(pid int, signal syscall.Signal) error { return errors.New("not support") @@ -19,3 +45,21 @@ func Kill(pid int, signal syscall.Signal) error { func ProcessExists(pid int) bool { return process.Exists(pid) } + +// OpenBrowser Open browser URL +// +// Mac: +// +// open 'https://github.com/inhere' +// +// Linux: +// +// xdg-open URL +// x-www-browser 'https://github.com/inhere' +// +// Windows: +// +// cmd /c start https://github.com/inhere +func OpenBrowser(URL string) error { + return exec.Command("cmd", "/c", "start", URL).Run() +} diff --git a/vendor/github.com/gookit/goutil/sysutil/user.go b/vendor/github.com/gookit/goutil/sysutil/user.go index 33e434b4..4eeb6336 100644 --- a/vendor/github.com/gookit/goutil/sysutil/user.go +++ b/vendor/github.com/gookit/goutil/sysutil/user.go @@ -4,7 +4,7 @@ import ( "os" "os/user" - "github.com/mitchellh/go-homedir" + "github.com/gookit/goutil/internal/comfunc" ) // MustFindUser must find an system user by name @@ -31,12 +31,6 @@ func CurrentUser() *user.User { return u } -// UserHomeDir is alias of os.UserHomeDir, but ignore error -func UserHomeDir() string { - dir, _ := os.UserHomeDir() - return dir -} - // UHomeDir get user home dir path. func UHomeDir() string { // check $HOME/.terminfo @@ -47,35 +41,43 @@ func UHomeDir() string { return u.HomeDir } +// homeDir cache +var homeDir string + +// UserHomeDir is alias of os.UserHomeDir, but ignore error +func UserHomeDir() string { + if homeDir != "" { + return homeDir + } + + homeDir, _ = os.UserHomeDir() + return homeDir +} + // HomeDir get user home dir path. func HomeDir() string { - dir, _ := homedir.Dir() - return dir + return UserHomeDir() } // UserDir will prepend user home dir to subPath func UserDir(subPath string) string { - dir, _ := homedir.Dir() - + dir := UserHomeDir() return dir + "/" + subPath } // UserCacheDir will prepend user `$HOME/.cache` to subPath func UserCacheDir(subPath string) string { - dir, _ := homedir.Dir() - + dir := UserHomeDir() return dir + "/.cache/" + subPath } // UserConfigDir will prepend user `$HOME/.config` to subPath func UserConfigDir(subPath string) string { - dir, _ := homedir.Dir() - + dir := UserHomeDir() return dir + "/.config/" + subPath } // ExpandPath will parse `~` as user home dir path. func ExpandPath(path string) string { - path, _ = homedir.Expand(path) - return path + return comfunc.ExpandPath(path) } diff --git a/vendor/github.com/gookit/goutil/utils.go b/vendor/github.com/gookit/goutil/utils.go deleted file mode 100644 index 142ad92d..00000000 --- a/vendor/github.com/gookit/goutil/utils.go +++ /dev/null @@ -1,24 +0,0 @@ -package goutil - -import ( - "github.com/gookit/goutil/jsonutil" -) - -// Go is a basic promise implementation: it wraps calls a function in a goroutine -// and returns a channel which will later return the function's return value. -func Go(f func() error) error { - ch := make(chan error) - go func() { - ch <- f() - }() - return <-ch -} - -// Filling filling a model from submitted data -// form 提交过来的数据结构体 -// model 定义表模型的数据结构体 -// 相当于是在合并两个结构体(data 必须是 model 的子集) -func Filling(form interface{}, model interface{}) error { - jsonBytes, _ := jsonutil.Encode(form) - return jsonutil.Decode(jsonBytes, model) -} diff --git a/vendor/github.com/gookit/goutil/value.go b/vendor/github.com/gookit/goutil/value.go deleted file mode 100644 index e8a3cf52..00000000 --- a/vendor/github.com/gookit/goutil/value.go +++ /dev/null @@ -1,95 +0,0 @@ -package goutil - -import ( - "github.com/gookit/goutil/mathutil" - "github.com/gookit/goutil/strutil" -) - -// Value data store -type Value struct { - // V value - V interface{} -} - -// Reset value -func (v *Value) Reset() { - v.V = nil -} - -// Val get -func (v Value) Val() interface{} { - return v.V -} - -// Int value -func (v Value) Int() int { - if v.V == nil { - return 0 - } - - return mathutil.MustInt(v.V) -} - -// Int64 value -func (v Value) Int64() int64 { - if v.V == nil { - return 0 - } - - return mathutil.MustInt64(v.V) -} - -// Bool value -func (v Value) Bool() bool { - if v.V == nil { - return false - } - - if bl, ok := v.V.(bool); ok { - return bl - } - - if str, ok := v.V.(string); ok { - return strutil.MustBool(str) - } - return false -} - -// Float64 value -func (v Value) Float64() float64 { - if v.V == nil { - return 0 - } - - return mathutil.MustFloat(v.V) -} - -// String value -func (v Value) String() string { - if v.V == nil { - return "" - } - - if str, ok := v.V.(string); ok { - return str - } - - return strutil.MustString(v.V) -} - -// Strings value -func (v Value) Strings() (ss []string) { - if v.V == nil { - return - } - - if ss, ok := v.V.([]string); ok { - return ss - } - return -} - -// IsEmpty value -func (v Value) IsEmpty() bool { - return v.V == nil -} diff --git a/vendor/github.com/mitchellh/go-homedir/LICENSE b/vendor/github.com/mitchellh/go-homedir/LICENSE deleted file mode 100644 index f9c841a5..00000000 --- a/vendor/github.com/mitchellh/go-homedir/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 Mitchell Hashimoto - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/vendor/github.com/mitchellh/go-homedir/README.md b/vendor/github.com/mitchellh/go-homedir/README.md deleted file mode 100644 index d70706d5..00000000 --- a/vendor/github.com/mitchellh/go-homedir/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# go-homedir - -This is a Go library for detecting the user's home directory without -the use of cgo, so the library can be used in cross-compilation environments. - -Usage is incredibly simple, just call `homedir.Dir()` to get the home directory -for a user, and `homedir.Expand()` to expand the `~` in a path to the home -directory. - -**Why not just use `os/user`?** The built-in `os/user` package requires -cgo on Darwin systems. This means that any Go code that uses that package -cannot cross compile. But 99% of the time the use for `os/user` is just to -retrieve the home directory, which we can do for the current user without -cgo. This library does that, enabling cross-compilation. diff --git a/vendor/github.com/mitchellh/go-homedir/homedir.go b/vendor/github.com/mitchellh/go-homedir/homedir.go deleted file mode 100644 index 25378537..00000000 --- a/vendor/github.com/mitchellh/go-homedir/homedir.go +++ /dev/null @@ -1,167 +0,0 @@ -package homedir - -import ( - "bytes" - "errors" - "os" - "os/exec" - "path/filepath" - "runtime" - "strconv" - "strings" - "sync" -) - -// DisableCache will disable caching of the home directory. Caching is enabled -// by default. -var DisableCache bool - -var homedirCache string -var cacheLock sync.RWMutex - -// Dir returns the home directory for the executing user. -// -// This uses an OS-specific method for discovering the home directory. -// An error is returned if a home directory cannot be detected. -func Dir() (string, error) { - if !DisableCache { - cacheLock.RLock() - cached := homedirCache - cacheLock.RUnlock() - if cached != "" { - return cached, nil - } - } - - cacheLock.Lock() - defer cacheLock.Unlock() - - var result string - var err error - if runtime.GOOS == "windows" { - result, err = dirWindows() - } else { - // Unix-like system, so just assume Unix - result, err = dirUnix() - } - - if err != nil { - return "", err - } - homedirCache = result - return result, nil -} - -// Expand expands the path to include the home directory if the path -// is prefixed with `~`. If it isn't prefixed with `~`, the path is -// returned as-is. -func Expand(path string) (string, error) { - if len(path) == 0 { - return path, nil - } - - if path[0] != '~' { - return path, nil - } - - if len(path) > 1 && path[1] != '/' && path[1] != '\\' { - return "", errors.New("cannot expand user-specific home dir") - } - - dir, err := Dir() - if err != nil { - return "", err - } - - return filepath.Join(dir, path[1:]), nil -} - -// Reset clears the cache, forcing the next call to Dir to re-detect -// the home directory. This generally never has to be called, but can be -// useful in tests if you're modifying the home directory via the HOME -// env var or something. -func Reset() { - cacheLock.Lock() - defer cacheLock.Unlock() - homedirCache = "" -} - -func dirUnix() (string, error) { - homeEnv := "HOME" - if runtime.GOOS == "plan9" { - // On plan9, env vars are lowercase. - homeEnv = "home" - } - - // First prefer the HOME environmental variable - if home := os.Getenv(homeEnv); home != "" { - return home, nil - } - - var stdout bytes.Buffer - - // If that fails, try OS specific commands - if runtime.GOOS == "darwin" { - cmd := exec.Command("sh", "-c", `dscl -q . -read /Users/"$(whoami)" NFSHomeDirectory | sed 's/^[^ ]*: //'`) - cmd.Stdout = &stdout - if err := cmd.Run(); err == nil { - result := strings.TrimSpace(stdout.String()) - if result != "" { - return result, nil - } - } - } else { - cmd := exec.Command("getent", "passwd", strconv.Itoa(os.Getuid())) - cmd.Stdout = &stdout - if err := cmd.Run(); err != nil { - // If the error is ErrNotFound, we ignore it. Otherwise, return it. - if err != exec.ErrNotFound { - return "", err - } - } else { - if passwd := strings.TrimSpace(stdout.String()); passwd != "" { - // username:password:uid:gid:gecos:home:shell - passwdParts := strings.SplitN(passwd, ":", 7) - if len(passwdParts) > 5 { - return passwdParts[5], nil - } - } - } - } - - // If all else fails, try the shell - stdout.Reset() - cmd := exec.Command("sh", "-c", "cd && pwd") - cmd.Stdout = &stdout - if err := cmd.Run(); err != nil { - return "", err - } - - result := strings.TrimSpace(stdout.String()) - if result == "" { - return "", errors.New("blank output when reading home directory") - } - - return result, nil -} - -func dirWindows() (string, error) { - // First prefer the HOME environmental variable - if home := os.Getenv("HOME"); home != "" { - return home, nil - } - - // Prefer standard environment variable USERPROFILE - if home := os.Getenv("USERPROFILE"); home != "" { - return home, nil - } - - drive := os.Getenv("HOMEDRIVE") - path := os.Getenv("HOMEPATH") - home := drive + path - if drive == "" || path == "" { - return "", errors.New("HOMEDRIVE, HOMEPATH, or USERPROFILE are blank") - } - - return home, nil -} diff --git a/vendor/golang.org/x/crypto/AUTHORS b/vendor/golang.org/x/crypto/AUTHORS deleted file mode 100644 index 2b00ddba..00000000 --- a/vendor/golang.org/x/crypto/AUTHORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code refers to The Go Authors for copyright purposes. -# The master list of authors is in the main Go distribution, -# visible at https://tip.golang.org/AUTHORS. diff --git a/vendor/golang.org/x/crypto/CONTRIBUTORS b/vendor/golang.org/x/crypto/CONTRIBUTORS deleted file mode 100644 index 1fbd3e97..00000000 --- a/vendor/golang.org/x/crypto/CONTRIBUTORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code was written by the Go contributors. -# The master list of contributors is in the main Go distribution, -# visible at https://tip.golang.org/CONTRIBUTORS. diff --git a/vendor/golang.org/x/sync/LICENSE b/vendor/golang.org/x/sync/LICENSE new file mode 100644 index 00000000..6a66aea5 --- /dev/null +++ b/vendor/golang.org/x/sync/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/sync/PATENTS b/vendor/golang.org/x/sync/PATENTS new file mode 100644 index 00000000..73309904 --- /dev/null +++ b/vendor/golang.org/x/sync/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/sync/errgroup/errgroup.go b/vendor/golang.org/x/sync/errgroup/errgroup.go new file mode 100644 index 00000000..cbee7a4e --- /dev/null +++ b/vendor/golang.org/x/sync/errgroup/errgroup.go @@ -0,0 +1,132 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package errgroup provides synchronization, error propagation, and Context +// cancelation for groups of goroutines working on subtasks of a common task. +package errgroup + +import ( + "context" + "fmt" + "sync" +) + +type token struct{} + +// A Group is a collection of goroutines working on subtasks that are part of +// the same overall task. +// +// A zero Group is valid, has no limit on the number of active goroutines, +// and does not cancel on error. +type Group struct { + cancel func() + + wg sync.WaitGroup + + sem chan token + + errOnce sync.Once + err error +} + +func (g *Group) done() { + if g.sem != nil { + <-g.sem + } + g.wg.Done() +} + +// WithContext returns a new Group and an associated Context derived from ctx. +// +// The derived Context is canceled the first time a function passed to Go +// returns a non-nil error or the first time Wait returns, whichever occurs +// first. +func WithContext(ctx context.Context) (*Group, context.Context) { + ctx, cancel := context.WithCancel(ctx) + return &Group{cancel: cancel}, ctx +} + +// Wait blocks until all function calls from the Go method have returned, then +// returns the first non-nil error (if any) from them. +func (g *Group) Wait() error { + g.wg.Wait() + if g.cancel != nil { + g.cancel() + } + return g.err +} + +// Go calls the given function in a new goroutine. +// It blocks until the new goroutine can be added without the number of +// active goroutines in the group exceeding the configured limit. +// +// The first call to return a non-nil error cancels the group's context, if the +// group was created by calling WithContext. The error will be returned by Wait. +func (g *Group) Go(f func() error) { + if g.sem != nil { + g.sem <- token{} + } + + g.wg.Add(1) + go func() { + defer g.done() + + if err := f(); err != nil { + g.errOnce.Do(func() { + g.err = err + if g.cancel != nil { + g.cancel() + } + }) + } + }() +} + +// TryGo calls the given function in a new goroutine only if the number of +// active goroutines in the group is currently below the configured limit. +// +// The return value reports whether the goroutine was started. +func (g *Group) TryGo(f func() error) bool { + if g.sem != nil { + select { + case g.sem <- token{}: + // Note: this allows barging iff channels in general allow barging. + default: + return false + } + } + + g.wg.Add(1) + go func() { + defer g.done() + + if err := f(); err != nil { + g.errOnce.Do(func() { + g.err = err + if g.cancel != nil { + g.cancel() + } + }) + } + }() + return true +} + +// SetLimit limits the number of active goroutines in this group to at most n. +// A negative value indicates no limit. +// +// Any subsequent call to the Go method will block until it can add an active +// goroutine without exceeding the configured limit. +// +// The limit must not be modified while any goroutines in the group are active. +func (g *Group) SetLimit(n int) { + if n < 0 { + g.sem = nil + return + } + if len(g.sem) != 0 { + panic(fmt.Errorf("errgroup: modify limit while %v goroutines in the group are still active", len(g.sem))) + } + g.sem = make(chan token, n) +} diff --git a/vendor/golang.org/x/sys/unix/ioctl_linux.go b/vendor/golang.org/x/sys/unix/ioctl_linux.go index 884430b8..0d12c085 100644 --- a/vendor/golang.org/x/sys/unix/ioctl_linux.go +++ b/vendor/golang.org/x/sys/unix/ioctl_linux.go @@ -4,9 +4,7 @@ package unix -import ( - "unsafe" -) +import "unsafe" // IoctlRetInt performs an ioctl operation specified by req on a device // associated with opened file descriptor fd, and returns a non-negative @@ -217,3 +215,19 @@ func IoctlKCMAttach(fd int, info KCMAttach) error { func IoctlKCMUnattach(fd int, info KCMUnattach) error { return ioctlPtr(fd, SIOCKCMUNATTACH, unsafe.Pointer(&info)) } + +// IoctlLoopGetStatus64 gets the status of the loop device associated with the +// file descriptor fd using the LOOP_GET_STATUS64 operation. +func IoctlLoopGetStatus64(fd int) (*LoopInfo64, error) { + var value LoopInfo64 + if err := ioctlPtr(fd, LOOP_GET_STATUS64, unsafe.Pointer(&value)); err != nil { + return nil, err + } + return &value, nil +} + +// IoctlLoopSetStatus64 sets the status of the loop device associated with the +// file descriptor fd using the LOOP_SET_STATUS64 operation. +func IoctlLoopSetStatus64(fd int, value *LoopInfo64) error { + return ioctlPtr(fd, LOOP_SET_STATUS64, unsafe.Pointer(value)) +} diff --git a/vendor/golang.org/x/sys/unix/str.go b/vendor/golang.org/x/sys/unix/str.go deleted file mode 100644 index 8ba89ed8..00000000 --- a/vendor/golang.org/x/sys/unix/str.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris -// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris - -package unix - -func itoa(val int) string { // do it here rather than with fmt to avoid dependency - if val < 0 { - return "-" + uitoa(uint(-val)) - } - return uitoa(uint(val)) -} - -func uitoa(val uint) string { - var buf [32]byte // big enough for int64 - i := len(buf) - 1 - for val >= 10 { - buf[i] = byte(val%10 + '0') - i-- - val /= 10 - } - buf[i] = byte(val + '0') - return string(buf[i:]) -} diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go index ecb0f27f..eecb58d7 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -13,6 +13,7 @@ package unix import ( "encoding/binary" + "strconv" "syscall" "time" "unsafe" @@ -233,7 +234,7 @@ func Futimesat(dirfd int, path string, tv []Timeval) error { func Futimes(fd int, tv []Timeval) (err error) { // Believe it or not, this is the best we can do on Linux // (and is what glibc does). - return Utimes("/proc/self/fd/"+itoa(fd), tv) + return Utimes("/proc/self/fd/"+strconv.Itoa(fd), tv) } const ImplementsGetwd = true diff --git a/vendor/golang.org/x/term/AUTHORS b/vendor/golang.org/x/term/AUTHORS deleted file mode 100644 index 15167cd7..00000000 --- a/vendor/golang.org/x/term/AUTHORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code refers to The Go Authors for copyright purposes. -# The master list of authors is in the main Go distribution, -# visible at http://tip.golang.org/AUTHORS. diff --git a/vendor/golang.org/x/term/CONTRIBUTORS b/vendor/golang.org/x/term/CONTRIBUTORS deleted file mode 100644 index 1c4577e9..00000000 --- a/vendor/golang.org/x/term/CONTRIBUTORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code was written by the Go contributors. -# The master list of contributors is in the main Go distribution, -# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/vendor/golang.org/x/term/term.go b/vendor/golang.org/x/term/term.go index d5927088..1a40d101 100644 --- a/vendor/golang.org/x/term/term.go +++ b/vendor/golang.org/x/term/term.go @@ -7,11 +7,11 @@ // // Putting a terminal into raw mode is the most common requirement: // -// oldState, err := term.MakeRaw(int(os.Stdin.Fd())) -// if err != nil { -// panic(err) -// } -// defer term.Restore(int(os.Stdin.Fd()), oldState) +// oldState, err := term.MakeRaw(int(os.Stdin.Fd())) +// if err != nil { +// panic(err) +// } +// defer term.Restore(int(os.Stdin.Fd()), oldState) // // Note that on non-Unix systems os.Stdin.Fd() may not be 0. package term diff --git a/vendor/golang.org/x/term/terminal.go b/vendor/golang.org/x/term/terminal.go index 535ab825..4b48a589 100644 --- a/vendor/golang.org/x/term/terminal.go +++ b/vendor/golang.org/x/term/terminal.go @@ -935,7 +935,7 @@ func (s *stRingBuffer) Add(a string) { // next most recent, and so on. If such an element doesn't exist then ok is // false. func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) { - if n >= s.size { + if n < 0 || n >= s.size { return "", false } index := s.head - n diff --git a/vendor/modules.txt b/vendor/modules.txt index 7222a845..e5efa50a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -86,25 +86,34 @@ github.com/godbus/dbus/v5/prop # github.com/gomodule/redigo v1.8.8 ## explicit; go 1.16 github.com/gomodule/redigo/redis -# github.com/gookit/color v1.5.0 -## explicit; go 1.13 +# github.com/gookit/color v1.5.2 +## explicit; go 1.16 github.com/gookit/color # github.com/gookit/gcli/v2 v2.3.4 ## explicit; go 1.12 github.com/gookit/gcli/v2 github.com/gookit/gcli/v2/helper github.com/gookit/gcli/v2/progress -# github.com/gookit/goutil v0.5.2 -## explicit; go 1.14 +# github.com/gookit/goutil v0.6.0 +## explicit; go 1.18 github.com/gookit/goutil +github.com/gookit/goutil/arrutil github.com/gookit/goutil/cliutil/cmdline +github.com/gookit/goutil/comdef github.com/gookit/goutil/envutil +github.com/gookit/goutil/errorx github.com/gookit/goutil/fmtutil -github.com/gookit/goutil/jsonutil +github.com/gookit/goutil/fsutil +github.com/gookit/goutil/internal/comfunc +github.com/gookit/goutil/maputil github.com/gookit/goutil/mathutil +github.com/gookit/goutil/reflects +github.com/gookit/goutil/stdio github.com/gookit/goutil/stdutil +github.com/gookit/goutil/structs github.com/gookit/goutil/strutil github.com/gookit/goutil/sysutil +github.com/gookit/goutil/sysutil/cmdr github.com/gookit/goutil/sysutil/process # github.com/gookit/ini/v2 v2.1.0 ## explicit; go 1.15 @@ -161,9 +170,6 @@ github.com/mewkiz/flac/meta ## explicit; go 1.12 github.com/mewkiz/pkg/errutil github.com/mewkiz/pkg/term -# github.com/mitchellh/go-homedir v1.1.0 -## explicit -github.com/mitchellh/go-homedir # github.com/mitchellh/mapstructure v1.5.0 ## explicit; go 1.14 github.com/mitchellh/mapstructure @@ -210,7 +216,7 @@ github.com/tosone/minimp3 # github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 ## explicit; go 1.15 github.com/xo/terminfo -# golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b +# golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 ## explicit; go 1.17 golang.org/x/crypto/ssh/terminal # golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 @@ -239,13 +245,16 @@ golang.org/x/mobile/event/touch golang.org/x/mobile/geom golang.org/x/mobile/gl golang.org/x/mobile/internal/mobileinit -# golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 +# golang.org/x/sync v0.1.0 +## explicit +golang.org/x/sync/errgroup +# golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 ## explicit; go 1.17 golang.org/x/sys/internal/unsafeheader golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows -# golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 +# golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 ## explicit; go 1.17 golang.org/x/term # golang.org/x/text v0.7.0