diff --git a/go.mod b/go.mod index 26f3dc3e9..f6c42fb31 100644 --- a/go.mod +++ b/go.mod @@ -44,7 +44,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/google/go-tika v0.3.0 github.com/google/uuid v1.3.0 - github.com/gookit/config/v2 v2.1.8 + github.com/gookit/config/v2 v2.2.2 github.com/gorilla/mux v1.8.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.13.0 github.com/jellydator/ttlcache/v2 v2.11.1 @@ -92,7 +92,7 @@ require ( golang.org/x/image v0.6.0 golang.org/x/net v0.10.0 golang.org/x/oauth2 v0.8.0 - golang.org/x/sync v0.1.0 + golang.org/x/sync v0.2.0 golang.org/x/term v0.8.0 golang.org/x/text v0.9.0 google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f @@ -108,7 +108,7 @@ require ( require ( contrib.go.opencensus.io/exporter/prometheus v0.4.2 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect - github.com/BurntSushi/toml v1.2.1 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/sprig v2.22.0+incompatible // indirect github.com/Microsoft/go-winio v0.6.0 // indirect @@ -172,7 +172,7 @@ require ( github.com/emirpasic/gods v1.18.1 // indirect github.com/emvi/iso-639-1 v1.0.1 // indirect github.com/evanphx/json-patch/v5 v5.5.0 // indirect - github.com/fatih/color v1.13.0 // indirect + github.com/fatih/color v1.14.1 // indirect github.com/felixge/httpsnoop v1.0.3 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gdexlab/go-render v1.0.1 // indirect @@ -193,10 +193,12 @@ require ( github.com/go-resty/resty/v2 v2.7.0 // indirect github.com/go-sql-driver/mysql v1.6.0 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect + github.com/go-test/deep v1.1.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gobwas/httphead v0.1.0 // indirect github.com/gobwas/pool v0.2.1 // indirect github.com/gobwas/ws v1.0.4 // indirect + github.com/goccy/go-yaml v1.11.0 // indirect github.com/godbus/dbus/v5 v5.0.6 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect @@ -209,8 +211,8 @@ require ( github.com/google/go-cmp v0.5.9 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect - github.com/gookit/color v1.5.2 // indirect - github.com/gookit/goutil v0.6.0 // indirect + github.com/gookit/color v1.5.3 // indirect + github.com/gookit/goutil v0.6.9 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/schema v1.2.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect @@ -227,7 +229,7 @@ require ( github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/iancoleman/strcase v0.2.0 // indirect - github.com/imdario/mergo v0.3.13 // indirect + github.com/imdario/mergo v0.3.15 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect @@ -242,7 +244,7 @@ require ( github.com/longsleep/rndm v1.2.0 // indirect github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect @@ -301,7 +303,7 @@ require ( github.com/xanzy/ssh-agent v0.3.2 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect - github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/yashtewari/glob-intersection v0.1.0 // indirect go.etcd.io/etcd/api/v3 v3.5.7 // indirect @@ -320,6 +322,7 @@ require ( golang.org/x/sys v0.8.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.7.0 // indirect + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect diff --git a/go.sum b/go.sum index c575bd851..04528336e 100644 --- a/go.sum +++ b/go.sum @@ -416,8 +416,8 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= -github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/CiscoM31/godata v1.0.7 h1:y3FHdICAU9j+IkK6E66ezCghaQSamFbYoj/YEHig0kY= github.com/CiscoM31/godata v1.0.7/go.mod h1:ZMiT6JuD3Rm83HEtiTx4JEChsd25YCrxchKGag/sdTc= @@ -454,7 +454,6 @@ github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWX github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= -github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= @@ -477,9 +476,6 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM= -github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= -github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -697,8 +693,9 @@ github.com/exoscale/egoscale v0.46.0/go.mod h1:mpEXBpROAa/2i5GC0r33rfxG+TxSEka11 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= +github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= @@ -827,8 +824,11 @@ github.com/go-openapi/validate v0.21.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUri github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es= github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= @@ -841,8 +841,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= -github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= +github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVruqWQJBtW6+bTBDTniY8yZum5rF3b5jw= github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= @@ -877,6 +877,8 @@ github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm github.com/gobwas/ws v1.0.4 h1:5eXU1CZhpQdq5kXbKb+sECH5Ia5KiO6CYzIzdlVx6Bs= github.com/gobwas/ws v1.0.4/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA= +github.com/goccy/go-yaml v1.11.0 h1:n7Z+zx8S9f9KgzG6KtQKf+kwqXZlLNR2F6018Dgau54= +github.com/goccy/go-yaml v1.11.0/go.mod h1:H+mJrWtjPTJAHvRbV09MCK9xYwODM+wRTVFFTWckfng= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -920,7 +922,6 @@ github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3K github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v0.0.0-20170622202551-6a1fa9404c0a/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -1015,17 +1016,13 @@ github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqE github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= -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/config/v2 v2.1.8 h1:fH8NKzOLlQ2Ud65Czy5Tq+t1I8U7M9BmIPgVFQ4T9UQ= -github.com/gookit/config/v2 v2.1.8/go.mod h1:AP8Obh1AKx1pFkBNE2EghDb1ei2/fG33ULfkzuw4s0c= -github.com/gookit/goutil v0.5.14/go.mod h1:ozPE16eJS9f89aVbVk05ocEJsia3KPrYUqPTs8GvUTw= -github.com/gookit/goutil v0.5.15/go.mod h1:ozPE16eJS9f89aVbVk05ocEJsia3KPrYUqPTs8GvUTw= -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.3 h1:wQPpTWbuo5GuevQnuiZMVRtALfldNuBW0XvRFgF0EEk= -github.com/gookit/ini/v2 v2.1.3/go.mod h1:Mor4+c0wdx5UK660FBLAkmc6Yr2oBHLAUjydLQ+WgYg= -github.com/gookit/properties v0.2.1/go.mod h1:hEmnTl5DLbGKfodoIIS698l8hqHpbhWvIznY/WAyUHc= +github.com/gookit/color v1.5.3 h1:twfIhZs4QLCtimkP7MOxlF3A0U/5cDPseRT9M/+2SCE= +github.com/gookit/color v1.5.3/go.mod h1:NUzwzeehUfl7GIb36pqId+UGmRfQcU/WiiyTTeNjHtE= +github.com/gookit/config/v2 v2.2.2 h1:/iVW3H/5oPdNulrjSI370kf14Hs6D4Gc5E15u0AYxX8= +github.com/gookit/config/v2 v2.2.2/go.mod h1:9wXrsGnOc9nLTr4mU+tuaR0ORzFyZdf3q5DuqsTyodU= +github.com/gookit/goutil v0.6.9 h1:NrbGKt3cnWoqxjZonC5bTr0FrltTCRdzGReGQwnwhUk= +github.com/gookit/goutil v0.6.9/go.mod h1:ZYNl/t+EIMo0rCRe1mwfC7jKyF/q1FdOEOgrXzJpr5o= +github.com/gookit/ini/v2 v2.2.2 h1:3B8abZJrVH1vi/7TU4STuTBxdhiAq1ORSt6NJZCahaI= github.com/gophercloud/gophercloud v0.15.1-0.20210202035223-633d73521055/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4= github.com/gophercloud/gophercloud v0.16.0/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4= github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae/go.mod h1:wx8HMD8oQD0Ryhz6+6ykq75PJ79iPyEqYHfwZ4l7OsA= @@ -1102,7 +1099,6 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/hcl/v2 v2.14.1/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= @@ -1126,8 +1122,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= -github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= +github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= +github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= @@ -1202,12 +1198,12 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA= github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w= github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leonelquinteros/gotext v1.5.3-0.20230317130943-71a59c05b2c1 h1:k56sFOOJ0CYuQtGoRSeAMhP1R692+iNH+S1dC/CEz0w= github.com/leonelquinteros/gotext v1.5.3-0.20230317130943-71a59c05b2c1/go.mod h1:AT4NpQrOmyj1L/+hLja6aR0lk81yYYL4ePnj2kp7d6M= @@ -1257,8 +1253,9 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= @@ -1302,7 +1299,6 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0= -github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/hashstructure v1.1.0 h1:P6P1hdjqAAknpY/M1CGipelZgp+4y9ja9kmUZPXP+H0= @@ -1511,7 +1507,6 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUt github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= @@ -1556,7 +1551,6 @@ github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -1609,9 +1603,6 @@ github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPU github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/vimeo/go-util v1.4.1/go.mod h1:r+yspV//C48HeMXV8nEvtUeNiIiGfVv3bbEHzOgudwE= github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14/go.mod h1:RWc47jtnVuQv6+lY3c768WtXCas/Xi+U5UFc5xULmYg= -github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= -github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= -github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/vultr/govultr/v2 v2.0.0/go.mod h1:2PsEeg+gs3p/Fo5Pw8F9mv+DUBEOlrNZ8GmCTGmhOhs= github.com/wk8/go-ordered-map v1.0.0 h1:BV7z+2PaK8LTSd/mWgY12HyMAo5CEgkHqbkVq2thqr8= github.com/wk8/go-ordered-map v1.0.0/go.mod h1:9ZIbRunKbuvfPKyBP1SIKLcXNlv74YCOZ3t3VTS6gRk= @@ -1631,13 +1622,12 @@ github.com/xhit/go-simple-mail/v2 v2.13.0 h1:OANWU9jHZrVfBkNkvLf8Ww0fexwpQVF/v/5 github.com/xhit/go-simple-mail/v2 v2.13.0/go.mod h1:b7P5ygho6SYE+VIqpxA6QkYfv4teeyG4MKqB3utRu98= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= -github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= -github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yashtewari/glob-intersection v0.1.0 h1:6gJvMYQlTDOL3dMsPF6J0+26vwX9MB8/1q3uAdhmTrg= github.com/yashtewari/glob-intersection v0.1.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok= -github.com/yosuke-furukawa/json5 v0.1.1/go.mod h1:sw49aWDqNdRJ6DYUtIQiaA3xyj2IL9tjeNYmX2ixwcU= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1646,9 +1636,6 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= -github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= -github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= github.com/zenazn/goji v1.0.1/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go-micro.dev/v4 v4.9.0 h1:pd1CpqMT9hA47jSmX8mfdGK865PkMh95Rwj5RdfqPqE= go-micro.dev/v4 v4.9.0/go.mod h1:Ju8HrZ5hQSF+QguZ2QUs9Kbe42MHP1tJa/fpP5g07Cs= @@ -1743,9 +1730,7 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220517005047-85d78b3ac167/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= @@ -1795,7 +1780,6 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1911,8 +1895,9 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/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.0.0-20220929204114-8fcdb60fdcc0/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/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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= @@ -2027,7 +2012,6 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2039,7 +2023,6 @@ golang.org/x/sys v0.8.0/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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -2152,6 +2135,7 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -2432,7 +2416,6 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/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= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= diff --git a/vendor/github.com/BurntSushi/toml/decode.go b/vendor/github.com/BurntSushi/toml/decode.go index 0ca1dc4fe..4d38f3bfc 100644 --- a/vendor/github.com/BurntSushi/toml/decode.go +++ b/vendor/github.com/BurntSushi/toml/decode.go @@ -91,7 +91,7 @@ const ( // UnmarshalText method. See the Unmarshaler example for a demonstration with // email addresses. // -// ### Key mapping +// # Key mapping // // TOML keys can map to either keys in a Go map or field names in a Go struct. // The special `toml` struct tag can be used to map TOML keys to struct fields @@ -248,7 +248,7 @@ func (md *MetaData) unify(data interface{}, rv reflect.Value) error { case reflect.Bool: return md.unifyBool(data, rv) case reflect.Interface: - if rv.NumMethod() > 0 { // Only support empty interfaces are supported. + if rv.NumMethod() > 0 { /// Only empty interfaces are supported. return md.e("unsupported type %s", rv.Type()) } return md.unifyAnything(data, rv) diff --git a/vendor/github.com/BurntSushi/toml/deprecated.go b/vendor/github.com/BurntSushi/toml/deprecated.go index c6af3f239..b9e309717 100644 --- a/vendor/github.com/BurntSushi/toml/deprecated.go +++ b/vendor/github.com/BurntSushi/toml/deprecated.go @@ -5,17 +5,25 @@ import ( "io" ) +// TextMarshaler is an alias for encoding.TextMarshaler. +// // Deprecated: use encoding.TextMarshaler type TextMarshaler encoding.TextMarshaler +// TextUnmarshaler is an alias for encoding.TextUnmarshaler. +// // Deprecated: use encoding.TextUnmarshaler type TextUnmarshaler encoding.TextUnmarshaler +// PrimitiveDecode is an alias for MetaData.PrimitiveDecode(). +// // Deprecated: use MetaData.PrimitiveDecode. func PrimitiveDecode(primValue Primitive, v interface{}) error { md := MetaData{decoded: make(map[string]struct{})} return md.unify(primValue.undecoded, rvalue(v)) } +// DecodeReader is an alias for NewDecoder(r).Decode(v). +// // Deprecated: use NewDecoder(reader).Decode(&value). func DecodeReader(r io.Reader, v interface{}) (MetaData, error) { return NewDecoder(r).Decode(v) } diff --git a/vendor/github.com/BurntSushi/toml/encode.go b/vendor/github.com/BurntSushi/toml/encode.go index 930e1d521..9cd25d757 100644 --- a/vendor/github.com/BurntSushi/toml/encode.go +++ b/vendor/github.com/BurntSushi/toml/encode.go @@ -136,7 +136,8 @@ func NewEncoder(w io.Writer) *Encoder { // document. func (enc *Encoder) Encode(v interface{}) error { rv := eindirect(reflect.ValueOf(v)) - if err := enc.safeEncode(Key([]string{}), rv); err != nil { + err := enc.safeEncode(Key([]string{}), rv) + if err != nil { return err } return enc.w.Flush() @@ -457,6 +458,16 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) { frv := eindirect(rv.Field(i)) + if is32Bit { + // Copy so it works correct on 32bit archs; not clear why this + // is needed. See #314, and https://www.reddit.com/r/golang/comments/pnx8v4 + // This also works fine on 64bit, but 32bit archs are somewhat + // rare and this is a wee bit faster. + copyStart := make([]int, len(start)) + copy(copyStart, start) + start = copyStart + } + // Treat anonymous struct fields with tag names as though they are // not anonymous, like encoding/json does. // @@ -471,17 +482,7 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) { if typeIsTable(tomlTypeOfGo(frv)) { fieldsSub = append(fieldsSub, append(start, f.Index...)) } else { - // Copy so it works correct on 32bit archs; not clear why this - // is needed. See #314, and https://www.reddit.com/r/golang/comments/pnx8v4 - // This also works fine on 64bit, but 32bit archs are somewhat - // rare and this is a wee bit faster. - if is32Bit { - copyStart := make([]int, len(start)) - copy(copyStart, start) - fieldsDirect = append(fieldsDirect, append(copyStart, f.Index...)) - } else { - fieldsDirect = append(fieldsDirect, append(start, f.Index...)) - } + fieldsDirect = append(fieldsDirect, append(start, f.Index...)) } } } @@ -490,24 +491,27 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) { writeFields := func(fields [][]int) { for _, fieldIndex := range fields { fieldType := rt.FieldByIndex(fieldIndex) - fieldVal := eindirect(rv.FieldByIndex(fieldIndex)) - - if isNil(fieldVal) { /// Don't write anything for nil fields. - continue - } + fieldVal := rv.FieldByIndex(fieldIndex) opts := getOptions(fieldType.Tag) if opts.skip { continue } + if opts.omitempty && isEmpty(fieldVal) { + continue + } + + fieldVal = eindirect(fieldVal) + + if isNil(fieldVal) { /// Don't write anything for nil fields. + continue + } + keyName := fieldType.Name if opts.name != "" { keyName = opts.name } - if opts.omitempty && enc.isEmpty(fieldVal) { - continue - } if opts.omitzero && isZero(fieldVal) { continue } @@ -649,7 +653,7 @@ func isZero(rv reflect.Value) bool { return false } -func (enc *Encoder) isEmpty(rv reflect.Value) bool { +func isEmpty(rv reflect.Value) bool { switch rv.Kind() { case reflect.Array, reflect.Slice, reflect.Map, reflect.String: return rv.Len() == 0 @@ -664,13 +668,15 @@ func (enc *Encoder) isEmpty(rv reflect.Value) bool { // type b struct{ s []string } // s := a{field: b{s: []string{"AAA"}}} for i := 0; i < rv.NumField(); i++ { - if !enc.isEmpty(rv.Field(i)) { + if !isEmpty(rv.Field(i)) { return false } } return true case reflect.Bool: return !rv.Bool() + case reflect.Ptr: + return rv.IsNil() } return false } @@ -693,8 +699,11 @@ func (enc *Encoder) newline() { // v v v v vv // key = {k = 1, k2 = 2} func (enc *Encoder) writeKeyValue(key Key, val reflect.Value, inline bool) { + /// Marshaler used on top-level document; call eElement() to just call + /// Marshal{TOML,Text}. if len(key) == 0 { - encPanic(errNoKey) + enc.eElement(val) + return } enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1)) enc.eElement(val) diff --git a/vendor/github.com/BurntSushi/toml/error.go b/vendor/github.com/BurntSushi/toml/error.go index f4f390e64..efd68865b 100644 --- a/vendor/github.com/BurntSushi/toml/error.go +++ b/vendor/github.com/BurntSushi/toml/error.go @@ -84,7 +84,7 @@ func (pe ParseError) Error() string { pe.Position.Line, pe.LastKey, msg) } -// ErrorWithUsage() returns the error with detailed location context. +// ErrorWithPosition returns the error with detailed location context. // // See the documentation on [ParseError]. func (pe ParseError) ErrorWithPosition() string { @@ -124,7 +124,7 @@ func (pe ParseError) ErrorWithPosition() string { return b.String() } -// ErrorWithUsage() returns the error with detailed location context and usage +// ErrorWithUsage returns the error with detailed location context and usage // guidance. // // See the documentation on [ParseError]. diff --git a/vendor/github.com/BurntSushi/toml/lex.go b/vendor/github.com/BurntSushi/toml/lex.go index d4d70871d..3545a6ad6 100644 --- a/vendor/github.com/BurntSushi/toml/lex.go +++ b/vendor/github.com/BurntSushi/toml/lex.go @@ -46,12 +46,13 @@ func (p Position) String() string { } type lexer struct { - input string - start int - pos int - line int - state stateFn - items chan item + input string + start int + pos int + line int + state stateFn + items chan item + tomlNext bool // Allow for backing up up to 4 runes. This is necessary because TOML // contains 3-rune tokens (""" and '''). @@ -87,13 +88,14 @@ func (lx *lexer) nextItem() item { } } -func lex(input string) *lexer { +func lex(input string, tomlNext bool) *lexer { lx := &lexer{ - input: input, - state: lexTop, - items: make(chan item, 10), - stack: make([]stateFn, 0, 10), - line: 1, + input: input, + state: lexTop, + items: make(chan item, 10), + stack: make([]stateFn, 0, 10), + line: 1, + tomlNext: tomlNext, } return lx } @@ -408,7 +410,7 @@ func lexTableNameEnd(lx *lexer) stateFn { // Lexes only one part, e.g. only 'a' inside 'a.b'. func lexBareName(lx *lexer) stateFn { r := lx.next() - if isBareKeyChar(r) { + if isBareKeyChar(r, lx.tomlNext) { return lexBareName } lx.backup() @@ -618,6 +620,9 @@ func lexInlineTableValue(lx *lexer) stateFn { case isWhitespace(r): return lexSkip(lx, lexInlineTableValue) case isNL(r): + if lx.tomlNext { + return lexSkip(lx, lexInlineTableValue) + } return lx.errorPrevLine(errLexInlineTableNL{}) case r == '#': lx.push(lexInlineTableValue) @@ -640,6 +645,9 @@ func lexInlineTableValueEnd(lx *lexer) stateFn { case isWhitespace(r): return lexSkip(lx, lexInlineTableValueEnd) case isNL(r): + if lx.tomlNext { + return lexSkip(lx, lexInlineTableValueEnd) + } return lx.errorPrevLine(errLexInlineTableNL{}) case r == '#': lx.push(lexInlineTableValueEnd) @@ -648,6 +656,9 @@ func lexInlineTableValueEnd(lx *lexer) stateFn { lx.ignore() lx.skip(isWhitespace) if lx.peek() == '}' { + if lx.tomlNext { + return lexInlineTableValueEnd + } return lx.errorf("trailing comma not allowed in inline tables") } return lexInlineTableValue @@ -770,8 +781,8 @@ func lexRawString(lx *lexer) stateFn { } } -// lexMultilineRawString consumes a raw string. Nothing can be escaped in such -// a string. It assumes that the beginning ''' has already been consumed and +// lexMultilineRawString consumes a raw string. Nothing can be escaped in such a +// string. It assumes that the beginning triple-' has already been consumed and // ignored. func lexMultilineRawString(lx *lexer) stateFn { r := lx.next() @@ -828,6 +839,11 @@ func lexMultilineStringEscape(lx *lexer) stateFn { func lexStringEscape(lx *lexer) stateFn { r := lx.next() switch r { + case 'e': + if !lx.tomlNext { + return lx.error(errLexEscape{r}) + } + fallthrough case 'b': fallthrough case 't': @@ -846,6 +862,11 @@ func lexStringEscape(lx *lexer) stateFn { fallthrough case '\\': return lx.pop() + case 'x': + if !lx.tomlNext { + return lx.error(errLexEscape{r}) + } + return lexHexEscape case 'u': return lexShortUnicodeEscape case 'U': @@ -854,6 +875,19 @@ func lexStringEscape(lx *lexer) stateFn { return lx.error(errLexEscape{r}) } +func lexHexEscape(lx *lexer) stateFn { + var r rune + for i := 0; i < 2; i++ { + r = lx.next() + if !isHexadecimal(r) { + return lx.errorf( + `expected two hexadecimal digits after '\x', but got %q instead`, + lx.current()) + } + } + return lx.pop() +} + func lexShortUnicodeEscape(lx *lexer) stateFn { var r rune for i := 0; i < 4; i++ { @@ -1225,7 +1259,23 @@ func isOctal(r rune) bool { return r >= '0' && r <= '7' } func isHexadecimal(r rune) bool { return (r >= '0' && r <= '9') || (r >= 'a' && r <= 'f') || (r >= 'A' && r <= 'F') } -func isBareKeyChar(r rune) bool { + +func isBareKeyChar(r rune, tomlNext bool) bool { + if tomlNext { + return (r >= 'A' && r <= 'Z') || + (r >= 'a' && r <= 'z') || + (r >= '0' && r <= '9') || + r == '_' || r == '-' || + r == 0xb2 || r == 0xb3 || r == 0xb9 || (r >= 0xbc && r <= 0xbe) || + (r >= 0xc0 && r <= 0xd6) || (r >= 0xd8 && r <= 0xf6) || (r >= 0xf8 && r <= 0x037d) || + (r >= 0x037f && r <= 0x1fff) || + (r >= 0x200c && r <= 0x200d) || (r >= 0x203f && r <= 0x2040) || + (r >= 0x2070 && r <= 0x218f) || (r >= 0x2460 && r <= 0x24ff) || + (r >= 0x2c00 && r <= 0x2fef) || (r >= 0x3001 && r <= 0xd7ff) || + (r >= 0xf900 && r <= 0xfdcf) || (r >= 0xfdf0 && r <= 0xfffd) || + (r >= 0x10000 && r <= 0xeffff) + } + return (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || diff --git a/vendor/github.com/BurntSushi/toml/meta.go b/vendor/github.com/BurntSushi/toml/meta.go index 71847a041..2e78b24e9 100644 --- a/vendor/github.com/BurntSushi/toml/meta.go +++ b/vendor/github.com/BurntSushi/toml/meta.go @@ -106,7 +106,7 @@ func (k Key) maybeQuoted(i int) string { return `""` } for _, c := range k[i] { - if !isBareKeyChar(c) { + if !isBareKeyChar(c, false) { return `"` + dblQuotedReplacer.Replace(k[i]) + `"` } } diff --git a/vendor/github.com/BurntSushi/toml/parse.go b/vendor/github.com/BurntSushi/toml/parse.go index d2542d6f9..9c1915369 100644 --- a/vendor/github.com/BurntSushi/toml/parse.go +++ b/vendor/github.com/BurntSushi/toml/parse.go @@ -2,6 +2,7 @@ package toml import ( "fmt" + "os" "strconv" "strings" "time" @@ -15,6 +16,7 @@ type parser struct { context Key // Full key for the current hash in scope. currentKey string // Base key name for everything except hashes. pos Position // Current position in the TOML file. + tomlNext bool ordered []Key // List of keys in the order that they appear in the TOML data. @@ -29,6 +31,8 @@ type keyInfo struct { } func parse(data string) (p *parser, err error) { + _, tomlNext := os.LookupEnv("BURNTSUSHI_TOML_110") + defer func() { if r := recover(); r != nil { if pErr, ok := r.(ParseError); ok { @@ -41,9 +45,12 @@ func parse(data string) (p *parser, err error) { }() // Read over BOM; do this here as the lexer calls utf8.DecodeRuneInString() - // which mangles stuff. - if strings.HasPrefix(data, "\xff\xfe") || strings.HasPrefix(data, "\xfe\xff") { + // which mangles stuff. UTF-16 BOM isn't strictly valid, but some tools add + // it anyway. + if strings.HasPrefix(data, "\xff\xfe") || strings.HasPrefix(data, "\xfe\xff") { // UTF-16 data = data[2:] + } else if strings.HasPrefix(data, "\xef\xbb\xbf") { // UTF-8 + data = data[3:] } // Examine first few bytes for NULL bytes; this probably means it's a UTF-16 @@ -65,9 +72,10 @@ func parse(data string) (p *parser, err error) { p = &parser{ keyInfo: make(map[string]keyInfo), mapping: make(map[string]interface{}), - lx: lex(data), + lx: lex(data, tomlNext), ordered: make([]Key, 0), implicits: make(map[string]struct{}), + tomlNext: tomlNext, } for { item := p.next() @@ -194,12 +202,12 @@ func (p *parser) topLevel(item item) { for i := range context { p.addImplicitContext(append(p.context, context[i:i+1]...)) } + p.ordered = append(p.ordered, p.context.add(p.currentKey)) /// Set value. vItem := p.next() val, typ := p.value(vItem, false) p.set(p.currentKey, val, typ, vItem.pos) - p.ordered = append(p.ordered, p.context.add(p.currentKey)) /// Remove the context we added (preserving any context from [tbl] lines). p.context = outerContext @@ -236,7 +244,7 @@ func (p *parser) value(it item, parentIsArray bool) (interface{}, tomlType) { case itemString: return p.replaceEscapes(it, it.val), p.typeOfPrimitive(it) case itemMultilineString: - return p.replaceEscapes(it, stripFirstNewline(p.stripEscapedNewlines(it.val))), p.typeOfPrimitive(it) + return p.replaceEscapes(it, p.stripEscapedNewlines(stripFirstNewline(it.val))), p.typeOfPrimitive(it) case itemRawString: return it.val, p.typeOfPrimitive(it) case itemRawMultilineString: @@ -331,11 +339,17 @@ func (p *parser) valueFloat(it item) (interface{}, tomlType) { var dtTypes = []struct { fmt string zone *time.Location + next bool }{ - {time.RFC3339Nano, time.Local}, - {"2006-01-02T15:04:05.999999999", internal.LocalDatetime}, - {"2006-01-02", internal.LocalDate}, - {"15:04:05.999999999", internal.LocalTime}, + {time.RFC3339Nano, time.Local, false}, + {"2006-01-02T15:04:05.999999999", internal.LocalDatetime, false}, + {"2006-01-02", internal.LocalDate, false}, + {"15:04:05.999999999", internal.LocalTime, false}, + + // tomlNext + {"2006-01-02T15:04Z07:00", time.Local, true}, + {"2006-01-02T15:04", internal.LocalDatetime, true}, + {"15:04", internal.LocalTime, true}, } func (p *parser) valueDatetime(it item) (interface{}, tomlType) { @@ -346,6 +360,9 @@ func (p *parser) valueDatetime(it item) (interface{}, tomlType) { err error ) for _, dt := range dtTypes { + if dt.next && !p.tomlNext { + continue + } t, err = time.ParseInLocation(dt.fmt, it.val, dt.zone) if err == nil { ok = true @@ -384,6 +401,7 @@ func (p *parser) valueArray(it item) (interface{}, tomlType) { // // Not entirely sure how to best store this; could use "key[0]", // "key[1]" notation, or maybe store it on the Array type? + _ = types } return array, tomlArray } @@ -426,11 +444,11 @@ func (p *parser) valueInlineTable(it item, parentIsArray bool) (interface{}, tom for i := range context { p.addImplicitContext(append(p.context, context[i:i+1]...)) } + p.ordered = append(p.ordered, p.context.add(p.currentKey)) /// Set the value. val, typ := p.value(p.next(), false) p.set(p.currentKey, val, typ, it.pos) - p.ordered = append(p.ordered, p.context.add(p.currentKey)) hash[p.currentKey] = val /// Restore context. @@ -551,7 +569,6 @@ func (p *parser) addContext(key Key, array bool) { func (p *parser) set(key string, val interface{}, typ tomlType, pos Position) { p.setValue(key, val) p.setType(key, typ, pos) - } // setValue sets the given key to the given value in the current context. @@ -632,14 +649,11 @@ func (p *parser) setType(key string, typ tomlType, pos Position) { // Implicit keys need to be created when tables are implied in "a.b.c.d = 1" and // "[a.b.c]" (the "a", "b", and "c" hashes are never created explicitly). -func (p *parser) addImplicit(key Key) { p.implicits[key.String()] = struct{}{} } -func (p *parser) removeImplicit(key Key) { delete(p.implicits, key.String()) } -func (p *parser) isImplicit(key Key) bool { _, ok := p.implicits[key.String()]; return ok } -func (p *parser) isArray(key Key) bool { return p.keyInfo[key.String()].tomlType == tomlArray } -func (p *parser) addImplicitContext(key Key) { - p.addImplicit(key) - p.addContext(key, false) -} +func (p *parser) addImplicit(key Key) { p.implicits[key.String()] = struct{}{} } +func (p *parser) removeImplicit(key Key) { delete(p.implicits, key.String()) } +func (p *parser) isImplicit(key Key) bool { _, ok := p.implicits[key.String()]; return ok } +func (p *parser) isArray(key Key) bool { return p.keyInfo[key.String()].tomlType == tomlArray } +func (p *parser) addImplicitContext(key Key) { p.addImplicit(key); p.addContext(key, false) } // current returns the full key name of the current context. func (p *parser) current() string { @@ -662,49 +676,54 @@ func stripFirstNewline(s string) string { return s } -// Remove newlines inside triple-quoted strings if a line ends with "\". +// stripEscapedNewlines removes whitespace after line-ending backslashes in +// multiline strings. +// +// A line-ending backslash is an unescaped \ followed only by whitespace until +// the next newline. After a line-ending backslash, all whitespace is removed +// until the next non-whitespace character. func (p *parser) stripEscapedNewlines(s string) string { - split := strings.Split(s, "\n") - if len(split) < 1 { - return s - } + var b strings.Builder + var i int + for { + ix := strings.Index(s[i:], `\`) + if ix < 0 { + b.WriteString(s) + return b.String() + } + i += ix - escNL := false // Keep track of the last non-blank line was escaped. - for i, line := range split { - line = strings.TrimRight(line, " \t\r") - - if len(line) == 0 || line[len(line)-1] != '\\' { - split[i] = strings.TrimRight(split[i], "\r") - if !escNL && i != len(split)-1 { - split[i] += "\n" + if len(s) > i+1 && s[i+1] == '\\' { + // Escaped backslash. + i += 2 + continue + } + // Scan until the next non-whitespace. + j := i + 1 + whitespaceLoop: + for ; j < len(s); j++ { + switch s[j] { + case ' ', '\t', '\r', '\n': + default: + break whitespaceLoop } + } + if j == i+1 { + // Not a whitespace escape. + i++ continue } - - escBS := true - for j := len(line) - 1; j >= 0 && line[j] == '\\'; j-- { - escBS = !escBS - } - if escNL { - line = strings.TrimLeft(line, " \t\r") - } - escNL = !escBS - - if escBS { - split[i] += "\n" + if !strings.Contains(s[i:j], "\n") { + // This is not a line-ending backslash. + // (It's a bad escape sequence, but we can let + // replaceEscapes catch it.) + i++ continue } - - if i == len(split)-1 { - p.panicf("invalid escape: '\\ '") - } - - split[i] = line[:len(line)-1] // Remove \ - if len(split)-1 > i { - split[i+1] = strings.TrimLeft(split[i+1], " \t\r") - } + b.WriteString(s[:i]) + s = s[j:] + i = 0 } - return strings.Join(split, "") } func (p *parser) replaceEscapes(it item, str string) string { @@ -743,12 +762,23 @@ func (p *parser) replaceEscapes(it item, str string) string { case 'r': replaced = append(replaced, rune(0x000D)) r += 1 + case 'e': + if p.tomlNext { + replaced = append(replaced, rune(0x001B)) + r += 1 + } case '"': replaced = append(replaced, rune(0x0022)) r += 1 case '\\': replaced = append(replaced, rune(0x005C)) r += 1 + case 'x': + if p.tomlNext { + escaped := p.asciiEscapeToUnicode(it, s[r+1:r+3]) + replaced = append(replaced, escaped) + r += 3 + } case 'u': // At this point, we know we have a Unicode escape of the form // `uXXXX` at [r, r+5). (Because the lexer guarantees this diff --git a/vendor/github.com/fatih/color/README.md b/vendor/github.com/fatih/color/README.md index 5152bf59b..be82827ca 100644 --- a/vendor/github.com/fatih/color/README.md +++ b/vendor/github.com/fatih/color/README.md @@ -7,7 +7,6 @@ suits you. ![Color](https://user-images.githubusercontent.com/438920/96832689-03b3e000-13f4-11eb-9803-46f4c4de3406.jpg) - ## Install ```bash @@ -124,17 +123,17 @@ fmt.Println("All text will now be bold magenta.") ``` ### Disable/Enable color - + There might be a case where you want to explicitly disable/enable color output. the `go-isatty` package will automatically disable color output for non-tty output streams (for example if the output were piped directly to `less`). The `color` package also disables color output if the [`NO_COLOR`](https://no-color.org) environment -variable is set (regardless of its value). +variable is set to a non-empty string. -`Color` has support to disable/enable colors programatically both globally and +`Color` has support to disable/enable colors programmatically both globally and for single color definitions. For example suppose you have a CLI app and a -`--no-color` bool flag. You can easily disable the color output with: +`-no-color` bool flag. You can easily disable the color output with: ```go var flagNoColor = flag.Bool("no-color", false, "Disable color output") @@ -167,11 +166,10 @@ To output color in GitHub Actions (or other CI systems that support ANSI colors) * Save/Return previous values * Evaluate fmt.Formatter interface - ## Credits - * [Fatih Arslan](https://github.com/fatih) - * Windows support via @mattn: [colorable](https://github.com/mattn/go-colorable) +* [Fatih Arslan](https://github.com/fatih) +* Windows support via @mattn: [colorable](https://github.com/mattn/go-colorable) ## License diff --git a/vendor/github.com/fatih/color/color.go b/vendor/github.com/fatih/color/color.go index 98a60f3c8..889f9e77b 100644 --- a/vendor/github.com/fatih/color/color.go +++ b/vendor/github.com/fatih/color/color.go @@ -19,10 +19,10 @@ var ( // set (regardless of its value). This is a global option and affects all // colors. For more control over each color block use the methods // DisableColor() individually. - NoColor = noColorExists() || os.Getenv("TERM") == "dumb" || + NoColor = noColorIsSet() || os.Getenv("TERM") == "dumb" || (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) - // Output defines the standard output of the print functions. By default + // Output defines the standard output of the print functions. By default, // os.Stdout is used. Output = colorable.NewColorableStdout() @@ -35,10 +35,9 @@ var ( colorsCacheMu sync.Mutex // protects colorsCache ) -// noColorExists returns true if the environment variable NO_COLOR exists. -func noColorExists() bool { - _, exists := os.LookupEnv("NO_COLOR") - return exists +// noColorIsSet returns true if the environment variable NO_COLOR is set to a non-empty string. +func noColorIsSet() bool { + return os.Getenv("NO_COLOR") != "" } // Color defines a custom color object which is defined by SGR parameters. @@ -120,7 +119,7 @@ func New(value ...Attribute) *Color { params: make([]Attribute, 0), } - if noColorExists() { + if noColorIsSet() { c.noColor = boolPtr(true) } @@ -152,7 +151,7 @@ func (c *Color) Set() *Color { return c } - fmt.Fprintf(Output, c.format()) + fmt.Fprint(Output, c.format()) return c } @@ -164,16 +163,21 @@ func (c *Color) unset() { Unset() } -func (c *Color) setWriter(w io.Writer) *Color { +// SetWriter is used to set the SGR sequence with the given io.Writer. This is +// a low-level function, and users should use the higher-level functions, such +// as color.Fprint, color.Print, etc. +func (c *Color) SetWriter(w io.Writer) *Color { if c.isNoColorSet() { return c } - fmt.Fprintf(w, c.format()) + fmt.Fprint(w, c.format()) return c } -func (c *Color) unsetWriter(w io.Writer) { +// UnsetWriter resets all escape attributes and clears the output with the give +// io.Writer. Usually should be called after SetWriter(). +func (c *Color) UnsetWriter(w io.Writer) { if c.isNoColorSet() { return } @@ -192,20 +196,14 @@ func (c *Color) Add(value ...Attribute) *Color { return c } -func (c *Color) prepend(value Attribute) { - c.params = append(c.params, 0) - copy(c.params[1:], c.params[0:]) - c.params[0] = value -} - // Fprint formats using the default formats for its operands and writes to w. // Spaces are added between operands when neither is a string. // It returns the number of bytes written and any write error encountered. // On Windows, users should wrap w with colorable.NewColorable() if w is of // type *os.File. func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) { - c.setWriter(w) - defer c.unsetWriter(w) + c.SetWriter(w) + defer c.UnsetWriter(w) return fmt.Fprint(w, a...) } @@ -227,8 +225,8 @@ func (c *Color) Print(a ...interface{}) (n int, err error) { // On Windows, users should wrap w with colorable.NewColorable() if w is of // type *os.File. func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { - c.setWriter(w) - defer c.unsetWriter(w) + c.SetWriter(w) + defer c.UnsetWriter(w) return fmt.Fprintf(w, format, a...) } @@ -248,8 +246,8 @@ func (c *Color) Printf(format string, a ...interface{}) (n int, err error) { // On Windows, users should wrap w with colorable.NewColorable() if w is of // type *os.File. func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { - c.setWriter(w) - defer c.unsetWriter(w) + c.SetWriter(w) + defer c.UnsetWriter(w) return fmt.Fprintln(w, a...) } @@ -396,7 +394,7 @@ func (c *Color) DisableColor() { } // EnableColor enables the color output. Use it in conjunction with -// DisableColor(). Otherwise this method has no side effects. +// DisableColor(). Otherwise, this method has no side effects. func (c *Color) EnableColor() { c.noColor = boolPtr(false) } diff --git a/vendor/github.com/fatih/color/doc.go b/vendor/github.com/fatih/color/doc.go index 04541de78..9491ad541 100644 --- a/vendor/github.com/fatih/color/doc.go +++ b/vendor/github.com/fatih/color/doc.go @@ -5,106 +5,105 @@ that suits you. Use simple and default helper functions with predefined foreground colors: - color.Cyan("Prints text in cyan.") + color.Cyan("Prints text in cyan.") - // a newline will be appended automatically - color.Blue("Prints %s in blue.", "text") + // a newline will be appended automatically + color.Blue("Prints %s in blue.", "text") - // More default foreground colors.. - color.Red("We have red") - color.Yellow("Yellow color too!") - color.Magenta("And many others ..") + // More default foreground colors.. + color.Red("We have red") + color.Yellow("Yellow color too!") + color.Magenta("And many others ..") - // Hi-intensity colors - color.HiGreen("Bright green color.") - color.HiBlack("Bright black means gray..") - color.HiWhite("Shiny white color!") + // Hi-intensity colors + color.HiGreen("Bright green color.") + color.HiBlack("Bright black means gray..") + color.HiWhite("Shiny white color!") -However there are times where custom color mixes are required. Below are some +However, there are times when custom color mixes are required. Below are some examples to create custom color objects and use the print functions of each separate color object. - // Create a new color object - c := color.New(color.FgCyan).Add(color.Underline) - c.Println("Prints cyan text with an underline.") + // Create a new color object + c := color.New(color.FgCyan).Add(color.Underline) + c.Println("Prints cyan text with an underline.") - // Or just add them to New() - d := color.New(color.FgCyan, color.Bold) - d.Printf("This prints bold cyan %s\n", "too!.") + // Or just add them to New() + d := color.New(color.FgCyan, color.Bold) + d.Printf("This prints bold cyan %s\n", "too!.") - // Mix up foreground and background colors, create new mixes! - red := color.New(color.FgRed) + // Mix up foreground and background colors, create new mixes! + red := color.New(color.FgRed) - boldRed := red.Add(color.Bold) - boldRed.Println("This will print text in bold red.") + boldRed := red.Add(color.Bold) + boldRed.Println("This will print text in bold red.") - whiteBackground := red.Add(color.BgWhite) - whiteBackground.Println("Red text with White background.") + whiteBackground := red.Add(color.BgWhite) + whiteBackground.Println("Red text with White background.") - // Use your own io.Writer output - color.New(color.FgBlue).Fprintln(myWriter, "blue color!") + // Use your own io.Writer output + color.New(color.FgBlue).Fprintln(myWriter, "blue color!") - blue := color.New(color.FgBlue) - blue.Fprint(myWriter, "This will print text in blue.") + blue := color.New(color.FgBlue) + blue.Fprint(myWriter, "This will print text in blue.") You can create PrintXxx functions to simplify even more: - // Create a custom print function for convenient - red := color.New(color.FgRed).PrintfFunc() - red("warning") - red("error: %s", err) + // Create a custom print function for convenient + red := color.New(color.FgRed).PrintfFunc() + red("warning") + red("error: %s", err) - // Mix up multiple attributes - notice := color.New(color.Bold, color.FgGreen).PrintlnFunc() - notice("don't forget this...") + // Mix up multiple attributes + notice := color.New(color.Bold, color.FgGreen).PrintlnFunc() + notice("don't forget this...") You can also FprintXxx functions to pass your own io.Writer: - blue := color.New(FgBlue).FprintfFunc() - blue(myWriter, "important notice: %s", stars) - - // Mix up with multiple attributes - success := color.New(color.Bold, color.FgGreen).FprintlnFunc() - success(myWriter, don't forget this...") + blue := color.New(FgBlue).FprintfFunc() + blue(myWriter, "important notice: %s", stars) + // Mix up with multiple attributes + success := color.New(color.Bold, color.FgGreen).FprintlnFunc() + success(myWriter, don't forget this...") Or create SprintXxx functions to mix strings with other non-colorized strings: - yellow := New(FgYellow).SprintFunc() - red := New(FgRed).SprintFunc() + yellow := New(FgYellow).SprintFunc() + red := New(FgRed).SprintFunc() - fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error")) + fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error")) - info := New(FgWhite, BgGreen).SprintFunc() - fmt.Printf("this %s rocks!\n", info("package")) + info := New(FgWhite, BgGreen).SprintFunc() + fmt.Printf("this %s rocks!\n", info("package")) Windows support is enabled by default. All Print functions work as intended. -However only for color.SprintXXX functions, user should use fmt.FprintXXX and +However, only for color.SprintXXX functions, user should use fmt.FprintXXX and set the output to color.Output: - fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS")) + fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS")) - info := New(FgWhite, BgGreen).SprintFunc() - fmt.Fprintf(color.Output, "this %s rocks!\n", info("package")) + info := New(FgWhite, BgGreen).SprintFunc() + fmt.Fprintf(color.Output, "this %s rocks!\n", info("package")) Using with existing code is possible. Just use the Set() method to set the standard output to the given parameters. That way a rewrite of an existing code is not required. - // Use handy standard colors. - color.Set(color.FgYellow) + // Use handy standard colors. + color.Set(color.FgYellow) - fmt.Println("Existing text will be now in Yellow") - fmt.Printf("This one %s\n", "too") + fmt.Println("Existing text will be now in Yellow") + fmt.Printf("This one %s\n", "too") - color.Unset() // don't forget to unset + color.Unset() // don't forget to unset - // You can mix up parameters - color.Set(color.FgMagenta, color.Bold) - defer color.Unset() // use it in your function + // You can mix up parameters + color.Set(color.FgMagenta, color.Bold) + defer color.Unset() // use it in your function - fmt.Println("All text will be now bold magenta.") + fmt.Println("All text will be now bold magenta.") There might be a case where you want to disable color output (for example to pipe the standard output of your app to somewhere else). `Color` has support to @@ -112,24 +111,24 @@ disable colors both globally and for single color definition. For example suppose you have a CLI app and a `--no-color` bool flag. You can easily disable the color output with: - var flagNoColor = flag.Bool("no-color", false, "Disable color output") + var flagNoColor = flag.Bool("no-color", false, "Disable color output") - if *flagNoColor { - color.NoColor = true // disables colorized output - } + if *flagNoColor { + color.NoColor = true // disables colorized output + } You can also disable the color by setting the NO_COLOR environment variable to any value. It also has support for single color definitions (local). You can disable/enable color output on the fly: - c := color.New(color.FgCyan) - c.Println("Prints cyan text") + c := color.New(color.FgCyan) + c.Println("Prints cyan text") - c.DisableColor() - c.Println("This is printed without any color") + c.DisableColor() + c.Println("This is printed without any color") - c.EnableColor() - c.Println("This prints again cyan...") + c.EnableColor() + c.Println("This prints again cyan...") */ package color diff --git a/vendor/github.com/goccy/go-yaml/.codecov.yml b/vendor/github.com/goccy/go-yaml/.codecov.yml new file mode 100644 index 000000000..8364eea0b --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/.codecov.yml @@ -0,0 +1,31 @@ +codecov: + require_ci_to_pass: yes + +coverage: + precision: 2 + round: down + range: "70...100" + + status: + project: + default: + target: 75% + threshold: 2% + patch: off + changes: no + +parsers: + gcov: + branch_detection: + conditional: yes + loop: yes + method: no + macro: no + +comment: + layout: "header,diff" + behavior: default + require_changes: no + +ignore: + - ast diff --git a/vendor/github.com/goccy/go-yaml/CHANGELOG.md b/vendor/github.com/goccy/go-yaml/CHANGELOG.md new file mode 100644 index 000000000..2887563f3 --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/CHANGELOG.md @@ -0,0 +1,163 @@ +# 1.10.1 - 2023-03-28 + +### Features + +- Quote YAML 1.1 bools at encoding time for compatibility with other legacy parsers +- Add support of 32-bit architecture + +### Fix bugs + +- Don't trim all space characters in block style sequence +- Support strings starting with `@` + +# 1.10.0 - 2023-03-01 + +### Fix bugs + +Reversible conversion of comments was not working in various cases, which has been corrected. +**Breaking Change** exists in the comment map interface. However, if you are dealing with CommentMap directly, there is no problem. + + +# 1.9.8 - 2022-12-19 + +### Fix feature + +- Append new line at the end of file ( #329 ) + +### Fix bugs + +- Fix custom marshaler ( #333, #334 ) +- Fix behavior when struct fields conflicted( #335 ) +- Fix position calculation for literal, folded and raw folded strings ( #330 ) + +# 1.9.7 - 2022-12-03 + +### Fix bugs + +- Fix handling of quoted map key ( #328 ) +- Fix resusing process of scanning context ( #322 ) + +## v1.9.6 - 2022-10-26 + +### New Features + +- Introduce MapKeyNode interface to limit node types for map key ( #312 ) + +### Fix bugs + +- Quote strings with special characters in flow mode ( #270 ) +- typeError implements PrettyPrinter interface ( #280 ) +- Fix incorrect const type ( #284 ) +- Fix large literals type inference on 32 bits ( #293 ) +- Fix UTF-8 characters ( #294 ) +- Fix decoding of unknown aliases ( #317 ) +- Fix stream encoder for insert a separator between each encoded document ( #318 ) + +### Update + +- Update golang.org/x/sys ( #289 ) +- Update Go version in CI ( #295 ) +- Add test cases for missing keys to struct literals ( #300 ) + +## v1.9.5 - 2022-01-12 + +### New Features + +* Add UseSingleQuote option ( #265 ) + +### Fix bugs + +* Preserve defaults while decoding nested structs ( #260 ) +* Fix minor typo in decodeInit error ( #264 ) +* Handle empty sequence entries ( #275 ) +* Fix encoding of sequence with multiline string ( #276 ) +* Fix encoding of BytesMarshaler type ( #277 ) +* Fix indentState logic for multi-line value ( #278 ) + +## v1.9.4 - 2021-10-12 + +### Fix bugs + +* Keep prev/next reference between tokens containing comments when filtering comment tokens ( #257 ) +* Supports escaping reserved keywords in PathBuilder ( #258 ) + +## v1.9.3 - 2021-09-07 + +### New Features + +* Support encoding and decoding `time.Duration` fields ( #246 ) +* Allow reserved characters for key name in YAMLPath ( #251 ) +* Support getting YAMLPath from ast.Node ( #252 ) +* Support CommentToMap option ( #253 ) + +### Fix bugs + +* Fix encoding nested sequences with `yaml.IndentSequence` ( #241 ) +* Fix error reporting on inline structs in strict mode ( #244, #245 ) +* Fix encoding of large floats ( #247 ) + +### Improve workflow + +* Migrate CI from CircleCI to GitHub Action ( #249 ) +* Add workflow for ycat ( #250 ) + +## v1.9.2 - 2021-07-26 + +### Support WithComment option ( #238 ) + +`yaml.WithComment` is a option for encoding with comment. +The position where you want to add a comment is represented by YAMLPath, and it is the key of `yaml.CommentMap`. +Also, you can select `Head` comment or `Line` comment as the comment type. + +## v1.9.1 - 2021-07-20 + +### Fix DecodeFromNode ( #237 ) + +- Fix YAML handling where anchor exists + +## v1.9.0 - 2021-07-19 + +### New features + +- Support encoding of comment node ( #233 ) +- Support `yaml.NodeToValue(ast.Node, interface{}, ...DecodeOption) error` ( #236 ) + - Can convert a AST node to a value directly + +### Fix decoder for comment + +- Fix parsing of literal with comment ( #234 ) + +### Rename API ( #235 ) + +- Rename `MarshalWithContext` to `MarshalContext` +- Rename `UnmarshalWithContext` to `UnmarshalContext` + +## v1.8.10 - 2021-07-02 + +### Fixed bugs + +- Fix searching anchor by alias name ( #212 ) +- Fixing Issue 186, scanner should account for newline characters when processing multi-line text. Without this source annotations line/column number (for this and all subsequent tokens) is inconsistent with plain text editors. e.g. https://github.com/goccy/go-yaml/issues/186. This addresses the issue specifically for single and double quote text only. ( #210 ) +- Add error for unterminated flow mapping node ( #213 ) +- Handle missing required field validation ( #221 ) +- Nicely format unexpected node type errors ( #229 ) +- Support to encode map which has defined type key ( #231 ) + +### New features + +- Support sequence indentation by EncodeOption ( #232 ) + +## v1.8.9 - 2021-03-01 + +### Fixed bugs + +- Fix origin buffer for DocumentHeader and DocumentEnd and Directive +- Fix origin buffer for anchor value +- Fix syntax error about map value +- Fix parsing MergeKey ('<<') characters +- Fix encoding of float value +- Fix incorrect column annotation when single or double quotes are used + +### New features + +- Support to encode/decode of ast.Node directly diff --git a/vendor/github.com/goccy/go-yaml/LICENSE b/vendor/github.com/goccy/go-yaml/LICENSE new file mode 100644 index 000000000..04485ce65 --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Masaaki Goshima + +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/goccy/go-yaml/Makefile b/vendor/github.com/goccy/go-yaml/Makefile new file mode 100644 index 000000000..1b1d92397 --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/Makefile @@ -0,0 +1,19 @@ +.PHONY: test +test: + go test -v -race ./... + +.PHONY: simple-test +simple-test: + go test -v ./... + +.PHONY: cover +cover: + go test -coverprofile=cover.out ./... + +.PHONY: cover-html +cover-html: cover + go tool cover -html=cover.out + +.PHONY: ycat/build +ycat/build: + go build -o ycat ./cmd/ycat diff --git a/vendor/github.com/goccy/go-yaml/README.md b/vendor/github.com/goccy/go-yaml/README.md new file mode 100644 index 000000000..945234917 --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/README.md @@ -0,0 +1,370 @@ +# YAML support for the Go language + +[![PkgGoDev](https://pkg.go.dev/badge/github.com/goccy/go-yaml)](https://pkg.go.dev/github.com/goccy/go-yaml) +![Go](https://github.com/goccy/go-yaml/workflows/Go/badge.svg) +[![codecov](https://codecov.io/gh/goccy/go-yaml/branch/master/graph/badge.svg)](https://codecov.io/gh/goccy/go-yaml) +[![Go Report Card](https://goreportcard.com/badge/github.com/goccy/go-yaml)](https://goreportcard.com/report/github.com/goccy/go-yaml) + + + +# Why a new library? + +As of this writing, there already exists a de facto standard library for YAML processing for Go: [https://github.com/go-yaml/yaml](https://github.com/go-yaml/yaml). However we feel that some features are lacking, namely: + +- Pretty format for error notifications +- Direct manipulation of YAML abstract syntax tree +- Support for `Anchor` and `Alias` when marshaling +- Allow referencing elements declared in another file via anchors + +# Features + +- Pretty format for error notifications +- Supports `Scanner` or `Lexer` or `Parser` as public API +- Supports `Anchor` and `Alias` to Marshaler +- Allow referencing elements declared in another file via anchors +- Extract value or AST by YAMLPath ( YAMLPath is like a JSONPath ) + +# Installation + +```sh +go get -u github.com/goccy/go-yaml +``` + +# Synopsis + +## 1. Simple Encode/Decode + +Has an interface like `go-yaml/yaml` using `reflect` + +```go +var v struct { + A int + B string +} +v.A = 1 +v.B = "hello" +bytes, err := yaml.Marshal(v) +if err != nil { + //... +} +fmt.Println(string(bytes)) // "a: 1\nb: hello\n" +``` + +```go + yml := ` +%YAML 1.2 +--- +a: 1 +b: c +` +var v struct { + A int + B string +} +if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + //... +} +``` + +To control marshal/unmarshal behavior, you can use the `yaml` tag. + +```go + yml := `--- +foo: 1 +bar: c +` +var v struct { + A int `yaml:"foo"` + B string `yaml:"bar"` +} +if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + //... +} +``` + +For convenience, we also accept the `json` tag. Note that not all options from +the `json` tag will have significance when parsing YAML documents. If both +tags exist, `yaml` tag will take precedence. + +```go + yml := `--- +foo: 1 +bar: c +` +var v struct { + A int `json:"foo"` + B string `json:"bar"` +} +if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + //... +} +``` + +For custom marshal/unmarshaling, implement either `Bytes` or `Interface` variant of marshaler/unmarshaler. The difference is that while `BytesMarshaler`/`BytesUnmarshaler` behaves like [`encoding/json`](https://pkg.go.dev/encoding/json) and `InterfaceMarshaler`/`InterfaceUnmarshaler` behaves like [`gopkg.in/yaml.v2`](https://pkg.go.dev/gopkg.in/yaml.v2). + +Semantically both are the same, but they differ in performance. Because indentation matters in YAML, you cannot simply accept a valid YAML fragment from a Marshaler, and expect it to work when it is attached to the parent container's serialized form. Therefore when we receive use the `BytesMarshaler`, which returns `[]byte`, we must decode it once to figure out how to make it work in the given context. If you use the `InterfaceMarshaler`, we can skip the decoding. + +If you are repeatedly marshaling complex objects, the latter is always better +performance wise. But if you are, for example, just providing a choice between +a config file format that is read only once, the former is probably easier to +code. + +## 2. Reference elements declared in another file + +`testdata` directory contains `anchor.yml` file: + +```shell +├── testdata +   └── anchor.yml +``` + +And `anchor.yml` is defined as follows: + +```yaml +a: &a + b: 1 + c: hello +``` + +Then, if `yaml.ReferenceDirs("testdata")` option is passed to `yaml.Decoder`, + `Decoder` tries to find the anchor definition from YAML files the under `testdata` directory. + +```go +buf := bytes.NewBufferString("a: *a\n") +dec := yaml.NewDecoder(buf, yaml.ReferenceDirs("testdata")) +var v struct { + A struct { + B int + C string + } +} +if err := dec.Decode(&v); err != nil { + //... +} +fmt.Printf("%+v\n", v) // {A:{B:1 C:hello}} +``` + +## 3. Encode with `Anchor` and `Alias` + +### 3.1. Explicitly declared `Anchor` name and `Alias` name + +If you want to use `anchor` or `alias`, you can define it as a struct tag. + +```go +type T struct { + A int + B string +} +var v struct { + C *T `yaml:"c,anchor=x"` + D *T `yaml:"d,alias=x"` +} +v.C = &T{A: 1, B: "hello"} +v.D = v.C +bytes, err := yaml.Marshal(v) +if err != nil { + panic(err) +} +fmt.Println(string(bytes)) +/* +c: &x + a: 1 + b: hello +d: *x +*/ +``` + +### 3.2. Implicitly declared `Anchor` and `Alias` names + +If you do not explicitly declare the anchor name, the default behavior is to +use the equivalent of `strings.ToLower($FieldName)` as the name of the anchor. + +If you do not explicitly declare the alias name AND the value is a pointer +to another element, we look up the anchor name by finding out which anchor +field the value is assigned to by looking up its pointer address. + +```go +type T struct { + I int + S string +} +var v struct { + A *T `yaml:"a,anchor"` + B *T `yaml:"b,anchor"` + C *T `yaml:"c,alias"` + D *T `yaml:"d,alias"` +} +v.A = &T{I: 1, S: "hello"} +v.B = &T{I: 2, S: "world"} +v.C = v.A // C has same pointer address to A +v.D = v.B // D has same pointer address to B +bytes, err := yaml.Marshal(v) +if err != nil { + //... +} +fmt.Println(string(bytes)) +/* +a: &a + i: 1 + s: hello +b: &b + i: 2 + s: world +c: *a +d: *b +*/ +``` + +### 3.3 MergeKey and Alias + +Merge key and alias ( `<<: *alias` ) can be used by embedding a structure with the `inline,alias` tag. + +```go +type Person struct { + *Person `yaml:",omitempty,inline,alias"` // embed Person type for default value + Name string `yaml:",omitempty"` + Age int `yaml:",omitempty"` +} +defaultPerson := &Person{ + Name: "John Smith", + Age: 20, +} +people := []*Person{ + { + Person: defaultPerson, // assign default value + Name: "Ken", // override Name property + Age: 10, // override Age property + }, + { + Person: defaultPerson, // assign default value only + }, +} +var doc struct { + Default *Person `yaml:"default,anchor"` + People []*Person `yaml:"people"` +} +doc.Default = defaultPerson +doc.People = people +bytes, err := yaml.Marshal(doc) +if err != nil { + //... +} +fmt.Println(string(bytes)) +/* +default: &default + name: John Smith + age: 20 +people: +- <<: *default + name: Ken + age: 10 +- <<: *default +*/ +``` + +## 4. Pretty Formatted Errors + +Error values produced during parsing have two extra features over regular +error values. + +First, by default, they contain extra information on the location of the error +from the source YAML document, to make it easier to find the error location. + +Second, the error messages can optionally be colorized. + +If you would like to control exactly how the output looks like, consider +using `yaml.FormatError`, which accepts two boolean values to +control turning these features on or off. + + + +## 5. Use YAMLPath + +```go +yml := ` +store: + book: + - author: john + price: 10 + - author: ken + price: 12 + bicycle: + color: red + price: 19.95 +` +path, err := yaml.PathString("$.store.book[*].author") +if err != nil { + //... +} +var authors []string +if err := path.Read(strings.NewReader(yml), &authors); err != nil { + //... +} +fmt.Println(authors) +// [john ken] +``` + +### 5.1 Print customized error with YAML source code + +```go +package main + +import ( + "fmt" + + "github.com/goccy/go-yaml" +) + +func main() { + yml := ` +a: 1 +b: "hello" +` + var v struct { + A int + B string + } + if err := yaml.Unmarshal([]byte(yml), &v); err != nil { + panic(err) + } + if v.A != 2 { + // output error with YAML source + path, err := yaml.PathString("$.a") + if err != nil { + panic(err) + } + source, err := path.AnnotateSource([]byte(yml), true) + if err != nil { + panic(err) + } + fmt.Printf("a value expected 2 but actual %d:\n%s\n", v.A, string(source)) + } +} +``` + +output result is the following: + + + + +# Tools + +## ycat + +print yaml file with color + +ycat + +### Installation + +```sh +go install github.com/goccy/go-yaml/cmd/ycat@latest +``` + +# Looking for Sponsors + +I'm looking for sponsors this library. This library is being developed as a personal project in my spare time. If you want a quick response or problem resolution when using this library in your project, please register as a [sponsor](https://github.com/sponsors/goccy). I will cooperate as much as possible. Of course, this library is developed as an MIT license, so you can use it freely for free. + +# License + +MIT diff --git a/vendor/github.com/goccy/go-yaml/ast/ast.go b/vendor/github.com/goccy/go-yaml/ast/ast.go new file mode 100644 index 000000000..f535a2465 --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/ast/ast.go @@ -0,0 +1,2117 @@ +package ast + +import ( + "fmt" + "io" + "math" + "strconv" + "strings" + + "github.com/goccy/go-yaml/token" + "golang.org/x/xerrors" +) + +var ( + ErrInvalidTokenType = xerrors.New("invalid token type") + ErrInvalidAnchorName = xerrors.New("invalid anchor name") + ErrInvalidAliasName = xerrors.New("invalid alias name") +) + +// NodeType type identifier of node +type NodeType int + +const ( + // UnknownNodeType type identifier for default + UnknownNodeType NodeType = iota + // DocumentType type identifier for document node + DocumentType + // NullType type identifier for null node + NullType + // BoolType type identifier for boolean node + BoolType + // IntegerType type identifier for integer node + IntegerType + // FloatType type identifier for float node + FloatType + // InfinityType type identifier for infinity node + InfinityType + // NanType type identifier for nan node + NanType + // StringType type identifier for string node + StringType + // MergeKeyType type identifier for merge key node + MergeKeyType + // LiteralType type identifier for literal node + LiteralType + // MappingType type identifier for mapping node + MappingType + // MappingKeyType type identifier for mapping key node + MappingKeyType + // MappingValueType type identifier for mapping value node + MappingValueType + // SequenceType type identifier for sequence node + SequenceType + // AnchorType type identifier for anchor node + AnchorType + // AliasType type identifier for alias node + AliasType + // DirectiveType type identifier for directive node + DirectiveType + // TagType type identifier for tag node + TagType + // CommentType type identifier for comment node + CommentType + // CommentGroupType type identifier for comment group node + CommentGroupType +) + +// String node type identifier to text +func (t NodeType) String() string { + switch t { + case UnknownNodeType: + return "UnknownNode" + case DocumentType: + return "Document" + case NullType: + return "Null" + case BoolType: + return "Bool" + case IntegerType: + return "Integer" + case FloatType: + return "Float" + case InfinityType: + return "Infinity" + case NanType: + return "Nan" + case StringType: + return "String" + case MergeKeyType: + return "MergeKey" + case LiteralType: + return "Literal" + case MappingType: + return "Mapping" + case MappingKeyType: + return "MappingKey" + case MappingValueType: + return "MappingValue" + case SequenceType: + return "Sequence" + case AnchorType: + return "Anchor" + case AliasType: + return "Alias" + case DirectiveType: + return "Directive" + case TagType: + return "Tag" + case CommentType: + return "Comment" + case CommentGroupType: + return "CommentGroup" + } + return "" +} + +// String node type identifier to YAML Structure name +// based on https://yaml.org/spec/1.2/spec.html +func (t NodeType) YAMLName() string { + switch t { + case UnknownNodeType: + return "unknown" + case DocumentType: + return "document" + case NullType: + return "null" + case BoolType: + return "boolean" + case IntegerType: + return "int" + case FloatType: + return "float" + case InfinityType: + return "inf" + case NanType: + return "nan" + case StringType: + return "string" + case MergeKeyType: + return "merge key" + case LiteralType: + return "scalar" + case MappingType: + return "mapping" + case MappingKeyType: + return "key" + case MappingValueType: + return "value" + case SequenceType: + return "sequence" + case AnchorType: + return "anchor" + case AliasType: + return "alias" + case DirectiveType: + return "directive" + case TagType: + return "tag" + case CommentType: + return "comment" + case CommentGroupType: + return "comment" + } + return "" +} + +// Node type of node +type Node interface { + io.Reader + // String node to text + String() string + // GetToken returns token instance + GetToken() *token.Token + // Type returns type of node + Type() NodeType + // AddColumn add column number to child nodes recursively + AddColumn(int) + // SetComment set comment token to node + SetComment(*CommentGroupNode) error + // Comment returns comment token instance + GetComment() *CommentGroupNode + // GetPath returns YAMLPath for the current node + GetPath() string + // SetPath set YAMLPath for the current node + SetPath(string) + // MarshalYAML + MarshalYAML() ([]byte, error) + // already read length + readLen() int + // append read length + addReadLen(int) + // clean read length + clearLen() +} + +// MapKeyNode type for map key node +type MapKeyNode interface { + Node + // String node to text without comment + stringWithoutComment() string +} + +// ScalarNode type for scalar node +type ScalarNode interface { + MapKeyNode + GetValue() interface{} +} + +type BaseNode struct { + Path string + Comment *CommentGroupNode + read int +} + +func addCommentString(base string, node *CommentGroupNode) string { + return fmt.Sprintf("%s %s", base, node.String()) +} + +func (n *BaseNode) readLen() int { + return n.read +} + +func (n *BaseNode) clearLen() { + n.read = 0 +} + +func (n *BaseNode) addReadLen(len int) { + n.read += len +} + +// GetPath returns YAMLPath for the current node. +func (n *BaseNode) GetPath() string { + if n == nil { + return "" + } + return n.Path +} + +// SetPath set YAMLPath for the current node. +func (n *BaseNode) SetPath(path string) { + if n == nil { + return + } + n.Path = path +} + +// GetComment returns comment token instance +func (n *BaseNode) GetComment() *CommentGroupNode { + return n.Comment +} + +// SetComment set comment token +func (n *BaseNode) SetComment(node *CommentGroupNode) error { + n.Comment = node + return nil +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +func readNode(p []byte, node Node) (int, error) { + s := node.String() + readLen := node.readLen() + remain := len(s) - readLen + if remain == 0 { + node.clearLen() + return 0, io.EOF + } + size := min(remain, len(p)) + for idx, b := range []byte(s[readLen : readLen+size]) { + p[idx] = byte(b) + } + node.addReadLen(size) + return size, nil +} + +// Null create node for null value +func Null(tk *token.Token) *NullNode { + return &NullNode{ + BaseNode: &BaseNode{}, + Token: tk, + } +} + +// Bool create node for boolean value +func Bool(tk *token.Token) *BoolNode { + b, _ := strconv.ParseBool(tk.Value) + return &BoolNode{ + BaseNode: &BaseNode{}, + Token: tk, + Value: b, + } +} + +// Integer create node for integer value +func Integer(tk *token.Token) *IntegerNode { + value := removeUnderScoreFromNumber(tk.Value) + switch tk.Type { + case token.BinaryIntegerType: + // skip two characters because binary token starts with '0b' + skipCharacterNum := 2 + negativePrefix := "" + if value[0] == '-' { + skipCharacterNum++ + negativePrefix = "-" + } + if len(negativePrefix) > 0 { + i, _ := strconv.ParseInt(negativePrefix+value[skipCharacterNum:], 2, 64) + return &IntegerNode{ + BaseNode: &BaseNode{}, + Token: tk, + Value: i, + } + } + i, _ := strconv.ParseUint(negativePrefix+value[skipCharacterNum:], 2, 64) + return &IntegerNode{ + BaseNode: &BaseNode{}, + Token: tk, + Value: i, + } + case token.OctetIntegerType: + // octet token starts with '0o' or '-0o' or '0' or '-0' + skipCharacterNum := 1 + negativePrefix := "" + if value[0] == '-' { + skipCharacterNum++ + if len(value) > 2 && value[2] == 'o' { + skipCharacterNum++ + } + negativePrefix = "-" + } else { + if value[1] == 'o' { + skipCharacterNum++ + } + } + if len(negativePrefix) > 0 { + i, _ := strconv.ParseInt(negativePrefix+value[skipCharacterNum:], 8, 64) + return &IntegerNode{ + BaseNode: &BaseNode{}, + Token: tk, + Value: i, + } + } + i, _ := strconv.ParseUint(value[skipCharacterNum:], 8, 64) + return &IntegerNode{ + BaseNode: &BaseNode{}, + Token: tk, + Value: i, + } + case token.HexIntegerType: + // hex token starts with '0x' or '-0x' + skipCharacterNum := 2 + negativePrefix := "" + if value[0] == '-' { + skipCharacterNum++ + negativePrefix = "-" + } + if len(negativePrefix) > 0 { + i, _ := strconv.ParseInt(negativePrefix+value[skipCharacterNum:], 16, 64) + return &IntegerNode{ + BaseNode: &BaseNode{}, + Token: tk, + Value: i, + } + } + i, _ := strconv.ParseUint(value[skipCharacterNum:], 16, 64) + return &IntegerNode{ + BaseNode: &BaseNode{}, + Token: tk, + Value: i, + } + } + if value[0] == '-' || value[0] == '+' { + i, _ := strconv.ParseInt(value, 10, 64) + return &IntegerNode{ + BaseNode: &BaseNode{}, + Token: tk, + Value: i, + } + } + i, _ := strconv.ParseUint(value, 10, 64) + return &IntegerNode{ + BaseNode: &BaseNode{}, + Token: tk, + Value: i, + } +} + +// Float create node for float value +func Float(tk *token.Token) *FloatNode { + f, _ := strconv.ParseFloat(removeUnderScoreFromNumber(tk.Value), 64) + return &FloatNode{ + BaseNode: &BaseNode{}, + Token: tk, + Value: f, + } +} + +// Infinity create node for .inf or -.inf value +func Infinity(tk *token.Token) *InfinityNode { + node := &InfinityNode{ + BaseNode: &BaseNode{}, + Token: tk, + } + switch tk.Value { + case ".inf", ".Inf", ".INF": + node.Value = math.Inf(0) + case "-.inf", "-.Inf", "-.INF": + node.Value = math.Inf(-1) + } + return node +} + +// Nan create node for .nan value +func Nan(tk *token.Token) *NanNode { + return &NanNode{ + BaseNode: &BaseNode{}, + Token: tk, + } +} + +// String create node for string value +func String(tk *token.Token) *StringNode { + return &StringNode{ + BaseNode: &BaseNode{}, + Token: tk, + Value: tk.Value, + } +} + +// Comment create node for comment +func Comment(tk *token.Token) *CommentNode { + return &CommentNode{ + BaseNode: &BaseNode{}, + Token: tk, + } +} + +func CommentGroup(comments []*token.Token) *CommentGroupNode { + nodes := []*CommentNode{} + for _, comment := range comments { + nodes = append(nodes, Comment(comment)) + } + return &CommentGroupNode{ + BaseNode: &BaseNode{}, + Comments: nodes, + } +} + +// MergeKey create node for merge key ( << ) +func MergeKey(tk *token.Token) *MergeKeyNode { + return &MergeKeyNode{ + BaseNode: &BaseNode{}, + Token: tk, + } +} + +// Mapping create node for map +func Mapping(tk *token.Token, isFlowStyle bool, values ...*MappingValueNode) *MappingNode { + node := &MappingNode{ + BaseNode: &BaseNode{}, + Start: tk, + IsFlowStyle: isFlowStyle, + Values: []*MappingValueNode{}, + } + node.Values = append(node.Values, values...) + return node +} + +// MappingValue create node for mapping value +func MappingValue(tk *token.Token, key MapKeyNode, value Node) *MappingValueNode { + return &MappingValueNode{ + BaseNode: &BaseNode{}, + Start: tk, + Key: key, + Value: value, + } +} + +// MappingKey create node for map key ( '?' ). +func MappingKey(tk *token.Token) *MappingKeyNode { + return &MappingKeyNode{ + BaseNode: &BaseNode{}, + Start: tk, + } +} + +// Sequence create node for sequence +func Sequence(tk *token.Token, isFlowStyle bool) *SequenceNode { + return &SequenceNode{ + BaseNode: &BaseNode{}, + Start: tk, + IsFlowStyle: isFlowStyle, + Values: []Node{}, + } +} + +func Anchor(tk *token.Token) *AnchorNode { + return &AnchorNode{ + BaseNode: &BaseNode{}, + Start: tk, + } +} + +func Alias(tk *token.Token) *AliasNode { + return &AliasNode{ + BaseNode: &BaseNode{}, + Start: tk, + } +} + +func Document(tk *token.Token, body Node) *DocumentNode { + return &DocumentNode{ + BaseNode: &BaseNode{}, + Start: tk, + Body: body, + } +} + +func Directive(tk *token.Token) *DirectiveNode { + return &DirectiveNode{ + BaseNode: &BaseNode{}, + Start: tk, + } +} + +func Literal(tk *token.Token) *LiteralNode { + return &LiteralNode{ + BaseNode: &BaseNode{}, + Start: tk, + } +} + +func Tag(tk *token.Token) *TagNode { + return &TagNode{ + BaseNode: &BaseNode{}, + Start: tk, + } +} + +// File contains all documents in YAML file +type File struct { + Name string + Docs []*DocumentNode +} + +// Read implements (io.Reader).Read +func (f *File) Read(p []byte) (int, error) { + for _, doc := range f.Docs { + n, err := doc.Read(p) + if err == io.EOF { + continue + } + return n, nil + } + return 0, io.EOF +} + +// String all documents to text +func (f *File) String() string { + docs := []string{} + for _, doc := range f.Docs { + docs = append(docs, doc.String()) + } + if len(docs) > 0 { + return strings.Join(docs, "\n") + "\n" + } else { + return "" + } +} + +// DocumentNode type of Document +type DocumentNode struct { + *BaseNode + Start *token.Token // position of DocumentHeader ( `---` ) + End *token.Token // position of DocumentEnd ( `...` ) + Body Node +} + +// Read implements (io.Reader).Read +func (d *DocumentNode) Read(p []byte) (int, error) { + return readNode(p, d) +} + +// Type returns DocumentNodeType +func (d *DocumentNode) Type() NodeType { return DocumentType } + +// GetToken returns token instance +func (d *DocumentNode) GetToken() *token.Token { + return d.Body.GetToken() +} + +// AddColumn add column number to child nodes recursively +func (d *DocumentNode) AddColumn(col int) { + if d.Body != nil { + d.Body.AddColumn(col) + } +} + +// String document to text +func (d *DocumentNode) String() string { + doc := []string{} + if d.Start != nil { + doc = append(doc, d.Start.Value) + } + doc = append(doc, d.Body.String()) + if d.End != nil { + doc = append(doc, d.End.Value) + } + return strings.Join(doc, "\n") +} + +// MarshalYAML encodes to a YAML text +func (d *DocumentNode) MarshalYAML() ([]byte, error) { + return []byte(d.String()), nil +} + +func removeUnderScoreFromNumber(num string) string { + return strings.ReplaceAll(num, "_", "") +} + +// NullNode type of null node +type NullNode struct { + *BaseNode + Token *token.Token +} + +// Read implements (io.Reader).Read +func (n *NullNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns NullType +func (n *NullNode) Type() NodeType { return NullType } + +// GetToken returns token instance +func (n *NullNode) GetToken() *token.Token { + return n.Token +} + +// AddColumn add column number to child nodes recursively +func (n *NullNode) AddColumn(col int) { + n.Token.AddColumn(col) +} + +// GetValue returns nil value +func (n *NullNode) GetValue() interface{} { + return nil +} + +// String returns `null` text +func (n *NullNode) String() string { + if n.Comment != nil { + return addCommentString("null", n.Comment) + } + return n.stringWithoutComment() +} + +func (n *NullNode) stringWithoutComment() string { + return "null" +} + +// MarshalYAML encodes to a YAML text +func (n *NullNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// IntegerNode type of integer node +type IntegerNode struct { + *BaseNode + Token *token.Token + Value interface{} // int64 or uint64 value +} + +// Read implements (io.Reader).Read +func (n *IntegerNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns IntegerType +func (n *IntegerNode) Type() NodeType { return IntegerType } + +// GetToken returns token instance +func (n *IntegerNode) GetToken() *token.Token { + return n.Token +} + +// AddColumn add column number to child nodes recursively +func (n *IntegerNode) AddColumn(col int) { + n.Token.AddColumn(col) +} + +// GetValue returns int64 value +func (n *IntegerNode) GetValue() interface{} { + return n.Value +} + +// String int64 to text +func (n *IntegerNode) String() string { + if n.Comment != nil { + return addCommentString(n.Token.Value, n.Comment) + } + return n.stringWithoutComment() +} + +func (n *IntegerNode) stringWithoutComment() string { + return n.Token.Value +} + +// MarshalYAML encodes to a YAML text +func (n *IntegerNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// FloatNode type of float node +type FloatNode struct { + *BaseNode + Token *token.Token + Precision int + Value float64 +} + +// Read implements (io.Reader).Read +func (n *FloatNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns FloatType +func (n *FloatNode) Type() NodeType { return FloatType } + +// GetToken returns token instance +func (n *FloatNode) GetToken() *token.Token { + return n.Token +} + +// AddColumn add column number to child nodes recursively +func (n *FloatNode) AddColumn(col int) { + n.Token.AddColumn(col) +} + +// GetValue returns float64 value +func (n *FloatNode) GetValue() interface{} { + return n.Value +} + +// String float64 to text +func (n *FloatNode) String() string { + if n.Comment != nil { + return addCommentString(n.Token.Value, n.Comment) + } + return n.stringWithoutComment() +} + +func (n *FloatNode) stringWithoutComment() string { + return n.Token.Value +} + +// MarshalYAML encodes to a YAML text +func (n *FloatNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// StringNode type of string node +type StringNode struct { + *BaseNode + Token *token.Token + Value string +} + +// Read implements (io.Reader).Read +func (n *StringNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns StringType +func (n *StringNode) Type() NodeType { return StringType } + +// GetToken returns token instance +func (n *StringNode) GetToken() *token.Token { + return n.Token +} + +// AddColumn add column number to child nodes recursively +func (n *StringNode) AddColumn(col int) { + n.Token.AddColumn(col) +} + +// GetValue returns string value +func (n *StringNode) GetValue() interface{} { + return n.Value +} + +// escapeSingleQuote escapes s to a single quoted scalar. +// https://yaml.org/spec/1.2.2/#732-single-quoted-style +func escapeSingleQuote(s string) string { + var sb strings.Builder + growLen := len(s) + // s includes also one ' from the doubled pair + 2 + // opening and closing ' + strings.Count(s, "'") // ' added by ReplaceAll + sb.Grow(growLen) + sb.WriteString("'") + sb.WriteString(strings.ReplaceAll(s, "'", "''")) + sb.WriteString("'") + return sb.String() +} + +// String string value to text with quote or literal header if required +func (n *StringNode) String() string { + switch n.Token.Type { + case token.SingleQuoteType: + quoted := escapeSingleQuote(n.Value) + if n.Comment != nil { + return addCommentString(quoted, n.Comment) + } + return quoted + case token.DoubleQuoteType: + quoted := strconv.Quote(n.Value) + if n.Comment != nil { + return addCommentString(quoted, n.Comment) + } + return quoted + } + + lbc := token.DetectLineBreakCharacter(n.Value) + if strings.Contains(n.Value, lbc) { + // This block assumes that the line breaks in this inside scalar content and the Outside scalar content are the same. + // It works mostly, but inconsistencies occur if line break characters are mixed. + header := token.LiteralBlockHeader(n.Value) + space := strings.Repeat(" ", n.Token.Position.Column-1) + values := []string{} + for _, v := range strings.Split(n.Value, lbc) { + values = append(values, fmt.Sprintf("%s %s", space, v)) + } + block := strings.TrimSuffix(strings.TrimSuffix(strings.Join(values, lbc), fmt.Sprintf("%s %s", lbc, space)), fmt.Sprintf(" %s", space)) + return fmt.Sprintf("%s%s%s", header, lbc, block) + } else if len(n.Value) > 0 && (n.Value[0] == '{' || n.Value[0] == '[') { + return fmt.Sprintf(`'%s'`, n.Value) + } + if n.Comment != nil { + return addCommentString(n.Value, n.Comment) + } + return n.Value +} + +func (n *StringNode) stringWithoutComment() string { + switch n.Token.Type { + case token.SingleQuoteType: + quoted := fmt.Sprintf(`'%s'`, n.Value) + return quoted + case token.DoubleQuoteType: + quoted := strconv.Quote(n.Value) + return quoted + } + + lbc := token.DetectLineBreakCharacter(n.Value) + if strings.Contains(n.Value, lbc) { + // This block assumes that the line breaks in this inside scalar content and the Outside scalar content are the same. + // It works mostly, but inconsistencies occur if line break characters are mixed. + header := token.LiteralBlockHeader(n.Value) + space := strings.Repeat(" ", n.Token.Position.Column-1) + values := []string{} + for _, v := range strings.Split(n.Value, lbc) { + values = append(values, fmt.Sprintf("%s %s", space, v)) + } + block := strings.TrimSuffix(strings.TrimSuffix(strings.Join(values, lbc), fmt.Sprintf("%s %s", lbc, space)), fmt.Sprintf(" %s", space)) + return fmt.Sprintf("%s%s%s", header, lbc, block) + } else if len(n.Value) > 0 && (n.Value[0] == '{' || n.Value[0] == '[') { + return fmt.Sprintf(`'%s'`, n.Value) + } + return n.Value +} + +// MarshalYAML encodes to a YAML text +func (n *StringNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// LiteralNode type of literal node +type LiteralNode struct { + *BaseNode + Start *token.Token + Value *StringNode +} + +// Read implements (io.Reader).Read +func (n *LiteralNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns LiteralType +func (n *LiteralNode) Type() NodeType { return LiteralType } + +// GetToken returns token instance +func (n *LiteralNode) GetToken() *token.Token { + return n.Start +} + +// AddColumn add column number to child nodes recursively +func (n *LiteralNode) AddColumn(col int) { + n.Start.AddColumn(col) + if n.Value != nil { + n.Value.AddColumn(col) + } +} + +// GetValue returns string value +func (n *LiteralNode) GetValue() interface{} { + return n.String() +} + +// String literal to text +func (n *LiteralNode) String() string { + origin := n.Value.GetToken().Origin + lit := strings.TrimRight(strings.TrimRight(origin, " "), "\n") + if n.Comment != nil { + return fmt.Sprintf("%s %s\n%s", n.Start.Value, n.Comment.String(), lit) + } + return fmt.Sprintf("%s\n%s", n.Start.Value, lit) +} + +func (n *LiteralNode) stringWithoutComment() string { + return n.String() +} + +// MarshalYAML encodes to a YAML text +func (n *LiteralNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// MergeKeyNode type of merge key node +type MergeKeyNode struct { + *BaseNode + Token *token.Token +} + +// Read implements (io.Reader).Read +func (n *MergeKeyNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns MergeKeyType +func (n *MergeKeyNode) Type() NodeType { return MergeKeyType } + +// GetToken returns token instance +func (n *MergeKeyNode) GetToken() *token.Token { + return n.Token +} + +// GetValue returns '<<' value +func (n *MergeKeyNode) GetValue() interface{} { + return n.Token.Value +} + +// String returns '<<' value +func (n *MergeKeyNode) String() string { + return n.stringWithoutComment() +} + +func (n *MergeKeyNode) stringWithoutComment() string { + return n.Token.Value +} + +// AddColumn add column number to child nodes recursively +func (n *MergeKeyNode) AddColumn(col int) { + n.Token.AddColumn(col) +} + +// MarshalYAML encodes to a YAML text +func (n *MergeKeyNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// BoolNode type of boolean node +type BoolNode struct { + *BaseNode + Token *token.Token + Value bool +} + +// Read implements (io.Reader).Read +func (n *BoolNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns BoolType +func (n *BoolNode) Type() NodeType { return BoolType } + +// GetToken returns token instance +func (n *BoolNode) GetToken() *token.Token { + return n.Token +} + +// AddColumn add column number to child nodes recursively +func (n *BoolNode) AddColumn(col int) { + n.Token.AddColumn(col) +} + +// GetValue returns boolean value +func (n *BoolNode) GetValue() interface{} { + return n.Value +} + +// String boolean to text +func (n *BoolNode) String() string { + if n.Comment != nil { + return addCommentString(n.Token.Value, n.Comment) + } + return n.stringWithoutComment() +} + +func (n *BoolNode) stringWithoutComment() string { + return n.Token.Value +} + +// MarshalYAML encodes to a YAML text +func (n *BoolNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// InfinityNode type of infinity node +type InfinityNode struct { + *BaseNode + Token *token.Token + Value float64 +} + +// Read implements (io.Reader).Read +func (n *InfinityNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns InfinityType +func (n *InfinityNode) Type() NodeType { return InfinityType } + +// GetToken returns token instance +func (n *InfinityNode) GetToken() *token.Token { + return n.Token +} + +// AddColumn add column number to child nodes recursively +func (n *InfinityNode) AddColumn(col int) { + n.Token.AddColumn(col) +} + +// GetValue returns math.Inf(0) or math.Inf(-1) +func (n *InfinityNode) GetValue() interface{} { + return n.Value +} + +// String infinity to text +func (n *InfinityNode) String() string { + if n.Comment != nil { + return addCommentString(n.Token.Value, n.Comment) + } + return n.stringWithoutComment() +} + +func (n *InfinityNode) stringWithoutComment() string { + return n.Token.Value +} + +// MarshalYAML encodes to a YAML text +func (n *InfinityNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// NanNode type of nan node +type NanNode struct { + *BaseNode + Token *token.Token +} + +// Read implements (io.Reader).Read +func (n *NanNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns NanType +func (n *NanNode) Type() NodeType { return NanType } + +// GetToken returns token instance +func (n *NanNode) GetToken() *token.Token { + return n.Token +} + +// AddColumn add column number to child nodes recursively +func (n *NanNode) AddColumn(col int) { + n.Token.AddColumn(col) +} + +// GetValue returns math.NaN() +func (n *NanNode) GetValue() interface{} { + return math.NaN() +} + +// String returns .nan +func (n *NanNode) String() string { + if n.Comment != nil { + return addCommentString(n.Token.Value, n.Comment) + } + return n.stringWithoutComment() +} + +func (n *NanNode) stringWithoutComment() string { + return n.Token.Value +} + +// MarshalYAML encodes to a YAML text +func (n *NanNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// MapNode interface of MappingValueNode / MappingNode +type MapNode interface { + MapRange() *MapNodeIter +} + +// MapNodeIter is an iterator for ranging over a MapNode +type MapNodeIter struct { + values []*MappingValueNode + idx int +} + +const ( + startRangeIndex = -1 +) + +// Next advances the map iterator and reports whether there is another entry. +// It returns false when the iterator is exhausted. +func (m *MapNodeIter) Next() bool { + m.idx++ + next := m.idx < len(m.values) + return next +} + +// Key returns the key of the iterator's current map node entry. +func (m *MapNodeIter) Key() MapKeyNode { + return m.values[m.idx].Key +} + +// Value returns the value of the iterator's current map node entry. +func (m *MapNodeIter) Value() Node { + return m.values[m.idx].Value +} + +// MappingNode type of mapping node +type MappingNode struct { + *BaseNode + Start *token.Token + End *token.Token + IsFlowStyle bool + Values []*MappingValueNode + FootComment *CommentGroupNode +} + +func (n *MappingNode) startPos() *token.Position { + if len(n.Values) == 0 { + return n.Start.Position + } + return n.Values[0].Key.GetToken().Position +} + +// Merge merge key/value of map. +func (n *MappingNode) Merge(target *MappingNode) { + keyToMapValueMap := map[string]*MappingValueNode{} + for _, value := range n.Values { + key := value.Key.String() + keyToMapValueMap[key] = value + } + column := n.startPos().Column - target.startPos().Column + target.AddColumn(column) + for _, value := range target.Values { + mapValue, exists := keyToMapValueMap[value.Key.String()] + if exists { + mapValue.Value = value.Value + } else { + n.Values = append(n.Values, value) + } + } +} + +// SetIsFlowStyle set value to IsFlowStyle field recursively. +func (n *MappingNode) SetIsFlowStyle(isFlow bool) { + n.IsFlowStyle = isFlow + for _, value := range n.Values { + value.SetIsFlowStyle(isFlow) + } +} + +// Read implements (io.Reader).Read +func (n *MappingNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns MappingType +func (n *MappingNode) Type() NodeType { return MappingType } + +// GetToken returns token instance +func (n *MappingNode) GetToken() *token.Token { + return n.Start +} + +// AddColumn add column number to child nodes recursively +func (n *MappingNode) AddColumn(col int) { + n.Start.AddColumn(col) + n.End.AddColumn(col) + for _, value := range n.Values { + value.AddColumn(col) + } +} + +func (n *MappingNode) flowStyleString(commentMode bool) string { + values := []string{} + for _, value := range n.Values { + values = append(values, strings.TrimLeft(value.String(), " ")) + } + mapText := fmt.Sprintf("{%s}", strings.Join(values, ", ")) + if commentMode && n.Comment != nil { + return addCommentString(mapText, n.Comment) + } + return mapText +} + +func (n *MappingNode) blockStyleString(commentMode bool) string { + values := []string{} + for _, value := range n.Values { + values = append(values, value.String()) + } + mapText := strings.Join(values, "\n") + if commentMode && n.Comment != nil { + value := values[0] + var spaceNum int + for i := 0; i < len(value); i++ { + if value[i] != ' ' { + break + } + spaceNum++ + } + comment := n.Comment.StringWithSpace(spaceNum) + return fmt.Sprintf("%s\n%s", comment, mapText) + } + return mapText +} + +// String mapping values to text +func (n *MappingNode) String() string { + if len(n.Values) == 0 { + if n.Comment != nil { + return addCommentString("{}", n.Comment) + } + return "{}" + } + + commentMode := true + if n.IsFlowStyle || len(n.Values) == 0 { + return n.flowStyleString(commentMode) + } + return n.blockStyleString(commentMode) +} + +// MapRange implements MapNode protocol +func (n *MappingNode) MapRange() *MapNodeIter { + return &MapNodeIter{ + idx: startRangeIndex, + values: n.Values, + } +} + +// MarshalYAML encodes to a YAML text +func (n *MappingNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// MappingKeyNode type of tag node +type MappingKeyNode struct { + *BaseNode + Start *token.Token + Value Node +} + +// Read implements (io.Reader).Read +func (n *MappingKeyNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns MappingKeyType +func (n *MappingKeyNode) Type() NodeType { return MappingKeyType } + +// GetToken returns token instance +func (n *MappingKeyNode) GetToken() *token.Token { + return n.Start +} + +// AddColumn add column number to child nodes recursively +func (n *MappingKeyNode) AddColumn(col int) { + n.Start.AddColumn(col) + if n.Value != nil { + n.Value.AddColumn(col) + } +} + +// String tag to text +func (n *MappingKeyNode) String() string { + return n.stringWithoutComment() +} + +func (n *MappingKeyNode) stringWithoutComment() string { + return fmt.Sprintf("%s %s", n.Start.Value, n.Value.String()) +} + +// MarshalYAML encodes to a YAML text +func (n *MappingKeyNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// MappingValueNode type of mapping value +type MappingValueNode struct { + *BaseNode + Start *token.Token + Key MapKeyNode + Value Node + FootComment *CommentGroupNode +} + +// Replace replace value node. +func (n *MappingValueNode) Replace(value Node) error { + column := n.Value.GetToken().Position.Column - value.GetToken().Position.Column + value.AddColumn(column) + n.Value = value + return nil +} + +// Read implements (io.Reader).Read +func (n *MappingValueNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns MappingValueType +func (n *MappingValueNode) Type() NodeType { return MappingValueType } + +// GetToken returns token instance +func (n *MappingValueNode) GetToken() *token.Token { + return n.Start +} + +// AddColumn add column number to child nodes recursively +func (n *MappingValueNode) AddColumn(col int) { + n.Start.AddColumn(col) + if n.Key != nil { + n.Key.AddColumn(col) + } + if n.Value != nil { + n.Value.AddColumn(col) + } +} + +// SetIsFlowStyle set value to IsFlowStyle field recursively. +func (n *MappingValueNode) SetIsFlowStyle(isFlow bool) { + switch value := n.Value.(type) { + case *MappingNode: + value.SetIsFlowStyle(isFlow) + case *MappingValueNode: + value.SetIsFlowStyle(isFlow) + case *SequenceNode: + value.SetIsFlowStyle(isFlow) + } +} + +// String mapping value to text +func (n *MappingValueNode) String() string { + var text string + if n.Comment != nil { + text = fmt.Sprintf( + "%s\n%s", + n.Comment.StringWithSpace(n.Key.GetToken().Position.Column-1), + n.toString(), + ) + } else { + text = n.toString() + } + if n.FootComment != nil { + text += fmt.Sprintf("\n%s", n.FootComment.StringWithSpace(n.Key.GetToken().Position.Column-1)) + } + return text +} + +func (n *MappingValueNode) toString() string { + space := strings.Repeat(" ", n.Key.GetToken().Position.Column-1) + keyIndentLevel := n.Key.GetToken().Position.IndentLevel + valueIndentLevel := n.Value.GetToken().Position.IndentLevel + keyComment := n.Key.GetComment() + if _, ok := n.Value.(ScalarNode); ok { + return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String()) + } else if keyIndentLevel < valueIndentLevel { + if keyComment != nil { + return fmt.Sprintf( + "%s%s: %s\n%s", + space, + n.Key.stringWithoutComment(), + keyComment.String(), + n.Value.String(), + ) + } + return fmt.Sprintf("%s%s:\n%s", space, n.Key.String(), n.Value.String()) + } else if m, ok := n.Value.(*MappingNode); ok && (m.IsFlowStyle || len(m.Values) == 0) { + return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String()) + } else if s, ok := n.Value.(*SequenceNode); ok && (s.IsFlowStyle || len(s.Values) == 0) { + return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String()) + } else if _, ok := n.Value.(*AnchorNode); ok { + return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String()) + } else if _, ok := n.Value.(*AliasNode); ok { + return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String()) + } + if keyComment != nil { + return fmt.Sprintf( + "%s%s: %s\n%s", + space, + n.Key.stringWithoutComment(), + keyComment.String(), + n.Value.String(), + ) + } + if m, ok := n.Value.(*MappingNode); ok && m.Comment != nil { + return fmt.Sprintf( + "%s%s: %s", + space, + n.Key.String(), + strings.TrimLeft(n.Value.String(), " "), + ) + } + return fmt.Sprintf("%s%s:\n%s", space, n.Key.String(), n.Value.String()) +} + +// MapRange implements MapNode protocol +func (n *MappingValueNode) MapRange() *MapNodeIter { + return &MapNodeIter{ + idx: startRangeIndex, + values: []*MappingValueNode{n}, + } +} + +// MarshalYAML encodes to a YAML text +func (n *MappingValueNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// ArrayNode interface of SequenceNode +type ArrayNode interface { + ArrayRange() *ArrayNodeIter +} + +// ArrayNodeIter is an iterator for ranging over a ArrayNode +type ArrayNodeIter struct { + values []Node + idx int +} + +// Next advances the array iterator and reports whether there is another entry. +// It returns false when the iterator is exhausted. +func (m *ArrayNodeIter) Next() bool { + m.idx++ + next := m.idx < len(m.values) + return next +} + +// Value returns the value of the iterator's current array entry. +func (m *ArrayNodeIter) Value() Node { + return m.values[m.idx] +} + +// Len returns length of array +func (m *ArrayNodeIter) Len() int { + return len(m.values) +} + +// SequenceNode type of sequence node +type SequenceNode struct { + *BaseNode + Start *token.Token + End *token.Token + IsFlowStyle bool + Values []Node + ValueHeadComments []*CommentGroupNode + FootComment *CommentGroupNode +} + +// Replace replace value node. +func (n *SequenceNode) Replace(idx int, value Node) error { + if len(n.Values) <= idx { + return xerrors.Errorf( + "invalid index for sequence: sequence length is %d, but specified %d index", + len(n.Values), idx, + ) + } + column := n.Values[idx].GetToken().Position.Column - value.GetToken().Position.Column + value.AddColumn(column) + n.Values[idx] = value + return nil +} + +// Merge merge sequence value. +func (n *SequenceNode) Merge(target *SequenceNode) { + column := n.Start.Position.Column - target.Start.Position.Column + target.AddColumn(column) + for _, value := range target.Values { + n.Values = append(n.Values, value) + } +} + +// SetIsFlowStyle set value to IsFlowStyle field recursively. +func (n *SequenceNode) SetIsFlowStyle(isFlow bool) { + n.IsFlowStyle = isFlow + for _, value := range n.Values { + switch value := value.(type) { + case *MappingNode: + value.SetIsFlowStyle(isFlow) + case *MappingValueNode: + value.SetIsFlowStyle(isFlow) + case *SequenceNode: + value.SetIsFlowStyle(isFlow) + } + } +} + +// Read implements (io.Reader).Read +func (n *SequenceNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns SequenceType +func (n *SequenceNode) Type() NodeType { return SequenceType } + +// GetToken returns token instance +func (n *SequenceNode) GetToken() *token.Token { + return n.Start +} + +// AddColumn add column number to child nodes recursively +func (n *SequenceNode) AddColumn(col int) { + n.Start.AddColumn(col) + n.End.AddColumn(col) + for _, value := range n.Values { + value.AddColumn(col) + } +} + +func (n *SequenceNode) flowStyleString() string { + values := []string{} + for _, value := range n.Values { + values = append(values, value.String()) + } + return fmt.Sprintf("[%s]", strings.Join(values, ", ")) +} + +func (n *SequenceNode) blockStyleString() string { + space := strings.Repeat(" ", n.Start.Position.Column-1) + values := []string{} + if n.Comment != nil { + values = append(values, n.Comment.StringWithSpace(n.Start.Position.Column-1)) + } + + for idx, value := range n.Values { + valueStr := value.String() + splittedValues := strings.Split(valueStr, "\n") + trimmedFirstValue := strings.TrimLeft(splittedValues[0], " ") + diffLength := len(splittedValues[0]) - len(trimmedFirstValue) + if len(splittedValues) > 1 && value.Type() == StringType || value.Type() == LiteralType { + // If multi-line string, the space characters for indent have already been added, so delete them. + prefix := space + " " + for i := 1; i < len(splittedValues); i++ { + splittedValues[i] = strings.TrimPrefix(splittedValues[i], prefix) + } + } + newValues := []string{trimmedFirstValue} + for i := 1; i < len(splittedValues); i++ { + if len(splittedValues[i]) <= diffLength { + // this line is \n or white space only + newValues = append(newValues, "") + continue + } + trimmed := splittedValues[i][diffLength:] + newValues = append(newValues, fmt.Sprintf("%s %s", space, trimmed)) + } + newValue := strings.Join(newValues, "\n") + if len(n.ValueHeadComments) == len(n.Values) && n.ValueHeadComments[idx] != nil { + values = append(values, n.ValueHeadComments[idx].StringWithSpace(n.Start.Position.Column-1)) + } + values = append(values, fmt.Sprintf("%s- %s", space, newValue)) + } + if n.FootComment != nil { + values = append(values, n.FootComment.StringWithSpace(n.Start.Position.Column-1)) + } + return strings.Join(values, "\n") +} + +// String sequence to text +func (n *SequenceNode) String() string { + if n.IsFlowStyle || len(n.Values) == 0 { + return n.flowStyleString() + } + return n.blockStyleString() +} + +// ArrayRange implements ArrayNode protocol +func (n *SequenceNode) ArrayRange() *ArrayNodeIter { + return &ArrayNodeIter{ + idx: startRangeIndex, + values: n.Values, + } +} + +// MarshalYAML encodes to a YAML text +func (n *SequenceNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// AnchorNode type of anchor node +type AnchorNode struct { + *BaseNode + Start *token.Token + Name Node + Value Node +} + +func (n *AnchorNode) SetName(name string) error { + if n.Name == nil { + return ErrInvalidAnchorName + } + s, ok := n.Name.(*StringNode) + if !ok { + return ErrInvalidAnchorName + } + s.Value = name + return nil +} + +// Read implements (io.Reader).Read +func (n *AnchorNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns AnchorType +func (n *AnchorNode) Type() NodeType { return AnchorType } + +// GetToken returns token instance +func (n *AnchorNode) GetToken() *token.Token { + return n.Start +} + +// AddColumn add column number to child nodes recursively +func (n *AnchorNode) AddColumn(col int) { + n.Start.AddColumn(col) + if n.Name != nil { + n.Name.AddColumn(col) + } + if n.Value != nil { + n.Value.AddColumn(col) + } +} + +// String anchor to text +func (n *AnchorNode) String() string { + value := n.Value.String() + if len(strings.Split(value, "\n")) > 1 { + return fmt.Sprintf("&%s\n%s", n.Name.String(), value) + } else if s, ok := n.Value.(*SequenceNode); ok && !s.IsFlowStyle { + return fmt.Sprintf("&%s\n%s", n.Name.String(), value) + } else if m, ok := n.Value.(*MappingNode); ok && !m.IsFlowStyle { + return fmt.Sprintf("&%s\n%s", n.Name.String(), value) + } + return fmt.Sprintf("&%s %s", n.Name.String(), value) +} + +// MarshalYAML encodes to a YAML text +func (n *AnchorNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// AliasNode type of alias node +type AliasNode struct { + *BaseNode + Start *token.Token + Value Node +} + +func (n *AliasNode) SetName(name string) error { + if n.Value == nil { + return ErrInvalidAliasName + } + s, ok := n.Value.(*StringNode) + if !ok { + return ErrInvalidAliasName + } + s.Value = name + return nil +} + +// Read implements (io.Reader).Read +func (n *AliasNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns AliasType +func (n *AliasNode) Type() NodeType { return AliasType } + +// GetToken returns token instance +func (n *AliasNode) GetToken() *token.Token { + return n.Start +} + +// AddColumn add column number to child nodes recursively +func (n *AliasNode) AddColumn(col int) { + n.Start.AddColumn(col) + if n.Value != nil { + n.Value.AddColumn(col) + } +} + +// String alias to text +func (n *AliasNode) String() string { + return fmt.Sprintf("*%s", n.Value.String()) +} + +// MarshalYAML encodes to a YAML text +func (n *AliasNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// DirectiveNode type of directive node +type DirectiveNode struct { + *BaseNode + Start *token.Token + Value Node +} + +// Read implements (io.Reader).Read +func (n *DirectiveNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns DirectiveType +func (n *DirectiveNode) Type() NodeType { return DirectiveType } + +// GetToken returns token instance +func (n *DirectiveNode) GetToken() *token.Token { + return n.Start +} + +// AddColumn add column number to child nodes recursively +func (n *DirectiveNode) AddColumn(col int) { + if n.Value != nil { + n.Value.AddColumn(col) + } +} + +// String directive to text +func (n *DirectiveNode) String() string { + return fmt.Sprintf("%s%s", n.Start.Value, n.Value.String()) +} + +// MarshalYAML encodes to a YAML text +func (n *DirectiveNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// TagNode type of tag node +type TagNode struct { + *BaseNode + Start *token.Token + Value Node +} + +// Read implements (io.Reader).Read +func (n *TagNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns TagType +func (n *TagNode) Type() NodeType { return TagType } + +// GetToken returns token instance +func (n *TagNode) GetToken() *token.Token { + return n.Start +} + +// AddColumn add column number to child nodes recursively +func (n *TagNode) AddColumn(col int) { + n.Start.AddColumn(col) + if n.Value != nil { + n.Value.AddColumn(col) + } +} + +// String tag to text +func (n *TagNode) String() string { + return fmt.Sprintf("%s %s", n.Start.Value, n.Value.String()) +} + +// MarshalYAML encodes to a YAML text +func (n *TagNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// CommentNode type of comment node +type CommentNode struct { + *BaseNode + Token *token.Token +} + +// Read implements (io.Reader).Read +func (n *CommentNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns TagType +func (n *CommentNode) Type() NodeType { return CommentType } + +// GetToken returns token instance +func (n *CommentNode) GetToken() *token.Token { return n.Token } + +// AddColumn add column number to child nodes recursively +func (n *CommentNode) AddColumn(col int) { + if n.Token == nil { + return + } + n.Token.AddColumn(col) +} + +// String comment to text +func (n *CommentNode) String() string { + return fmt.Sprintf("#%s", n.Token.Value) +} + +// MarshalYAML encodes to a YAML text +func (n *CommentNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// CommentGroupNode type of comment node +type CommentGroupNode struct { + *BaseNode + Comments []*CommentNode +} + +// Read implements (io.Reader).Read +func (n *CommentGroupNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// Type returns TagType +func (n *CommentGroupNode) Type() NodeType { return CommentType } + +// GetToken returns token instance +func (n *CommentGroupNode) GetToken() *token.Token { + if len(n.Comments) > 0 { + return n.Comments[0].Token + } + return nil +} + +// AddColumn add column number to child nodes recursively +func (n *CommentGroupNode) AddColumn(col int) { + for _, comment := range n.Comments { + comment.AddColumn(col) + } +} + +// String comment to text +func (n *CommentGroupNode) String() string { + values := []string{} + for _, comment := range n.Comments { + values = append(values, comment.String()) + } + return strings.Join(values, "\n") +} + +func (n *CommentGroupNode) StringWithSpace(col int) string { + values := []string{} + space := strings.Repeat(" ", col) + for _, comment := range n.Comments { + values = append(values, space+comment.String()) + } + return strings.Join(values, "\n") + +} + +// MarshalYAML encodes to a YAML text +func (n *CommentGroupNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +// Visitor has Visit method that is invokded for each node encountered by Walk. +// If the result visitor w is not nil, Walk visits each of the children of node with the visitor w, +// followed by a call of w.Visit(nil). +type Visitor interface { + Visit(Node) Visitor +} + +// Walk traverses an AST in depth-first order: It starts by calling v.Visit(node); node must not be nil. +// If the visitor w returned by v.Visit(node) is not nil, +// Walk is invoked recursively with visitor w for each of the non-nil children of node, +// followed by a call of w.Visit(nil). +func Walk(v Visitor, node Node) { + if v = v.Visit(node); v == nil { + return + } + + switch n := node.(type) { + case *CommentNode: + case *NullNode: + walkComment(v, n.BaseNode) + case *IntegerNode: + walkComment(v, n.BaseNode) + case *FloatNode: + walkComment(v, n.BaseNode) + case *StringNode: + walkComment(v, n.BaseNode) + case *MergeKeyNode: + walkComment(v, n.BaseNode) + case *BoolNode: + walkComment(v, n.BaseNode) + case *InfinityNode: + walkComment(v, n.BaseNode) + case *NanNode: + walkComment(v, n.BaseNode) + case *LiteralNode: + walkComment(v, n.BaseNode) + Walk(v, n.Value) + case *DirectiveNode: + walkComment(v, n.BaseNode) + Walk(v, n.Value) + case *TagNode: + walkComment(v, n.BaseNode) + Walk(v, n.Value) + case *DocumentNode: + walkComment(v, n.BaseNode) + Walk(v, n.Body) + case *MappingNode: + walkComment(v, n.BaseNode) + for _, value := range n.Values { + Walk(v, value) + } + case *MappingKeyNode: + walkComment(v, n.BaseNode) + Walk(v, n.Value) + case *MappingValueNode: + walkComment(v, n.BaseNode) + Walk(v, n.Key) + Walk(v, n.Value) + case *SequenceNode: + walkComment(v, n.BaseNode) + for _, value := range n.Values { + Walk(v, value) + } + case *AnchorNode: + walkComment(v, n.BaseNode) + Walk(v, n.Name) + Walk(v, n.Value) + case *AliasNode: + walkComment(v, n.BaseNode) + Walk(v, n.Value) + } +} + +func walkComment(v Visitor, base *BaseNode) { + if base == nil { + return + } + if base.Comment == nil { + return + } + Walk(v, base.Comment) +} + +type filterWalker struct { + typ NodeType + results []Node +} + +func (v *filterWalker) Visit(n Node) Visitor { + if v.typ == n.Type() { + v.results = append(v.results, n) + } + return v +} + +type parentFinder struct { + target Node +} + +func (f *parentFinder) walk(parent, node Node) Node { + if f.target == node { + return parent + } + switch n := node.(type) { + case *CommentNode: + return nil + case *NullNode: + return nil + case *IntegerNode: + return nil + case *FloatNode: + return nil + case *StringNode: + return nil + case *MergeKeyNode: + return nil + case *BoolNode: + return nil + case *InfinityNode: + return nil + case *NanNode: + return nil + case *LiteralNode: + return f.walk(node, n.Value) + case *DirectiveNode: + return f.walk(node, n.Value) + case *TagNode: + return f.walk(node, n.Value) + case *DocumentNode: + return f.walk(node, n.Body) + case *MappingNode: + for _, value := range n.Values { + if found := f.walk(node, value); found != nil { + return found + } + } + case *MappingKeyNode: + return f.walk(node, n.Value) + case *MappingValueNode: + if found := f.walk(node, n.Key); found != nil { + return found + } + return f.walk(node, n.Value) + case *SequenceNode: + for _, value := range n.Values { + if found := f.walk(node, value); found != nil { + return found + } + } + case *AnchorNode: + if found := f.walk(node, n.Name); found != nil { + return found + } + return f.walk(node, n.Value) + case *AliasNode: + return f.walk(node, n.Value) + } + return nil +} + +// Parent get parent node from child node. +func Parent(root, child Node) Node { + finder := &parentFinder{target: child} + return finder.walk(root, root) +} + +// Filter returns a list of nodes that match the given type. +func Filter(typ NodeType, node Node) []Node { + walker := &filterWalker{typ: typ} + Walk(walker, node) + return walker.results +} + +// FilterFile returns a list of nodes that match the given type. +func FilterFile(typ NodeType, file *File) []Node { + results := []Node{} + for _, doc := range file.Docs { + walker := &filterWalker{typ: typ} + Walk(walker, doc) + results = append(results, walker.results...) + } + return results +} + +type ErrInvalidMergeType struct { + dst Node + src Node +} + +func (e *ErrInvalidMergeType) Error() string { + return fmt.Sprintf("cannot merge %s into %s", e.src.Type(), e.dst.Type()) +} + +// Merge merge document, map, sequence node. +func Merge(dst Node, src Node) error { + if doc, ok := src.(*DocumentNode); ok { + src = doc.Body + } + err := &ErrInvalidMergeType{dst: dst, src: src} + switch dst.Type() { + case DocumentType: + node := dst.(*DocumentNode) + return Merge(node.Body, src) + case MappingType: + node := dst.(*MappingNode) + target, ok := src.(*MappingNode) + if !ok { + return err + } + node.Merge(target) + return nil + case SequenceType: + node := dst.(*SequenceNode) + target, ok := src.(*SequenceNode) + if !ok { + return err + } + node.Merge(target) + return nil + } + return err +} diff --git a/vendor/github.com/goccy/go-yaml/decode.go b/vendor/github.com/goccy/go-yaml/decode.go new file mode 100644 index 000000000..d3dbabcbc --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/decode.go @@ -0,0 +1,1753 @@ +package yaml + +import ( + "bytes" + "context" + "encoding" + "encoding/base64" + "fmt" + "io" + "io/ioutil" + "math" + "os" + "path/filepath" + "reflect" + "sort" + "strconv" + "time" + + "github.com/goccy/go-yaml/ast" + "github.com/goccy/go-yaml/internal/errors" + "github.com/goccy/go-yaml/parser" + "github.com/goccy/go-yaml/token" + "golang.org/x/xerrors" +) + +// Decoder reads and decodes YAML values from an input stream. +type Decoder struct { + reader io.Reader + referenceReaders []io.Reader + anchorNodeMap map[string]ast.Node + anchorValueMap map[string]reflect.Value + customUnmarshalerMap map[reflect.Type]func(interface{}, []byte) error + toCommentMap CommentMap + opts []DecodeOption + referenceFiles []string + referenceDirs []string + isRecursiveDir bool + isResolvedReference bool + validator StructValidator + disallowUnknownField bool + disallowDuplicateKey bool + useOrderedMap bool + useJSONUnmarshaler bool + parsedFile *ast.File + streamIndex int +} + +// NewDecoder returns a new decoder that reads from r. +func NewDecoder(r io.Reader, opts ...DecodeOption) *Decoder { + return &Decoder{ + reader: r, + anchorNodeMap: map[string]ast.Node{}, + anchorValueMap: map[string]reflect.Value{}, + customUnmarshalerMap: map[reflect.Type]func(interface{}, []byte) error{}, + opts: opts, + referenceReaders: []io.Reader{}, + referenceFiles: []string{}, + referenceDirs: []string{}, + isRecursiveDir: false, + isResolvedReference: false, + disallowUnknownField: false, + disallowDuplicateKey: false, + useOrderedMap: false, + } +} + +func (d *Decoder) castToFloat(v interface{}) interface{} { + switch vv := v.(type) { + case int: + return float64(vv) + case int8: + return float64(vv) + case int16: + return float64(vv) + case int32: + return float64(vv) + case int64: + return float64(vv) + case uint: + return float64(vv) + case uint8: + return float64(vv) + case uint16: + return float64(vv) + case uint32: + return float64(vv) + case uint64: + return float64(vv) + case float32: + return float64(vv) + case float64: + return vv + case string: + // if error occurred, return zero value + f, _ := strconv.ParseFloat(vv, 64) + return f + } + return 0 +} + +func (d *Decoder) mergeValueNode(value ast.Node) ast.Node { + if value.Type() == ast.AliasType { + aliasNode := value.(*ast.AliasNode) + aliasName := aliasNode.Value.GetToken().Value + return d.anchorNodeMap[aliasName] + } + return value +} + +func (d *Decoder) mapKeyNodeToString(node ast.MapKeyNode) string { + key := d.nodeToValue(node) + if key == nil { + return "null" + } + if k, ok := key.(string); ok { + return k + } + return fmt.Sprint(key) +} + +func (d *Decoder) setToMapValue(node ast.Node, m map[string]interface{}) { + d.setPathToCommentMap(node) + switch n := node.(type) { + case *ast.MappingValueNode: + if n.Key.Type() == ast.MergeKeyType { + d.setToMapValue(d.mergeValueNode(n.Value), m) + } else { + key := d.mapKeyNodeToString(n.Key) + m[key] = d.nodeToValue(n.Value) + } + case *ast.MappingNode: + for _, value := range n.Values { + d.setToMapValue(value, m) + } + case *ast.AnchorNode: + anchorName := n.Name.GetToken().Value + d.anchorNodeMap[anchorName] = n.Value + } +} + +func (d *Decoder) setToOrderedMapValue(node ast.Node, m *MapSlice) { + switch n := node.(type) { + case *ast.MappingValueNode: + if n.Key.Type() == ast.MergeKeyType { + d.setToOrderedMapValue(d.mergeValueNode(n.Value), m) + } else { + key := d.mapKeyNodeToString(n.Key) + *m = append(*m, MapItem{Key: key, Value: d.nodeToValue(n.Value)}) + } + case *ast.MappingNode: + for _, value := range n.Values { + d.setToOrderedMapValue(value, m) + } + } +} + +func (d *Decoder) setPathToCommentMap(node ast.Node) { + if d.toCommentMap == nil { + return + } + d.addHeadOrLineCommentToMap(node) + d.addFootCommentToMap(node) +} + +func (d *Decoder) addHeadOrLineCommentToMap(node ast.Node) { + sequence, ok := node.(*ast.SequenceNode) + if ok { + d.addSequenceNodeCommentToMap(sequence) + return + } + commentGroup := node.GetComment() + if commentGroup == nil { + return + } + texts := []string{} + targetLine := node.GetToken().Position.Line + minCommentLine := math.MaxInt + for _, comment := range commentGroup.Comments { + if minCommentLine > comment.Token.Position.Line { + minCommentLine = comment.Token.Position.Line + } + texts = append(texts, comment.Token.Value) + } + if len(texts) == 0 { + return + } + commentPath := node.GetPath() + if minCommentLine < targetLine { + d.addCommentToMap(commentPath, HeadComment(texts...)) + } else { + d.addCommentToMap(commentPath, LineComment(texts[0])) + } +} + +func (d *Decoder) addSequenceNodeCommentToMap(node *ast.SequenceNode) { + if len(node.ValueHeadComments) != 0 { + for idx, headComment := range node.ValueHeadComments { + if headComment == nil { + continue + } + texts := make([]string, 0, len(headComment.Comments)) + for _, comment := range headComment.Comments { + texts = append(texts, comment.Token.Value) + } + if len(texts) != 0 { + d.addCommentToMap(node.Values[idx].GetPath(), HeadComment(texts...)) + } + } + } + firstElemHeadComment := node.GetComment() + if firstElemHeadComment != nil { + texts := make([]string, 0, len(firstElemHeadComment.Comments)) + for _, comment := range firstElemHeadComment.Comments { + texts = append(texts, comment.Token.Value) + } + if len(texts) != 0 { + d.addCommentToMap(node.Values[0].GetPath(), HeadComment(texts...)) + } + } +} + +func (d *Decoder) addFootCommentToMap(node ast.Node) { + var ( + footComment *ast.CommentGroupNode + footCommentPath string = node.GetPath() + ) + switch n := node.(type) { + case *ast.SequenceNode: + if len(n.Values) != 0 { + footCommentPath = n.Values[len(n.Values)-1].GetPath() + } + footComment = n.FootComment + case *ast.MappingNode: + footComment = n.FootComment + case *ast.MappingValueNode: + footComment = n.FootComment + } + if footComment == nil { + return + } + var texts []string + for _, comment := range footComment.Comments { + texts = append(texts, comment.Token.Value) + } + if len(texts) != 0 { + d.addCommentToMap(footCommentPath, FootComment(texts...)) + } +} + +func (d *Decoder) addCommentToMap(path string, comment *Comment) { + for _, c := range d.toCommentMap[path] { + if c.Position == comment.Position { + // already added same comment + return + } + } + d.toCommentMap[path] = append(d.toCommentMap[path], comment) + sort.Slice(d.toCommentMap[path], func(i, j int) bool { + return d.toCommentMap[path][i].Position < d.toCommentMap[path][j].Position + }) +} + +func (d *Decoder) nodeToValue(node ast.Node) interface{} { + d.setPathToCommentMap(node) + switch n := node.(type) { + case *ast.NullNode: + return nil + case *ast.StringNode: + return n.GetValue() + case *ast.IntegerNode: + return n.GetValue() + case *ast.FloatNode: + return n.GetValue() + case *ast.BoolNode: + return n.GetValue() + case *ast.InfinityNode: + return n.GetValue() + case *ast.NanNode: + return n.GetValue() + case *ast.TagNode: + switch token.ReservedTagKeyword(n.Start.Value) { + case token.TimestampTag: + t, _ := d.castToTime(n.Value) + return t + case token.IntegerTag: + i, _ := strconv.Atoi(fmt.Sprint(d.nodeToValue(n.Value))) + return i + case token.FloatTag: + return d.castToFloat(d.nodeToValue(n.Value)) + case token.NullTag: + return nil + case token.BinaryTag: + b, _ := base64.StdEncoding.DecodeString(d.nodeToValue(n.Value).(string)) + return b + case token.StringTag: + return d.nodeToValue(n.Value) + case token.MappingTag: + return d.nodeToValue(n.Value) + } + case *ast.AnchorNode: + anchorName := n.Name.GetToken().Value + anchorValue := d.nodeToValue(n.Value) + d.anchorNodeMap[anchorName] = n.Value + return anchorValue + case *ast.AliasNode: + aliasName := n.Value.GetToken().Value + node := d.anchorNodeMap[aliasName] + return d.nodeToValue(node) + case *ast.LiteralNode: + return n.Value.GetValue() + case *ast.MappingKeyNode: + return d.nodeToValue(n.Value) + case *ast.MappingValueNode: + if n.Key.Type() == ast.MergeKeyType { + value := d.mergeValueNode(n.Value) + if d.useOrderedMap { + m := MapSlice{} + d.setToOrderedMapValue(value, &m) + return m + } + m := map[string]interface{}{} + d.setToMapValue(value, m) + return m + } + key := d.mapKeyNodeToString(n.Key) + if d.useOrderedMap { + return MapSlice{{Key: key, Value: d.nodeToValue(n.Value)}} + } + return map[string]interface{}{ + key: d.nodeToValue(n.Value), + } + case *ast.MappingNode: + if d.useOrderedMap { + m := make(MapSlice, 0, len(n.Values)) + for _, value := range n.Values { + d.setToOrderedMapValue(value, &m) + } + return m + } + m := make(map[string]interface{}, len(n.Values)) + for _, value := range n.Values { + d.setToMapValue(value, m) + } + return m + case *ast.SequenceNode: + v := make([]interface{}, 0, len(n.Values)) + for _, value := range n.Values { + v = append(v, d.nodeToValue(value)) + } + return v + } + return nil +} + +func (d *Decoder) resolveAlias(node ast.Node) (ast.Node, error) { + switch n := node.(type) { + case *ast.MappingNode: + for idx, v := range n.Values { + value, err := d.resolveAlias(v) + if err != nil { + return nil, err + } + n.Values[idx] = value.(*ast.MappingValueNode) + } + case *ast.TagNode: + value, err := d.resolveAlias(n.Value) + if err != nil { + return nil, err + } + n.Value = value + case *ast.MappingKeyNode: + value, err := d.resolveAlias(n.Value) + if err != nil { + return nil, err + } + n.Value = value + case *ast.MappingValueNode: + if n.Key.Type() == ast.MergeKeyType && n.Value.Type() == ast.AliasType { + value, err := d.resolveAlias(n.Value) + if err != nil { + return nil, err + } + keyColumn := n.Key.GetToken().Position.Column + requiredColumn := keyColumn + 2 + value.AddColumn(requiredColumn) + n.Value = value + } else { + key, err := d.resolveAlias(n.Key) + if err != nil { + return nil, err + } + n.Key = key.(ast.MapKeyNode) + value, err := d.resolveAlias(n.Value) + if err != nil { + return nil, err + } + n.Value = value + } + case *ast.SequenceNode: + for idx, v := range n.Values { + value, err := d.resolveAlias(v) + if err != nil { + return nil, err + } + n.Values[idx] = value + } + case *ast.AliasNode: + aliasName := n.Value.GetToken().Value + node := d.anchorNodeMap[aliasName] + if node == nil { + return nil, xerrors.Errorf("cannot find anchor by alias name %s", aliasName) + } + return d.resolveAlias(node) + } + return node, nil +} + +func (d *Decoder) getMapNode(node ast.Node) (ast.MapNode, error) { + if _, ok := node.(*ast.NullNode); ok { + return nil, nil + } + if anchor, ok := node.(*ast.AnchorNode); ok { + mapNode, ok := anchor.Value.(ast.MapNode) + if ok { + return mapNode, nil + } + return nil, errUnexpectedNodeType(anchor.Value.Type(), ast.MappingType, node.GetToken()) + } + if alias, ok := node.(*ast.AliasNode); ok { + aliasName := alias.Value.GetToken().Value + node := d.anchorNodeMap[aliasName] + if node == nil { + return nil, xerrors.Errorf("cannot find anchor by alias name %s", aliasName) + } + mapNode, ok := node.(ast.MapNode) + if ok { + return mapNode, nil + } + return nil, errUnexpectedNodeType(node.Type(), ast.MappingType, node.GetToken()) + } + mapNode, ok := node.(ast.MapNode) + if !ok { + return nil, errUnexpectedNodeType(node.Type(), ast.MappingType, node.GetToken()) + } + return mapNode, nil +} + +func (d *Decoder) getArrayNode(node ast.Node) (ast.ArrayNode, error) { + if _, ok := node.(*ast.NullNode); ok { + return nil, nil + } + if anchor, ok := node.(*ast.AnchorNode); ok { + arrayNode, ok := anchor.Value.(ast.ArrayNode) + if ok { + return arrayNode, nil + } + + return nil, errUnexpectedNodeType(anchor.Value.Type(), ast.SequenceType, node.GetToken()) + } + if alias, ok := node.(*ast.AliasNode); ok { + aliasName := alias.Value.GetToken().Value + node := d.anchorNodeMap[aliasName] + if node == nil { + return nil, xerrors.Errorf("cannot find anchor by alias name %s", aliasName) + } + arrayNode, ok := node.(ast.ArrayNode) + if ok { + return arrayNode, nil + } + return nil, errUnexpectedNodeType(node.Type(), ast.SequenceType, node.GetToken()) + } + arrayNode, ok := node.(ast.ArrayNode) + if !ok { + return nil, errUnexpectedNodeType(node.Type(), ast.SequenceType, node.GetToken()) + } + return arrayNode, nil +} + +func (d *Decoder) fileToNode(f *ast.File) ast.Node { + for _, doc := range f.Docs { + if v := d.nodeToValue(doc.Body); v != nil { + return doc.Body + } + } + return nil +} + +func (d *Decoder) convertValue(v reflect.Value, typ reflect.Type, src ast.Node) (reflect.Value, error) { + if typ.Kind() != reflect.String { + if !v.Type().ConvertibleTo(typ) { + return reflect.Zero(typ), errTypeMismatch(typ, v.Type(), src.GetToken()) + } + return v.Convert(typ), nil + } + // cast value to string + switch v.Type().Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return reflect.ValueOf(fmt.Sprint(v.Int())), nil + case reflect.Float32, reflect.Float64: + return reflect.ValueOf(fmt.Sprint(v.Float())), nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return reflect.ValueOf(fmt.Sprint(v.Uint())), nil + case reflect.Bool: + return reflect.ValueOf(fmt.Sprint(v.Bool())), nil + } + if !v.Type().ConvertibleTo(typ) { + return reflect.Zero(typ), errTypeMismatch(typ, v.Type(), src.GetToken()) + } + return v.Convert(typ), nil +} + +type overflowError struct { + dstType reflect.Type + srcNum string +} + +func (e *overflowError) Error() string { + return fmt.Sprintf("cannot unmarshal %s into Go value of type %s ( overflow )", e.srcNum, e.dstType) +} + +func errOverflow(dstType reflect.Type, num string) *overflowError { + return &overflowError{dstType: dstType, srcNum: num} +} + +func errTypeMismatch(dstType, srcType reflect.Type, token *token.Token) *errors.TypeError { + return &errors.TypeError{DstType: dstType, SrcType: srcType, Token: token} +} + +type unknownFieldError struct { + err error +} + +func (e *unknownFieldError) Error() string { + return e.err.Error() +} + +func errUnknownField(msg string, tk *token.Token) *unknownFieldError { + return &unknownFieldError{err: errors.ErrSyntax(msg, tk)} +} + +func errUnexpectedNodeType(actual, expected ast.NodeType, tk *token.Token) error { + return errors.ErrSyntax(fmt.Sprintf("%s was used where %s is expected", actual.YAMLName(), expected.YAMLName()), tk) +} + +type duplicateKeyError struct { + err error +} + +func (e *duplicateKeyError) Error() string { + return e.err.Error() +} + +func errDuplicateKey(msg string, tk *token.Token) *duplicateKeyError { + return &duplicateKeyError{err: errors.ErrSyntax(msg, tk)} +} + +func (d *Decoder) deleteStructKeys(structType reflect.Type, unknownFields map[string]ast.Node) error { + if structType.Kind() == reflect.Ptr { + structType = structType.Elem() + } + structFieldMap, err := structFieldMap(structType) + if err != nil { + return errors.Wrapf(err, "failed to create struct field map") + } + + for j := 0; j < structType.NumField(); j++ { + field := structType.Field(j) + if isIgnoredStructField(field) { + continue + } + + structField, exists := structFieldMap[field.Name] + if !exists { + continue + } + + if structField.IsInline { + d.deleteStructKeys(field.Type, unknownFields) + } else { + delete(unknownFields, structField.RenderName) + } + } + return nil +} + +func (d *Decoder) lastNode(node ast.Node) ast.Node { + switch n := node.(type) { + case *ast.MappingNode: + if len(n.Values) > 0 { + return d.lastNode(n.Values[len(n.Values)-1]) + } + case *ast.MappingValueNode: + return d.lastNode(n.Value) + case *ast.SequenceNode: + if len(n.Values) > 0 { + return d.lastNode(n.Values[len(n.Values)-1]) + } + } + return node +} + +func (d *Decoder) unmarshalableDocument(node ast.Node) ([]byte, error) { + var err error + node, err = d.resolveAlias(node) + if err != nil { + return nil, err + } + doc := node.String() + last := d.lastNode(node) + if last != nil && last.Type() == ast.LiteralType { + doc += "\n" + } + return []byte(doc), nil +} + +func (d *Decoder) unmarshalableText(node ast.Node) ([]byte, bool, error) { + var err error + node, err = d.resolveAlias(node) + if err != nil { + return nil, false, err + } + if node.Type() == ast.AnchorType { + node = node.(*ast.AnchorNode).Value + } + switch n := node.(type) { + case *ast.StringNode: + return []byte(n.Value), true, nil + case *ast.LiteralNode: + return []byte(n.Value.GetToken().Value), true, nil + default: + scalar, ok := n.(ast.ScalarNode) + if ok { + return []byte(fmt.Sprint(scalar.GetValue())), true, nil + } + } + return nil, false, nil +} + +type jsonUnmarshaler interface { + UnmarshalJSON([]byte) error +} + +func (d *Decoder) existsTypeInCustomUnmarshalerMap(t reflect.Type) bool { + if _, exists := d.customUnmarshalerMap[t]; exists { + return true + } + + globalCustomUnmarshalerMu.Lock() + defer globalCustomUnmarshalerMu.Unlock() + if _, exists := globalCustomUnmarshalerMap[t]; exists { + return true + } + return false +} + +func (d *Decoder) unmarshalerFromCustomUnmarshalerMap(t reflect.Type) (func(interface{}, []byte) error, bool) { + if unmarshaler, exists := d.customUnmarshalerMap[t]; exists { + return unmarshaler, exists + } + + globalCustomUnmarshalerMu.Lock() + defer globalCustomUnmarshalerMu.Unlock() + if unmarshaler, exists := globalCustomUnmarshalerMap[t]; exists { + return unmarshaler, exists + } + return nil, false +} + +func (d *Decoder) canDecodeByUnmarshaler(dst reflect.Value) bool { + ptrValue := dst.Addr() + if d.existsTypeInCustomUnmarshalerMap(ptrValue.Type()) { + return true + } + iface := ptrValue.Interface() + switch iface.(type) { + case BytesUnmarshalerContext: + return true + case BytesUnmarshaler: + return true + case InterfaceUnmarshalerContext: + return true + case InterfaceUnmarshaler: + return true + case *time.Time: + return true + case *time.Duration: + return true + case encoding.TextUnmarshaler: + return true + case jsonUnmarshaler: + return d.useJSONUnmarshaler + } + return false +} + +func (d *Decoder) decodeByUnmarshaler(ctx context.Context, dst reflect.Value, src ast.Node) error { + ptrValue := dst.Addr() + if unmarshaler, exists := d.unmarshalerFromCustomUnmarshalerMap(ptrValue.Type()); exists { + b, err := d.unmarshalableDocument(src) + if err != nil { + return errors.Wrapf(err, "failed to UnmarshalYAML") + } + if err := unmarshaler(ptrValue.Interface(), b); err != nil { + return errors.Wrapf(err, "failed to UnmarshalYAML") + } + return nil + } + iface := ptrValue.Interface() + + if unmarshaler, ok := iface.(BytesUnmarshalerContext); ok { + b, err := d.unmarshalableDocument(src) + if err != nil { + return errors.Wrapf(err, "failed to UnmarshalYAML") + } + if err := unmarshaler.UnmarshalYAML(ctx, b); err != nil { + return errors.Wrapf(err, "failed to UnmarshalYAML") + } + return nil + } + + if unmarshaler, ok := iface.(BytesUnmarshaler); ok { + b, err := d.unmarshalableDocument(src) + if err != nil { + return errors.Wrapf(err, "failed to UnmarshalYAML") + } + if err := unmarshaler.UnmarshalYAML(b); err != nil { + return errors.Wrapf(err, "failed to UnmarshalYAML") + } + return nil + } + + if unmarshaler, ok := iface.(InterfaceUnmarshalerContext); ok { + if err := unmarshaler.UnmarshalYAML(ctx, func(v interface{}) error { + rv := reflect.ValueOf(v) + if rv.Type().Kind() != reflect.Ptr { + return errors.ErrDecodeRequiredPointerType + } + if err := d.decodeValue(ctx, rv.Elem(), src); err != nil { + return errors.Wrapf(err, "failed to decode value") + } + return nil + }); err != nil { + return errors.Wrapf(err, "failed to UnmarshalYAML") + } + return nil + } + + if unmarshaler, ok := iface.(InterfaceUnmarshaler); ok { + if err := unmarshaler.UnmarshalYAML(func(v interface{}) error { + rv := reflect.ValueOf(v) + if rv.Type().Kind() != reflect.Ptr { + return errors.ErrDecodeRequiredPointerType + } + if err := d.decodeValue(ctx, rv.Elem(), src); err != nil { + return errors.Wrapf(err, "failed to decode value") + } + return nil + }); err != nil { + return errors.Wrapf(err, "failed to UnmarshalYAML") + } + return nil + } + + if _, ok := iface.(*time.Time); ok { + return d.decodeTime(ctx, dst, src) + } + + if _, ok := iface.(*time.Duration); ok { + return d.decodeDuration(ctx, dst, src) + } + + if unmarshaler, isText := iface.(encoding.TextUnmarshaler); isText { + b, ok, err := d.unmarshalableText(src) + if err != nil { + return errors.Wrapf(err, "failed to UnmarshalText") + } + if ok { + if err := unmarshaler.UnmarshalText(b); err != nil { + return errors.Wrapf(err, "failed to UnmarshalText") + } + return nil + } + } + + if d.useJSONUnmarshaler { + if unmarshaler, ok := iface.(jsonUnmarshaler); ok { + b, err := d.unmarshalableDocument(src) + if err != nil { + return errors.Wrapf(err, "failed to UnmarshalJSON") + } + jsonBytes, err := YAMLToJSON(b) + if err != nil { + return errors.Wrapf(err, "failed to convert yaml to json") + } + jsonBytes = bytes.TrimRight(jsonBytes, "\n") + if err := unmarshaler.UnmarshalJSON(jsonBytes); err != nil { + return errors.Wrapf(err, "failed to UnmarshalJSON") + } + return nil + } + } + + return xerrors.Errorf("does not implemented Unmarshaler") +} + +var ( + astNodeType = reflect.TypeOf((*ast.Node)(nil)).Elem() +) + +func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.Node) error { + if src.Type() == ast.AnchorType { + anchorName := src.(*ast.AnchorNode).Name.GetToken().Value + if _, exists := d.anchorValueMap[anchorName]; !exists { + d.anchorValueMap[anchorName] = dst + } + } + if d.canDecodeByUnmarshaler(dst) { + if err := d.decodeByUnmarshaler(ctx, dst, src); err != nil { + return errors.Wrapf(err, "failed to decode by unmarshaler") + } + return nil + } + valueType := dst.Type() + switch valueType.Kind() { + case reflect.Ptr: + if dst.IsNil() { + return nil + } + if src.Type() == ast.NullType { + // set nil value to pointer + dst.Set(reflect.Zero(valueType)) + return nil + } + v := d.createDecodableValue(dst.Type()) + if err := d.decodeValue(ctx, v, src); err != nil { + return errors.Wrapf(err, "failed to decode ptr value") + } + dst.Set(d.castToAssignableValue(v, dst.Type())) + case reflect.Interface: + if dst.Type() == astNodeType { + dst.Set(reflect.ValueOf(src)) + return nil + } + v := reflect.ValueOf(d.nodeToValue(src)) + if v.IsValid() { + dst.Set(v) + } + case reflect.Map: + return d.decodeMap(ctx, dst, src) + case reflect.Array: + return d.decodeArray(ctx, dst, src) + case reflect.Slice: + if mapSlice, ok := dst.Addr().Interface().(*MapSlice); ok { + return d.decodeMapSlice(ctx, mapSlice, src) + } + return d.decodeSlice(ctx, dst, src) + case reflect.Struct: + if mapItem, ok := dst.Addr().Interface().(*MapItem); ok { + return d.decodeMapItem(ctx, mapItem, src) + } + return d.decodeStruct(ctx, dst, src) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + v := d.nodeToValue(src) + switch vv := v.(type) { + case int64: + if !dst.OverflowInt(vv) { + dst.SetInt(vv) + return nil + } + case uint64: + if vv <= math.MaxInt64 && !dst.OverflowInt(int64(vv)) { + dst.SetInt(int64(vv)) + return nil + } + case float64: + if vv <= math.MaxInt64 && !dst.OverflowInt(int64(vv)) { + dst.SetInt(int64(vv)) + return nil + } + default: + return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken()) + } + return errOverflow(valueType, fmt.Sprint(v)) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + v := d.nodeToValue(src) + switch vv := v.(type) { + case int64: + if 0 <= vv && !dst.OverflowUint(uint64(vv)) { + dst.SetUint(uint64(vv)) + return nil + } + case uint64: + if !dst.OverflowUint(vv) { + dst.SetUint(vv) + return nil + } + case float64: + if 0 <= vv && vv <= math.MaxUint64 && !dst.OverflowUint(uint64(vv)) { + dst.SetUint(uint64(vv)) + return nil + } + default: + return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken()) + } + return errOverflow(valueType, fmt.Sprint(v)) + } + v := reflect.ValueOf(d.nodeToValue(src)) + if v.IsValid() { + convertedValue, err := d.convertValue(v, dst.Type(), src) + if err != nil { + return errors.Wrapf(err, "failed to convert value") + } + dst.Set(convertedValue) + } + return nil +} + +func (d *Decoder) createDecodableValue(typ reflect.Type) reflect.Value { + for { + if typ.Kind() == reflect.Ptr { + typ = typ.Elem() + continue + } + break + } + return reflect.New(typ).Elem() +} + +func (d *Decoder) castToAssignableValue(value reflect.Value, target reflect.Type) reflect.Value { + if target.Kind() != reflect.Ptr { + return value + } + maxTryCount := 5 + tryCount := 0 + for { + if tryCount > maxTryCount { + return value + } + if value.Type().AssignableTo(target) { + break + } + value = value.Addr() + tryCount++ + } + return value +} + +func (d *Decoder) createDecodedNewValue( + ctx context.Context, typ reflect.Type, defaultVal reflect.Value, node ast.Node, +) (reflect.Value, error) { + if node.Type() == ast.AliasType { + aliasName := node.(*ast.AliasNode).Value.GetToken().Value + newValue := d.anchorValueMap[aliasName] + if newValue.IsValid() { + return newValue, nil + } + } + if node.Type() == ast.NullType { + return reflect.Zero(typ), nil + } + newValue := d.createDecodableValue(typ) + for defaultVal.Kind() == reflect.Ptr { + defaultVal = defaultVal.Elem() + } + if defaultVal.IsValid() && defaultVal.Type().AssignableTo(newValue.Type()) { + newValue.Set(defaultVal) + } + if err := d.decodeValue(ctx, newValue, node); err != nil { + return newValue, errors.Wrapf(err, "failed to decode value") + } + return newValue, nil +} + +func (d *Decoder) keyToNodeMap(node ast.Node, ignoreMergeKey bool, getKeyOrValueNode func(*ast.MapNodeIter) ast.Node) (map[string]ast.Node, error) { + mapNode, err := d.getMapNode(node) + if err != nil { + return nil, errors.Wrapf(err, "failed to get map node") + } + keyMap := map[string]struct{}{} + keyToNodeMap := map[string]ast.Node{} + if mapNode == nil { + return keyToNodeMap, nil + } + mapIter := mapNode.MapRange() + for mapIter.Next() { + keyNode := mapIter.Key() + if keyNode.Type() == ast.MergeKeyType { + if ignoreMergeKey { + continue + } + mergeMap, err := d.keyToNodeMap(mapIter.Value(), ignoreMergeKey, getKeyOrValueNode) + if err != nil { + return nil, errors.Wrapf(err, "failed to get keyToNodeMap by MergeKey node") + } + for k, v := range mergeMap { + if err := d.validateDuplicateKey(keyMap, k, v); err != nil { + return nil, errors.Wrapf(err, "invalid struct key") + } + keyToNodeMap[k] = v + } + } else { + key, ok := d.nodeToValue(keyNode).(string) + if !ok { + return nil, errors.Wrapf(err, "failed to decode map key") + } + if err := d.validateDuplicateKey(keyMap, key, keyNode); err != nil { + return nil, errors.Wrapf(err, "invalid struct key") + } + keyToNodeMap[key] = getKeyOrValueNode(mapIter) + } + } + return keyToNodeMap, nil +} + +func (d *Decoder) keyToKeyNodeMap(node ast.Node, ignoreMergeKey bool) (map[string]ast.Node, error) { + m, err := d.keyToNodeMap(node, ignoreMergeKey, func(nodeMap *ast.MapNodeIter) ast.Node { return nodeMap.Key() }) + if err != nil { + return nil, errors.Wrapf(err, "failed to get keyToNodeMap") + } + return m, nil +} + +func (d *Decoder) keyToValueNodeMap(node ast.Node, ignoreMergeKey bool) (map[string]ast.Node, error) { + m, err := d.keyToNodeMap(node, ignoreMergeKey, func(nodeMap *ast.MapNodeIter) ast.Node { return nodeMap.Value() }) + if err != nil { + return nil, errors.Wrapf(err, "failed to get keyToNodeMap") + } + return m, nil +} + +func (d *Decoder) setDefaultValueIfConflicted(v reflect.Value, fieldMap StructFieldMap) error { + typ := v.Type() + if typ.Kind() != reflect.Struct { + return nil + } + embeddedStructFieldMap, err := structFieldMap(typ) + if err != nil { + return errors.Wrapf(err, "failed to get struct field map by embedded type") + } + for i := 0; i < typ.NumField(); i++ { + field := typ.Field(i) + if isIgnoredStructField(field) { + continue + } + structField := embeddedStructFieldMap[field.Name] + if !fieldMap.isIncludedRenderName(structField.RenderName) { + continue + } + // if declared same key name, set default value + fieldValue := v.Field(i) + if fieldValue.CanSet() { + fieldValue.Set(reflect.Zero(fieldValue.Type())) + } + } + return nil +} + +// This is a subset of the formats allowed by the regular expression +// defined at http://yaml.org/type/timestamp.html. +var allowedTimestampFormats = []string{ + "2006-1-2T15:4:5.999999999Z07:00", // RCF3339Nano with short date fields. + "2006-1-2t15:4:5.999999999Z07:00", // RFC3339Nano with short date fields and lower-case "t". + "2006-1-2 15:4:5.999999999", // space separated with no time zone + "2006-1-2", // date only +} + +func (d *Decoder) castToTime(src ast.Node) (time.Time, error) { + if src == nil { + return time.Time{}, nil + } + v := d.nodeToValue(src) + if t, ok := v.(time.Time); ok { + return t, nil + } + s, ok := v.(string) + if !ok { + return time.Time{}, errTypeMismatch(reflect.TypeOf(time.Time{}), reflect.TypeOf(v), src.GetToken()) + } + for _, format := range allowedTimestampFormats { + t, err := time.Parse(format, s) + if err != nil { + // invalid format + continue + } + return t, nil + } + return time.Time{}, nil +} + +func (d *Decoder) decodeTime(ctx context.Context, dst reflect.Value, src ast.Node) error { + t, err := d.castToTime(src) + if err != nil { + return errors.Wrapf(err, "failed to convert to time") + } + dst.Set(reflect.ValueOf(t)) + return nil +} + +func (d *Decoder) castToDuration(src ast.Node) (time.Duration, error) { + if src == nil { + return 0, nil + } + v := d.nodeToValue(src) + if t, ok := v.(time.Duration); ok { + return t, nil + } + s, ok := v.(string) + if !ok { + return 0, errTypeMismatch(reflect.TypeOf(time.Duration(0)), reflect.TypeOf(v), src.GetToken()) + } + t, err := time.ParseDuration(s) + if err != nil { + return 0, errors.Wrapf(err, "failed to parse duration") + } + return t, nil +} + +func (d *Decoder) decodeDuration(ctx context.Context, dst reflect.Value, src ast.Node) error { + t, err := d.castToDuration(src) + if err != nil { + return errors.Wrapf(err, "failed to convert to duration") + } + dst.Set(reflect.ValueOf(t)) + return nil +} + +// getMergeAliasName support single alias only +func (d *Decoder) getMergeAliasName(src ast.Node) string { + mapNode, err := d.getMapNode(src) + if err != nil { + return "" + } + if mapNode == nil { + return "" + } + mapIter := mapNode.MapRange() + for mapIter.Next() { + key := mapIter.Key() + value := mapIter.Value() + if key.Type() == ast.MergeKeyType && value.Type() == ast.AliasType { + return value.(*ast.AliasNode).Value.GetToken().Value + } + } + return "" +} + +func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.Node) error { + if src == nil { + return nil + } + structType := dst.Type() + srcValue := reflect.ValueOf(src) + srcType := srcValue.Type() + if srcType.Kind() == reflect.Ptr { + srcType = srcType.Elem() + srcValue = srcValue.Elem() + } + if structType == srcType { + // dst value implements ast.Node + dst.Set(srcValue) + return nil + } + structFieldMap, err := structFieldMap(structType) + if err != nil { + return errors.Wrapf(err, "failed to create struct field map") + } + ignoreMergeKey := structFieldMap.hasMergeProperty() + keyToNodeMap, err := d.keyToValueNodeMap(src, ignoreMergeKey) + if err != nil { + return errors.Wrapf(err, "failed to get keyToValueNodeMap") + } + var unknownFields map[string]ast.Node + if d.disallowUnknownField { + unknownFields, err = d.keyToKeyNodeMap(src, ignoreMergeKey) + if err != nil { + return errors.Wrapf(err, "failed to get keyToKeyNodeMap") + } + } + + aliasName := d.getMergeAliasName(src) + var foundErr error + + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if isIgnoredStructField(field) { + continue + } + structField := structFieldMap[field.Name] + if structField.IsInline { + fieldValue := dst.FieldByName(field.Name) + if structField.IsAutoAlias { + if aliasName != "" { + newFieldValue := d.anchorValueMap[aliasName] + if newFieldValue.IsValid() { + fieldValue.Set(d.castToAssignableValue(newFieldValue, fieldValue.Type())) + } + } + continue + } + if !fieldValue.CanSet() { + return xerrors.Errorf("cannot set embedded type as unexported field %s.%s", field.PkgPath, field.Name) + } + if fieldValue.Type().Kind() == reflect.Ptr && src.Type() == ast.NullType { + // set nil value to pointer + fieldValue.Set(reflect.Zero(fieldValue.Type())) + continue + } + mapNode := ast.Mapping(nil, false) + for k, v := range keyToNodeMap { + key := &ast.StringNode{BaseNode: &ast.BaseNode{}, Value: k} + mapNode.Values = append(mapNode.Values, ast.MappingValue(nil, key, v)) + } + newFieldValue, err := d.createDecodedNewValue(ctx, fieldValue.Type(), fieldValue, mapNode) + if d.disallowUnknownField { + if err := d.deleteStructKeys(fieldValue.Type(), unknownFields); err != nil { + return errors.Wrapf(err, "cannot delete struct keys") + } + } + + if err != nil { + if foundErr != nil { + continue + } + var te *errors.TypeError + if xerrors.As(err, &te) { + if te.StructFieldName != nil { + fieldName := fmt.Sprintf("%s.%s", structType.Name(), *te.StructFieldName) + te.StructFieldName = &fieldName + } else { + fieldName := fmt.Sprintf("%s.%s", structType.Name(), field.Name) + te.StructFieldName = &fieldName + } + foundErr = te + continue + } else { + foundErr = err + } + continue + } + d.setDefaultValueIfConflicted(newFieldValue, structFieldMap) + fieldValue.Set(d.castToAssignableValue(newFieldValue, fieldValue.Type())) + continue + } + v, exists := keyToNodeMap[structField.RenderName] + if !exists { + continue + } + delete(unknownFields, structField.RenderName) + fieldValue := dst.FieldByName(field.Name) + if fieldValue.Type().Kind() == reflect.Ptr && src.Type() == ast.NullType { + // set nil value to pointer + fieldValue.Set(reflect.Zero(fieldValue.Type())) + continue + } + newFieldValue, err := d.createDecodedNewValue(ctx, fieldValue.Type(), fieldValue, v) + if err != nil { + if foundErr != nil { + continue + } + var te *errors.TypeError + if xerrors.As(err, &te) { + fieldName := fmt.Sprintf("%s.%s", structType.Name(), field.Name) + te.StructFieldName = &fieldName + foundErr = te + } else { + foundErr = err + } + continue + } + fieldValue.Set(d.castToAssignableValue(newFieldValue, fieldValue.Type())) + } + if foundErr != nil { + return errors.Wrapf(foundErr, "failed to decode value") + } + + // Ignore unknown fields when parsing an inline struct (recognized by a nil token). + // Unknown fields are expected (they could be fields from the parent struct). + if len(unknownFields) != 0 && d.disallowUnknownField && src.GetToken() != nil { + for key, node := range unknownFields { + return errUnknownField(fmt.Sprintf(`unknown field "%s"`, key), node.GetToken()) + } + } + + if d.validator != nil { + if err := d.validator.Struct(dst.Interface()); err != nil { + ev := reflect.ValueOf(err) + if ev.Type().Kind() == reflect.Slice { + for i := 0; i < ev.Len(); i++ { + fieldErr, ok := ev.Index(i).Interface().(FieldError) + if !ok { + continue + } + fieldName := fieldErr.StructField() + structField, exists := structFieldMap[fieldName] + if !exists { + continue + } + node, exists := keyToNodeMap[structField.RenderName] + if exists { + // TODO: to make FieldError message cutomizable + return errors.ErrSyntax(fmt.Sprintf("%s", err), node.GetToken()) + } else if t := src.GetToken(); t != nil && t.Prev != nil && t.Prev.Prev != nil { + // A missing required field will not be in the keyToNodeMap + // the error needs to be associated with the parent of the source node + return errors.ErrSyntax(fmt.Sprintf("%s", err), t.Prev.Prev) + } + } + } + return err + } + } + return nil +} + +func (d *Decoder) decodeArray(ctx context.Context, dst reflect.Value, src ast.Node) error { + arrayNode, err := d.getArrayNode(src) + if err != nil { + return errors.Wrapf(err, "failed to get array node") + } + if arrayNode == nil { + return nil + } + iter := arrayNode.ArrayRange() + arrayValue := reflect.New(dst.Type()).Elem() + arrayType := dst.Type() + elemType := arrayType.Elem() + idx := 0 + + var foundErr error + for iter.Next() { + v := iter.Value() + if elemType.Kind() == reflect.Ptr && v.Type() == ast.NullType { + // set nil value to pointer + arrayValue.Index(idx).Set(reflect.Zero(elemType)) + } else { + dstValue, err := d.createDecodedNewValue(ctx, elemType, reflect.Value{}, v) + if err != nil { + if foundErr == nil { + foundErr = err + } + continue + } else { + arrayValue.Index(idx).Set(d.castToAssignableValue(dstValue, elemType)) + } + } + idx++ + } + dst.Set(arrayValue) + if foundErr != nil { + return errors.Wrapf(foundErr, "failed to decode value") + } + return nil +} + +func (d *Decoder) decodeSlice(ctx context.Context, dst reflect.Value, src ast.Node) error { + arrayNode, err := d.getArrayNode(src) + if err != nil { + return errors.Wrapf(err, "failed to get array node") + } + if arrayNode == nil { + return nil + } + iter := arrayNode.ArrayRange() + sliceType := dst.Type() + sliceValue := reflect.MakeSlice(sliceType, 0, iter.Len()) + elemType := sliceType.Elem() + + var foundErr error + for iter.Next() { + v := iter.Value() + if elemType.Kind() == reflect.Ptr && v.Type() == ast.NullType { + // set nil value to pointer + sliceValue = reflect.Append(sliceValue, reflect.Zero(elemType)) + continue + } + dstValue, err := d.createDecodedNewValue(ctx, elemType, reflect.Value{}, v) + if err != nil { + if foundErr == nil { + foundErr = err + } + continue + } + sliceValue = reflect.Append(sliceValue, d.castToAssignableValue(dstValue, elemType)) + } + dst.Set(sliceValue) + if foundErr != nil { + return errors.Wrapf(foundErr, "failed to decode value") + } + return nil +} + +func (d *Decoder) decodeMapItem(ctx context.Context, dst *MapItem, src ast.Node) error { + mapNode, err := d.getMapNode(src) + if err != nil { + return errors.Wrapf(err, "failed to get map node") + } + if mapNode == nil { + return nil + } + mapIter := mapNode.MapRange() + if !mapIter.Next() { + return nil + } + key := mapIter.Key() + value := mapIter.Value() + if key.Type() == ast.MergeKeyType { + if err := d.decodeMapItem(ctx, dst, value); err != nil { + return errors.Wrapf(err, "failed to decode map with merge key") + } + return nil + } + *dst = MapItem{ + Key: d.nodeToValue(key), + Value: d.nodeToValue(value), + } + return nil +} + +func (d *Decoder) validateDuplicateKey(keyMap map[string]struct{}, key interface{}, keyNode ast.Node) error { + k, ok := key.(string) + if !ok { + return nil + } + if d.disallowDuplicateKey { + if _, exists := keyMap[k]; exists { + return errDuplicateKey(fmt.Sprintf(`duplicate key "%s"`, k), keyNode.GetToken()) + } + } + keyMap[k] = struct{}{} + return nil +} + +func (d *Decoder) decodeMapSlice(ctx context.Context, dst *MapSlice, src ast.Node) error { + mapNode, err := d.getMapNode(src) + if err != nil { + return errors.Wrapf(err, "failed to get map node") + } + if mapNode == nil { + return nil + } + mapSlice := MapSlice{} + mapIter := mapNode.MapRange() + keyMap := map[string]struct{}{} + for mapIter.Next() { + key := mapIter.Key() + value := mapIter.Value() + if key.Type() == ast.MergeKeyType { + var m MapSlice + if err := d.decodeMapSlice(ctx, &m, value); err != nil { + return errors.Wrapf(err, "failed to decode map with merge key") + } + for _, v := range m { + if err := d.validateDuplicateKey(keyMap, v.Key, value); err != nil { + return errors.Wrapf(err, "invalid map key") + } + mapSlice = append(mapSlice, v) + } + continue + } + k := d.nodeToValue(key) + if err := d.validateDuplicateKey(keyMap, k, key); err != nil { + return errors.Wrapf(err, "invalid map key") + } + mapSlice = append(mapSlice, MapItem{ + Key: k, + Value: d.nodeToValue(value), + }) + } + *dst = mapSlice + return nil +} + +func (d *Decoder) decodeMap(ctx context.Context, dst reflect.Value, src ast.Node) error { + mapNode, err := d.getMapNode(src) + if err != nil { + return errors.Wrapf(err, "failed to get map node") + } + if mapNode == nil { + return nil + } + mapType := dst.Type() + mapValue := reflect.MakeMap(mapType) + keyType := mapValue.Type().Key() + valueType := mapValue.Type().Elem() + mapIter := mapNode.MapRange() + keyMap := map[string]struct{}{} + var foundErr error + for mapIter.Next() { + key := mapIter.Key() + value := mapIter.Value() + if key.Type() == ast.MergeKeyType { + if err := d.decodeMap(ctx, dst, value); err != nil { + return errors.Wrapf(err, "failed to decode map with merge key") + } + iter := dst.MapRange() + for iter.Next() { + if err := d.validateDuplicateKey(keyMap, iter.Key(), value); err != nil { + return errors.Wrapf(err, "invalid map key") + } + mapValue.SetMapIndex(iter.Key(), iter.Value()) + } + continue + } + k := reflect.ValueOf(d.nodeToValue(key)) + if k.IsValid() && k.Type().ConvertibleTo(keyType) { + k = k.Convert(keyType) + } + if k.IsValid() { + if err := d.validateDuplicateKey(keyMap, k.Interface(), key); err != nil { + return errors.Wrapf(err, "invalid map key") + } + } + if valueType.Kind() == reflect.Ptr && value.Type() == ast.NullType { + // set nil value to pointer + mapValue.SetMapIndex(k, reflect.Zero(valueType)) + continue + } + dstValue, err := d.createDecodedNewValue(ctx, valueType, reflect.Value{}, value) + if err != nil { + if foundErr == nil { + foundErr = err + } + } + if !k.IsValid() { + // expect nil key + mapValue.SetMapIndex(d.createDecodableValue(keyType), d.castToAssignableValue(dstValue, valueType)) + continue + } + mapValue.SetMapIndex(k, d.castToAssignableValue(dstValue, valueType)) + } + dst.Set(mapValue) + if foundErr != nil { + return errors.Wrapf(foundErr, "failed to decode value") + } + return nil +} + +func (d *Decoder) fileToReader(file string) (io.Reader, error) { + reader, err := os.Open(file) + if err != nil { + return nil, errors.Wrapf(err, "failed to open file") + } + return reader, nil +} + +func (d *Decoder) isYAMLFile(file string) bool { + ext := filepath.Ext(file) + if ext == ".yml" { + return true + } + if ext == ".yaml" { + return true + } + return false +} + +func (d *Decoder) readersUnderDir(dir string) ([]io.Reader, error) { + pattern := fmt.Sprintf("%s/*", dir) + matches, err := filepath.Glob(pattern) + if err != nil { + return nil, errors.Wrapf(err, "failed to get files by %s", pattern) + } + readers := []io.Reader{} + for _, match := range matches { + if !d.isYAMLFile(match) { + continue + } + reader, err := d.fileToReader(match) + if err != nil { + return nil, errors.Wrapf(err, "failed to get reader") + } + readers = append(readers, reader) + } + return readers, nil +} + +func (d *Decoder) readersUnderDirRecursive(dir string) ([]io.Reader, error) { + readers := []io.Reader{} + if err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if !d.isYAMLFile(path) { + return nil + } + reader, err := d.fileToReader(path) + if err != nil { + return errors.Wrapf(err, "failed to get reader") + } + readers = append(readers, reader) + return nil + }); err != nil { + return nil, errors.Wrapf(err, "interrupt walk in %s", dir) + } + return readers, nil +} + +func (d *Decoder) resolveReference() error { + for _, opt := range d.opts { + if err := opt(d); err != nil { + return errors.Wrapf(err, "failed to exec option") + } + } + for _, file := range d.referenceFiles { + reader, err := d.fileToReader(file) + if err != nil { + return errors.Wrapf(err, "failed to get reader") + } + d.referenceReaders = append(d.referenceReaders, reader) + } + for _, dir := range d.referenceDirs { + if !d.isRecursiveDir { + readers, err := d.readersUnderDir(dir) + if err != nil { + return errors.Wrapf(err, "failed to get readers from under the %s", dir) + } + d.referenceReaders = append(d.referenceReaders, readers...) + } else { + readers, err := d.readersUnderDirRecursive(dir) + if err != nil { + return errors.Wrapf(err, "failed to get readers from under the %s", dir) + } + d.referenceReaders = append(d.referenceReaders, readers...) + } + } + for _, reader := range d.referenceReaders { + bytes, err := ioutil.ReadAll(reader) + if err != nil { + return errors.Wrapf(err, "failed to read buffer") + } + + // assign new anchor definition to anchorMap + if _, err := d.parse(bytes); err != nil { + return errors.Wrapf(err, "failed to decode") + } + } + d.isResolvedReference = true + return nil +} + +func (d *Decoder) parse(bytes []byte) (*ast.File, error) { + var parseMode parser.Mode + if d.toCommentMap != nil { + parseMode = parser.ParseComments + } + f, err := parser.ParseBytes(bytes, parseMode) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse yaml") + } + normalizedFile := &ast.File{} + for _, doc := range f.Docs { + // try to decode ast.Node to value and map anchor value to anchorMap + if v := d.nodeToValue(doc.Body); v != nil { + normalizedFile.Docs = append(normalizedFile.Docs, doc) + } + } + return normalizedFile, nil +} + +func (d *Decoder) isInitialized() bool { + return d.parsedFile != nil +} + +func (d *Decoder) decodeInit() error { + if !d.isResolvedReference { + if err := d.resolveReference(); err != nil { + return errors.Wrapf(err, "failed to resolve reference") + } + } + var buf bytes.Buffer + if _, err := io.Copy(&buf, d.reader); err != nil { + return errors.Wrapf(err, "failed to copy from reader") + } + file, err := d.parse(buf.Bytes()) + if err != nil { + return errors.Wrapf(err, "failed to decode") + } + d.parsedFile = file + return nil +} + +func (d *Decoder) decode(ctx context.Context, v reflect.Value) error { + if len(d.parsedFile.Docs) <= d.streamIndex { + return io.EOF + } + body := d.parsedFile.Docs[d.streamIndex].Body + if body == nil { + return nil + } + if err := d.decodeValue(ctx, v.Elem(), body); err != nil { + return errors.Wrapf(err, "failed to decode value") + } + d.streamIndex++ + return nil +} + +// Decode reads the next YAML-encoded value from its input +// and stores it in the value pointed to by v. +// +// See the documentation for Unmarshal for details about the +// conversion of YAML into a Go value. +func (d *Decoder) Decode(v interface{}) error { + return d.DecodeContext(context.Background(), v) +} + +// DecodeContext reads the next YAML-encoded value from its input +// and stores it in the value pointed to by v with context.Context. +func (d *Decoder) DecodeContext(ctx context.Context, v interface{}) error { + rv := reflect.ValueOf(v) + if rv.Type().Kind() != reflect.Ptr { + return errors.ErrDecodeRequiredPointerType + } + if d.isInitialized() { + if err := d.decode(ctx, rv); err != nil { + if err == io.EOF { + return err + } + return errors.Wrapf(err, "failed to decode") + } + return nil + } + if err := d.decodeInit(); err != nil { + return errors.Wrapf(err, "failed to decodeInit") + } + if err := d.decode(ctx, rv); err != nil { + if err == io.EOF { + return err + } + return errors.Wrapf(err, "failed to decode") + } + return nil +} + +// DecodeFromNode decodes node into the value pointed to by v. +func (d *Decoder) DecodeFromNode(node ast.Node, v interface{}) error { + return d.DecodeFromNodeContext(context.Background(), node, v) +} + +// DecodeFromNodeContext decodes node into the value pointed to by v with context.Context. +func (d *Decoder) DecodeFromNodeContext(ctx context.Context, node ast.Node, v interface{}) error { + rv := reflect.ValueOf(v) + if rv.Type().Kind() != reflect.Ptr { + return errors.ErrDecodeRequiredPointerType + } + if !d.isInitialized() { + if err := d.decodeInit(); err != nil { + return errors.Wrapf(err, "failed to decodInit") + } + } + // resolve references to the anchor on the same file + d.nodeToValue(node) + if err := d.decodeValue(ctx, rv.Elem(), node); err != nil { + return errors.Wrapf(err, "failed to decode value") + } + return nil +} diff --git a/vendor/github.com/goccy/go-yaml/encode.go b/vendor/github.com/goccy/go-yaml/encode.go new file mode 100644 index 000000000..7d8d81e03 --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/encode.go @@ -0,0 +1,871 @@ +package yaml + +import ( + "context" + "encoding" + "fmt" + "io" + "math" + "reflect" + "sort" + "strconv" + "strings" + "time" + + "github.com/goccy/go-yaml/ast" + "github.com/goccy/go-yaml/internal/errors" + "github.com/goccy/go-yaml/parser" + "github.com/goccy/go-yaml/printer" + "github.com/goccy/go-yaml/token" + "golang.org/x/xerrors" +) + +const ( + // DefaultIndentSpaces default number of space for indent + DefaultIndentSpaces = 2 +) + +// Encoder writes YAML values to an output stream. +type Encoder struct { + writer io.Writer + opts []EncodeOption + indent int + indentSequence bool + singleQuote bool + isFlowStyle bool + isJSONStyle bool + useJSONMarshaler bool + anchorCallback func(*ast.AnchorNode, interface{}) error + anchorPtrToNameMap map[uintptr]string + customMarshalerMap map[reflect.Type]func(interface{}) ([]byte, error) + useLiteralStyleIfMultiline bool + commentMap map[*Path][]*Comment + written bool + + line int + column int + offset int + indentNum int + indentLevel int +} + +// NewEncoder returns a new encoder that writes to w. +// The Encoder should be closed after use to flush all data to w. +func NewEncoder(w io.Writer, opts ...EncodeOption) *Encoder { + return &Encoder{ + writer: w, + opts: opts, + indent: DefaultIndentSpaces, + anchorPtrToNameMap: map[uintptr]string{}, + customMarshalerMap: map[reflect.Type]func(interface{}) ([]byte, error){}, + line: 1, + column: 1, + offset: 0, + } +} + +// Close closes the encoder by writing any remaining data. +// It does not write a stream terminating string "...". +func (e *Encoder) Close() error { + return nil +} + +// Encode writes the YAML encoding of v to the stream. +// If multiple items are encoded to the stream, +// the second and subsequent document will be preceded with a "---" document separator, +// but the first will not. +// +// See the documentation for Marshal for details about the conversion of Go values to YAML. +func (e *Encoder) Encode(v interface{}) error { + return e.EncodeContext(context.Background(), v) +} + +// EncodeContext writes the YAML encoding of v to the stream with context.Context. +func (e *Encoder) EncodeContext(ctx context.Context, v interface{}) error { + node, err := e.EncodeToNodeContext(ctx, v) + if err != nil { + return errors.Wrapf(err, "failed to encode to node") + } + if err := e.setCommentByCommentMap(node); err != nil { + return errors.Wrapf(err, "failed to set comment by comment map") + } + if !e.written { + e.written = true + } else { + // write document separator + e.writer.Write([]byte("---\n")) + } + var p printer.Printer + e.writer.Write(p.PrintNode(node)) + return nil +} + +// EncodeToNode convert v to ast.Node. +func (e *Encoder) EncodeToNode(v interface{}) (ast.Node, error) { + return e.EncodeToNodeContext(context.Background(), v) +} + +// EncodeToNodeContext convert v to ast.Node with context.Context. +func (e *Encoder) EncodeToNodeContext(ctx context.Context, v interface{}) (ast.Node, error) { + for _, opt := range e.opts { + if err := opt(e); err != nil { + return nil, errors.Wrapf(err, "failed to run option for encoder") + } + } + node, err := e.encodeValue(ctx, reflect.ValueOf(v), 1) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode value") + } + return node, nil +} + +func (e *Encoder) setCommentByCommentMap(node ast.Node) error { + if e.commentMap == nil { + return nil + } + for path, comments := range e.commentMap { + n, err := path.FilterNode(node) + if err != nil { + return errors.Wrapf(err, "failed to filter node") + } + if n == nil { + continue + } + for _, comment := range comments { + commentTokens := []*token.Token{} + for _, text := range comment.Texts { + commentTokens = append(commentTokens, token.New(text, text, nil)) + } + commentGroup := ast.CommentGroup(commentTokens) + switch comment.Position { + case CommentHeadPosition: + if err := e.setHeadComment(node, n, commentGroup); err != nil { + return errors.Wrapf(err, "failed to set head comment") + } + case CommentLinePosition: + if err := e.setLineComment(node, n, commentGroup); err != nil { + return errors.Wrapf(err, "failed to set line comment") + } + case CommentFootPosition: + if err := e.setFootComment(node, n, commentGroup); err != nil { + return errors.Wrapf(err, "failed to set foot comment") + } + default: + return ErrUnknownCommentPositionType + } + } + } + return nil +} + +func (e *Encoder) setHeadComment(node ast.Node, filtered ast.Node, comment *ast.CommentGroupNode) error { + parent := ast.Parent(node, filtered) + if parent == nil { + return ErrUnsupportedHeadPositionType(node) + } + switch p := parent.(type) { + case *ast.MappingValueNode: + if err := p.SetComment(comment); err != nil { + return errors.Wrapf(err, "failed to set comment") + } + case *ast.MappingNode: + if err := p.SetComment(comment); err != nil { + return errors.Wrapf(err, "failed to set comment") + } + case *ast.SequenceNode: + if len(p.ValueHeadComments) == 0 { + p.ValueHeadComments = make([]*ast.CommentGroupNode, len(p.Values)) + } + var foundIdx int + for idx, v := range p.Values { + if v == filtered { + foundIdx = idx + break + } + } + p.ValueHeadComments[foundIdx] = comment + default: + return ErrUnsupportedHeadPositionType(node) + } + return nil +} + +func (e *Encoder) setLineComment(node ast.Node, filtered ast.Node, comment *ast.CommentGroupNode) error { + switch filtered.(type) { + case *ast.MappingValueNode, *ast.SequenceNode: + // Line comment cannot be set for mapping value node. + // It should probably be set for the parent map node + if err := e.setLineCommentToParentMapNode(node, filtered, comment); err != nil { + return errors.Wrapf(err, "failed to set line comment to parent node") + } + default: + if err := filtered.SetComment(comment); err != nil { + return errors.Wrapf(err, "failed to set comment") + } + } + return nil +} + +func (e *Encoder) setLineCommentToParentMapNode(node ast.Node, filtered ast.Node, comment *ast.CommentGroupNode) error { + parent := ast.Parent(node, filtered) + if parent == nil { + return ErrUnsupportedLinePositionType(node) + } + switch p := parent.(type) { + case *ast.MappingValueNode: + if err := p.Key.SetComment(comment); err != nil { + return errors.Wrapf(err, "failed to set comment") + } + case *ast.MappingNode: + if err := p.SetComment(comment); err != nil { + return errors.Wrapf(err, "failed to set comment") + } + default: + return ErrUnsupportedLinePositionType(parent) + } + return nil +} + +func (e *Encoder) setFootComment(node ast.Node, filtered ast.Node, comment *ast.CommentGroupNode) error { + parent := ast.Parent(node, filtered) + if parent == nil { + return ErrUnsupportedFootPositionType(node) + } + switch n := parent.(type) { + case *ast.MappingValueNode: + n.FootComment = comment + case *ast.MappingNode: + n.FootComment = comment + case *ast.SequenceNode: + n.FootComment = comment + default: + return ErrUnsupportedFootPositionType(n) + } + return nil +} + +func (e *Encoder) encodeDocument(doc []byte) (ast.Node, error) { + f, err := parser.ParseBytes(doc, 0) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse yaml") + } + for _, docNode := range f.Docs { + if docNode.Body != nil { + return docNode.Body, nil + } + } + return nil, nil +} + +func (e *Encoder) isInvalidValue(v reflect.Value) bool { + if !v.IsValid() { + return true + } + kind := v.Type().Kind() + if kind == reflect.Ptr && v.IsNil() { + return true + } + if kind == reflect.Interface && v.IsNil() { + return true + } + return false +} + +type jsonMarshaler interface { + MarshalJSON() ([]byte, error) +} + +func (e *Encoder) existsTypeInCustomMarshalerMap(t reflect.Type) bool { + if _, exists := e.customMarshalerMap[t]; exists { + return true + } + + globalCustomMarshalerMu.Lock() + defer globalCustomMarshalerMu.Unlock() + if _, exists := globalCustomMarshalerMap[t]; exists { + return true + } + return false +} + +func (e *Encoder) marshalerFromCustomMarshalerMap(t reflect.Type) (func(interface{}) ([]byte, error), bool) { + if marshaler, exists := e.customMarshalerMap[t]; exists { + return marshaler, exists + } + + globalCustomMarshalerMu.Lock() + defer globalCustomMarshalerMu.Unlock() + if marshaler, exists := globalCustomMarshalerMap[t]; exists { + return marshaler, exists + } + return nil, false +} + +func (e *Encoder) canEncodeByMarshaler(v reflect.Value) bool { + if !v.CanInterface() { + return false + } + if e.existsTypeInCustomMarshalerMap(v.Type()) { + return true + } + iface := v.Interface() + switch iface.(type) { + case BytesMarshalerContext: + return true + case BytesMarshaler: + return true + case InterfaceMarshalerContext: + return true + case InterfaceMarshaler: + return true + case time.Time: + return true + case time.Duration: + return true + case encoding.TextMarshaler: + return true + case jsonMarshaler: + return e.useJSONMarshaler + } + return false +} + +func (e *Encoder) encodeByMarshaler(ctx context.Context, v reflect.Value, column int) (ast.Node, error) { + iface := v.Interface() + + if marshaler, exists := e.marshalerFromCustomMarshalerMap(v.Type()); exists { + doc, err := marshaler(iface) + if err != nil { + return nil, errors.Wrapf(err, "failed to MarshalYAML") + } + node, err := e.encodeDocument(doc) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode document") + } + return node, nil + } + + if marshaler, ok := iface.(BytesMarshalerContext); ok { + doc, err := marshaler.MarshalYAML(ctx) + if err != nil { + return nil, errors.Wrapf(err, "failed to MarshalYAML") + } + node, err := e.encodeDocument(doc) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode document") + } + return node, nil + } + + if marshaler, ok := iface.(BytesMarshaler); ok { + doc, err := marshaler.MarshalYAML() + if err != nil { + return nil, errors.Wrapf(err, "failed to MarshalYAML") + } + node, err := e.encodeDocument(doc) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode document") + } + return node, nil + } + + if marshaler, ok := iface.(InterfaceMarshalerContext); ok { + marshalV, err := marshaler.MarshalYAML(ctx) + if err != nil { + return nil, errors.Wrapf(err, "failed to MarshalYAML") + } + return e.encodeValue(ctx, reflect.ValueOf(marshalV), column) + } + + if marshaler, ok := iface.(InterfaceMarshaler); ok { + marshalV, err := marshaler.MarshalYAML() + if err != nil { + return nil, errors.Wrapf(err, "failed to MarshalYAML") + } + return e.encodeValue(ctx, reflect.ValueOf(marshalV), column) + } + + if t, ok := iface.(time.Time); ok { + return e.encodeTime(t, column), nil + } + + if t, ok := iface.(time.Duration); ok { + return e.encodeDuration(t, column), nil + } + + if marshaler, ok := iface.(encoding.TextMarshaler); ok { + doc, err := marshaler.MarshalText() + if err != nil { + return nil, errors.Wrapf(err, "failed to MarshalText") + } + node, err := e.encodeDocument(doc) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode document") + } + return node, nil + } + + if e.useJSONMarshaler { + if marshaler, ok := iface.(jsonMarshaler); ok { + jsonBytes, err := marshaler.MarshalJSON() + if err != nil { + return nil, errors.Wrapf(err, "failed to MarshalJSON") + } + doc, err := JSONToYAML(jsonBytes) + if err != nil { + return nil, errors.Wrapf(err, "failed to convert json to yaml") + } + node, err := e.encodeDocument(doc) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode document") + } + return node, nil + } + } + + return nil, xerrors.Errorf("does not implemented Marshaler") +} + +func (e *Encoder) encodeValue(ctx context.Context, v reflect.Value, column int) (ast.Node, error) { + if e.isInvalidValue(v) { + return e.encodeNil(), nil + } + if e.canEncodeByMarshaler(v) { + node, err := e.encodeByMarshaler(ctx, v, column) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode by marshaler") + } + return node, nil + } + switch v.Type().Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return e.encodeInt(v.Int()), nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return e.encodeUint(v.Uint()), nil + case reflect.Float32: + return e.encodeFloat(v.Float(), 32), nil + case reflect.Float64: + return e.encodeFloat(v.Float(), 64), nil + case reflect.Ptr: + anchorName := e.anchorPtrToNameMap[v.Pointer()] + if anchorName != "" { + aliasName := anchorName + alias := ast.Alias(token.New("*", "*", e.pos(column))) + alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column))) + return alias, nil + } + return e.encodeValue(ctx, v.Elem(), column) + case reflect.Interface: + return e.encodeValue(ctx, v.Elem(), column) + case reflect.String: + return e.encodeString(v.String(), column), nil + case reflect.Bool: + return e.encodeBool(v.Bool()), nil + case reflect.Slice: + if mapSlice, ok := v.Interface().(MapSlice); ok { + return e.encodeMapSlice(ctx, mapSlice, column) + } + return e.encodeSlice(ctx, v) + case reflect.Array: + return e.encodeArray(ctx, v) + case reflect.Struct: + if v.CanInterface() { + if mapItem, ok := v.Interface().(MapItem); ok { + return e.encodeMapItem(ctx, mapItem, column) + } + if t, ok := v.Interface().(time.Time); ok { + return e.encodeTime(t, column), nil + } + } + return e.encodeStruct(ctx, v, column) + case reflect.Map: + return e.encodeMap(ctx, v, column), nil + default: + return nil, xerrors.Errorf("unknown value type %s", v.Type().String()) + } +} + +func (e *Encoder) pos(column int) *token.Position { + return &token.Position{ + Line: e.line, + Column: column, + Offset: e.offset, + IndentNum: e.indentNum, + IndentLevel: e.indentLevel, + } +} + +func (e *Encoder) encodeNil() *ast.NullNode { + value := "null" + return ast.Null(token.New(value, value, e.pos(e.column))) +} + +func (e *Encoder) encodeInt(v int64) *ast.IntegerNode { + value := fmt.Sprint(v) + return ast.Integer(token.New(value, value, e.pos(e.column))) +} + +func (e *Encoder) encodeUint(v uint64) *ast.IntegerNode { + value := fmt.Sprint(v) + return ast.Integer(token.New(value, value, e.pos(e.column))) +} + +func (e *Encoder) encodeFloat(v float64, bitSize int) ast.Node { + if v == math.Inf(0) { + value := ".inf" + return ast.Infinity(token.New(value, value, e.pos(e.column))) + } else if v == math.Inf(-1) { + value := "-.inf" + return ast.Infinity(token.New(value, value, e.pos(e.column))) + } else if math.IsNaN(v) { + value := ".nan" + return ast.Nan(token.New(value, value, e.pos(e.column))) + } + value := strconv.FormatFloat(v, 'g', -1, bitSize) + if !strings.Contains(value, ".") && !strings.Contains(value, "e") { + // append x.0 suffix to keep float value context + value = fmt.Sprintf("%s.0", value) + } + return ast.Float(token.New(value, value, e.pos(e.column))) +} + +func (e *Encoder) isNeedQuoted(v string) bool { + if e.isJSONStyle { + return true + } + if e.useLiteralStyleIfMultiline && strings.ContainsAny(v, "\n\r") { + return false + } + if e.isFlowStyle && strings.ContainsAny(v, `]},'"`) { + return true + } + if token.IsNeedQuoted(v) { + return true + } + return false +} + +func (e *Encoder) encodeString(v string, column int) *ast.StringNode { + if e.isNeedQuoted(v) { + if e.singleQuote { + v = quoteWith(v, '\'') + } else { + v = strconv.Quote(v) + } + } + return ast.String(token.New(v, v, e.pos(column))) +} + +func (e *Encoder) encodeBool(v bool) *ast.BoolNode { + value := fmt.Sprint(v) + return ast.Bool(token.New(value, value, e.pos(e.column))) +} + +func (e *Encoder) encodeSlice(ctx context.Context, value reflect.Value) (*ast.SequenceNode, error) { + if e.indentSequence { + e.column += e.indent + } + column := e.column + sequence := ast.Sequence(token.New("-", "-", e.pos(column)), e.isFlowStyle) + for i := 0; i < value.Len(); i++ { + node, err := e.encodeValue(ctx, value.Index(i), column) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode value for slice") + } + sequence.Values = append(sequence.Values, node) + } + if e.indentSequence { + e.column -= e.indent + } + return sequence, nil +} + +func (e *Encoder) encodeArray(ctx context.Context, value reflect.Value) (*ast.SequenceNode, error) { + if e.indentSequence { + e.column += e.indent + } + column := e.column + sequence := ast.Sequence(token.New("-", "-", e.pos(column)), e.isFlowStyle) + for i := 0; i < value.Len(); i++ { + node, err := e.encodeValue(ctx, value.Index(i), column) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode value for array") + } + sequence.Values = append(sequence.Values, node) + } + if e.indentSequence { + e.column -= e.indent + } + return sequence, nil +} + +func (e *Encoder) encodeMapItem(ctx context.Context, item MapItem, column int) (*ast.MappingValueNode, error) { + k := reflect.ValueOf(item.Key) + v := reflect.ValueOf(item.Value) + value, err := e.encodeValue(ctx, v, column) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode MapItem") + } + if e.isMapNode(value) { + value.AddColumn(e.indent) + } + return ast.MappingValue( + token.New("", "", e.pos(column)), + e.encodeString(k.Interface().(string), column), + value, + ), nil +} + +func (e *Encoder) encodeMapSlice(ctx context.Context, value MapSlice, column int) (*ast.MappingNode, error) { + node := ast.Mapping(token.New("", "", e.pos(column)), e.isFlowStyle) + for _, item := range value { + value, err := e.encodeMapItem(ctx, item, column) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode MapItem for MapSlice") + } + node.Values = append(node.Values, value) + } + return node, nil +} + +func (e *Encoder) isMapNode(node ast.Node) bool { + _, ok := node.(ast.MapNode) + return ok +} + +func (e *Encoder) encodeMap(ctx context.Context, value reflect.Value, column int) ast.Node { + node := ast.Mapping(token.New("", "", e.pos(column)), e.isFlowStyle) + keys := make([]interface{}, len(value.MapKeys())) + for i, k := range value.MapKeys() { + keys[i] = k.Interface() + } + sort.Slice(keys, func(i, j int) bool { + return fmt.Sprint(keys[i]) < fmt.Sprint(keys[j]) + }) + for _, key := range keys { + k := reflect.ValueOf(key) + v := value.MapIndex(k) + value, err := e.encodeValue(ctx, v, column) + if err != nil { + return nil + } + if e.isMapNode(value) { + value.AddColumn(e.indent) + } + node.Values = append(node.Values, ast.MappingValue( + nil, + e.encodeString(fmt.Sprint(key), column), + value, + )) + } + return node +} + +// IsZeroer is used to check whether an object is zero to determine +// whether it should be omitted when marshaling with the omitempty flag. +// One notable implementation is time.Time. +type IsZeroer interface { + IsZero() bool +} + +func (e *Encoder) isZeroValue(v reflect.Value) bool { + kind := v.Kind() + if z, ok := v.Interface().(IsZeroer); ok { + if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() { + return true + } + return z.IsZero() + } + switch kind { + case reflect.String: + return len(v.String()) == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + case reflect.Slice: + return v.Len() == 0 + case reflect.Map: + return v.Len() == 0 + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Struct: + vt := v.Type() + for i := v.NumField() - 1; i >= 0; i-- { + if vt.Field(i).PkgPath != "" { + continue // private field + } + if !e.isZeroValue(v.Field(i)) { + return false + } + } + return true + } + return false +} + +func (e *Encoder) encodeTime(v time.Time, column int) *ast.StringNode { + value := v.Format(time.RFC3339Nano) + if e.isJSONStyle { + value = strconv.Quote(value) + } + return ast.String(token.New(value, value, e.pos(column))) +} + +func (e *Encoder) encodeDuration(v time.Duration, column int) *ast.StringNode { + value := v.String() + if e.isJSONStyle { + value = strconv.Quote(value) + } + return ast.String(token.New(value, value, e.pos(column))) +} + +func (e *Encoder) encodeAnchor(anchorName string, value ast.Node, fieldValue reflect.Value, column int) (*ast.AnchorNode, error) { + anchorNode := ast.Anchor(token.New("&", "&", e.pos(column))) + anchorNode.Name = ast.String(token.New(anchorName, anchorName, e.pos(column))) + anchorNode.Value = value + if e.anchorCallback != nil { + if err := e.anchorCallback(anchorNode, fieldValue.Interface()); err != nil { + return nil, errors.Wrapf(err, "failed to marshal anchor") + } + if snode, ok := anchorNode.Name.(*ast.StringNode); ok { + anchorName = snode.Value + } + } + if fieldValue.Kind() == reflect.Ptr { + e.anchorPtrToNameMap[fieldValue.Pointer()] = anchorName + } + return anchorNode, nil +} + +func (e *Encoder) encodeStruct(ctx context.Context, value reflect.Value, column int) (ast.Node, error) { + node := ast.Mapping(token.New("", "", e.pos(column)), e.isFlowStyle) + structType := value.Type() + structFieldMap, err := structFieldMap(structType) + if err != nil { + return nil, errors.Wrapf(err, "failed to get struct field map") + } + hasInlineAnchorField := false + var inlineAnchorValue reflect.Value + for i := 0; i < value.NumField(); i++ { + field := structType.Field(i) + if isIgnoredStructField(field) { + continue + } + fieldValue := value.FieldByName(field.Name) + structField := structFieldMap[field.Name] + if structField.IsOmitEmpty && e.isZeroValue(fieldValue) { + // omit encoding + continue + } + ve := e + if !e.isFlowStyle && structField.IsFlow { + ve = &Encoder{} + *ve = *e + ve.isFlowStyle = true + } + value, err := ve.encodeValue(ctx, fieldValue, column) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode value") + } + if e.isMapNode(value) { + value.AddColumn(e.indent) + } + var key ast.MapKeyNode = e.encodeString(structField.RenderName, column) + switch { + case structField.AnchorName != "": + anchorNode, err := e.encodeAnchor(structField.AnchorName, value, fieldValue, column) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode anchor") + } + value = anchorNode + case structField.IsAutoAlias: + if fieldValue.Kind() != reflect.Ptr { + return nil, xerrors.Errorf( + "%s in struct is not pointer type. but required automatically alias detection", + structField.FieldName, + ) + } + anchorName := e.anchorPtrToNameMap[fieldValue.Pointer()] + if anchorName == "" { + return nil, xerrors.Errorf( + "cannot find anchor name from pointer address for automatically alias detection", + ) + } + aliasName := anchorName + alias := ast.Alias(token.New("*", "*", e.pos(column))) + alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column))) + value = alias + if structField.IsInline { + // if both used alias and inline, output `<<: *alias` + key = ast.MergeKey(token.New("<<", "<<", e.pos(column))) + } + case structField.AliasName != "": + aliasName := structField.AliasName + alias := ast.Alias(token.New("*", "*", e.pos(column))) + alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column))) + value = alias + if structField.IsInline { + // if both used alias and inline, output `<<: *alias` + key = ast.MergeKey(token.New("<<", "<<", e.pos(column))) + } + case structField.IsInline: + isAutoAnchor := structField.IsAutoAnchor + if !hasInlineAnchorField { + hasInlineAnchorField = isAutoAnchor + } + if isAutoAnchor { + inlineAnchorValue = fieldValue + } + mapNode, ok := value.(ast.MapNode) + if !ok { + return nil, xerrors.Errorf("inline value is must be map or struct type") + } + mapIter := mapNode.MapRange() + for mapIter.Next() { + key := mapIter.Key() + value := mapIter.Value() + keyName := key.GetToken().Value + if structFieldMap.isIncludedRenderName(keyName) { + // if declared same key name, skip encoding this field + continue + } + key.AddColumn(-e.indent) + value.AddColumn(-e.indent) + node.Values = append(node.Values, ast.MappingValue(nil, key, value)) + } + continue + case structField.IsAutoAnchor: + anchorNode, err := e.encodeAnchor(structField.RenderName, value, fieldValue, column) + if err != nil { + return nil, errors.Wrapf(err, "failed to encode anchor") + } + value = anchorNode + } + node.Values = append(node.Values, ast.MappingValue(nil, key, value)) + } + if hasInlineAnchorField { + node.AddColumn(e.indent) + anchorName := "anchor" + anchorNode := ast.Anchor(token.New("&", "&", e.pos(column))) + anchorNode.Name = ast.String(token.New(anchorName, anchorName, e.pos(column))) + anchorNode.Value = node + if e.anchorCallback != nil { + if err := e.anchorCallback(anchorNode, value.Addr().Interface()); err != nil { + return nil, errors.Wrapf(err, "failed to marshal anchor") + } + if snode, ok := anchorNode.Name.(*ast.StringNode); ok { + anchorName = snode.Value + } + } + if inlineAnchorValue.Kind() == reflect.Ptr { + e.anchorPtrToNameMap[inlineAnchorValue.Pointer()] = anchorName + } + return anchorNode, nil + } + return node, nil +} diff --git a/vendor/github.com/goccy/go-yaml/error.go b/vendor/github.com/goccy/go-yaml/error.go new file mode 100644 index 000000000..163dcc558 --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/error.go @@ -0,0 +1,62 @@ +package yaml + +import ( + "github.com/goccy/go-yaml/ast" + "golang.org/x/xerrors" +) + +var ( + ErrInvalidQuery = xerrors.New("invalid query") + ErrInvalidPath = xerrors.New("invalid path instance") + ErrInvalidPathString = xerrors.New("invalid path string") + ErrNotFoundNode = xerrors.New("node not found") + ErrUnknownCommentPositionType = xerrors.New("unknown comment position type") + ErrInvalidCommentMapValue = xerrors.New("invalid comment map value. it must be not nil value") +) + +func ErrUnsupportedHeadPositionType(node ast.Node) error { + return xerrors.Errorf("unsupported comment head position for %s", node.Type()) +} + +func ErrUnsupportedLinePositionType(node ast.Node) error { + return xerrors.Errorf("unsupported comment line position for %s", node.Type()) +} + +func ErrUnsupportedFootPositionType(node ast.Node) error { + return xerrors.Errorf("unsupported comment foot position for %s", node.Type()) +} + +// IsInvalidQueryError whether err is ErrInvalidQuery or not. +func IsInvalidQueryError(err error) bool { + return xerrors.Is(err, ErrInvalidQuery) +} + +// IsInvalidPathError whether err is ErrInvalidPath or not. +func IsInvalidPathError(err error) bool { + return xerrors.Is(err, ErrInvalidPath) +} + +// IsInvalidPathStringError whether err is ErrInvalidPathString or not. +func IsInvalidPathStringError(err error) bool { + return xerrors.Is(err, ErrInvalidPathString) +} + +// IsNotFoundNodeError whether err is ErrNotFoundNode or not. +func IsNotFoundNodeError(err error) bool { + return xerrors.Is(err, ErrNotFoundNode) +} + +// IsInvalidTokenTypeError whether err is ast.ErrInvalidTokenType or not. +func IsInvalidTokenTypeError(err error) bool { + return xerrors.Is(err, ast.ErrInvalidTokenType) +} + +// IsInvalidAnchorNameError whether err is ast.ErrInvalidAnchorName or not. +func IsInvalidAnchorNameError(err error) bool { + return xerrors.Is(err, ast.ErrInvalidAnchorName) +} + +// IsInvalidAliasNameError whether err is ast.ErrInvalidAliasName or not. +func IsInvalidAliasNameError(err error) bool { + return xerrors.Is(err, ast.ErrInvalidAliasName) +} diff --git a/vendor/github.com/goccy/go-yaml/internal/errors/error.go b/vendor/github.com/goccy/go-yaml/internal/errors/error.go new file mode 100644 index 000000000..7f1ea9af7 --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/internal/errors/error.go @@ -0,0 +1,260 @@ +package errors + +import ( + "bytes" + "fmt" + "reflect" + + "github.com/goccy/go-yaml/printer" + "github.com/goccy/go-yaml/token" + "golang.org/x/xerrors" +) + +const ( + defaultColorize = false + defaultIncludeSource = true +) + +var ( + ErrDecodeRequiredPointerType = xerrors.New("required pointer type value") +) + +// Wrapf wrap error for stack trace +func Wrapf(err error, msg string, args ...interface{}) error { + return &wrapError{ + baseError: &baseError{}, + err: xerrors.Errorf(msg, args...), + nextErr: err, + frame: xerrors.Caller(1), + } +} + +// ErrSyntax create syntax error instance with message and token +func ErrSyntax(msg string, tk *token.Token) *syntaxError { + return &syntaxError{ + baseError: &baseError{}, + msg: msg, + token: tk, + frame: xerrors.Caller(1), + } +} + +type baseError struct { + state fmt.State + verb rune +} + +func (e *baseError) Error() string { + return "" +} + +func (e *baseError) chainStateAndVerb(err error) { + wrapErr, ok := err.(*wrapError) + if ok { + wrapErr.state = e.state + wrapErr.verb = e.verb + } + syntaxErr, ok := err.(*syntaxError) + if ok { + syntaxErr.state = e.state + syntaxErr.verb = e.verb + } +} + +type wrapError struct { + *baseError + err error + nextErr error + frame xerrors.Frame +} + +type FormatErrorPrinter struct { + xerrors.Printer + Colored bool + InclSource bool +} + +func (e *wrapError) As(target interface{}) bool { + err := e.nextErr + for { + if wrapErr, ok := err.(*wrapError); ok { + err = wrapErr.nextErr + continue + } + break + } + return xerrors.As(err, target) +} + +func (e *wrapError) Unwrap() error { + return e.nextErr +} + +func (e *wrapError) PrettyPrint(p xerrors.Printer, colored, inclSource bool) error { + return e.FormatError(&FormatErrorPrinter{Printer: p, Colored: colored, InclSource: inclSource}) +} + +func (e *wrapError) FormatError(p xerrors.Printer) error { + if _, ok := p.(*FormatErrorPrinter); !ok { + p = &FormatErrorPrinter{ + Printer: p, + Colored: defaultColorize, + InclSource: defaultIncludeSource, + } + } + if e.verb == 'v' && e.state.Flag('+') { + // print stack trace for debugging + p.Print(e.err, "\n") + e.frame.Format(p) + e.chainStateAndVerb(e.nextErr) + return e.nextErr + } + err := e.nextErr + for { + if wrapErr, ok := err.(*wrapError); ok { + err = wrapErr.nextErr + continue + } + break + } + e.chainStateAndVerb(err) + if fmtErr, ok := err.(xerrors.Formatter); ok { + fmtErr.FormatError(p) + } else { + p.Print(err) + } + return nil +} + +type wrapState struct { + org fmt.State +} + +func (s *wrapState) Write(b []byte) (n int, err error) { + return s.org.Write(b) +} + +func (s *wrapState) Width() (wid int, ok bool) { + return s.org.Width() +} + +func (s *wrapState) Precision() (prec int, ok bool) { + return s.org.Precision() +} + +func (s *wrapState) Flag(c int) bool { + // set true to 'printDetail' forced because when p.Detail() is false, xerrors.Printer no output any text + if c == '#' { + // ignore '#' keyword because xerrors.FormatError doesn't set true to printDetail. + // ( see https://github.com/golang/xerrors/blob/master/adaptor.go#L39-L43 ) + return false + } + return true +} + +func (e *wrapError) Format(state fmt.State, verb rune) { + e.state = state + e.verb = verb + xerrors.FormatError(e, &wrapState{org: state}, verb) +} + +func (e *wrapError) Error() string { + var buf bytes.Buffer + e.PrettyPrint(&Sink{&buf}, defaultColorize, defaultIncludeSource) + return buf.String() +} + +type syntaxError struct { + *baseError + msg string + token *token.Token + frame xerrors.Frame +} + +func (e *syntaxError) PrettyPrint(p xerrors.Printer, colored, inclSource bool) error { + return e.FormatError(&FormatErrorPrinter{Printer: p, Colored: colored, InclSource: inclSource}) +} + +func (e *syntaxError) FormatError(p xerrors.Printer) error { + var pp printer.Printer + + var colored, inclSource bool + if fep, ok := p.(*FormatErrorPrinter); ok { + colored = fep.Colored + inclSource = fep.InclSource + } + + pos := fmt.Sprintf("[%d:%d] ", e.token.Position.Line, e.token.Position.Column) + msg := pp.PrintErrorMessage(fmt.Sprintf("%s%s", pos, e.msg), colored) + if inclSource { + msg += "\n" + pp.PrintErrorToken(e.token, colored) + } + p.Print(msg) + + if e.verb == 'v' && e.state.Flag('+') { + // %+v + // print stack trace for debugging + e.frame.Format(p) + } + return nil +} + +type PrettyPrinter interface { + PrettyPrint(xerrors.Printer, bool, bool) error +} +type Sink struct{ *bytes.Buffer } + +func (es *Sink) Print(args ...interface{}) { + fmt.Fprint(es.Buffer, args...) +} + +func (es *Sink) Printf(f string, args ...interface{}) { + fmt.Fprintf(es.Buffer, f, args...) +} + +func (es *Sink) Detail() bool { + return false +} + +func (e *syntaxError) Error() string { + var buf bytes.Buffer + e.PrettyPrint(&Sink{&buf}, defaultColorize, defaultIncludeSource) + return buf.String() +} + +type TypeError struct { + DstType reflect.Type + SrcType reflect.Type + StructFieldName *string + Token *token.Token +} + +func (e *TypeError) Error() string { + if e.StructFieldName != nil { + return fmt.Sprintf("cannot unmarshal %s into Go struct field %s of type %s", e.SrcType, *e.StructFieldName, e.DstType) + } + return fmt.Sprintf("cannot unmarshal %s into Go value of type %s", e.SrcType, e.DstType) +} + +func (e *TypeError) PrettyPrint(p xerrors.Printer, colored, inclSource bool) error { + return e.FormatError(&FormatErrorPrinter{Printer: p, Colored: colored, InclSource: inclSource}) +} + +func (e *TypeError) FormatError(p xerrors.Printer) error { + var pp printer.Printer + + var colored, inclSource bool + if fep, ok := p.(*FormatErrorPrinter); ok { + colored = fep.Colored + inclSource = fep.InclSource + } + + pos := fmt.Sprintf("[%d:%d] ", e.Token.Position.Line, e.Token.Position.Column) + msg := pp.PrintErrorMessage(fmt.Sprintf("%s%s", pos, e.Error()), colored) + if inclSource { + msg += "\n" + pp.PrintErrorToken(e.Token, colored) + } + p.Print(msg) + + return nil +} diff --git a/vendor/github.com/goccy/go-yaml/lexer/lexer.go b/vendor/github.com/goccy/go-yaml/lexer/lexer.go new file mode 100644 index 000000000..3207f4f22 --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/lexer/lexer.go @@ -0,0 +1,23 @@ +package lexer + +import ( + "io" + + "github.com/goccy/go-yaml/scanner" + "github.com/goccy/go-yaml/token" +) + +// Tokenize split to token instances from string +func Tokenize(src string) token.Tokens { + var s scanner.Scanner + s.Init(src) + var tokens token.Tokens + for { + subTokens, err := s.Scan() + if err == io.EOF { + break + } + tokens.Add(subTokens...) + } + return tokens +} diff --git a/vendor/github.com/goccy/go-yaml/option.go b/vendor/github.com/goccy/go-yaml/option.go new file mode 100644 index 000000000..eab5d43a8 --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/option.go @@ -0,0 +1,278 @@ +package yaml + +import ( + "io" + "reflect" + + "github.com/goccy/go-yaml/ast" +) + +// DecodeOption functional option type for Decoder +type DecodeOption func(d *Decoder) error + +// ReferenceReaders pass to Decoder that reference to anchor defined by passed readers +func ReferenceReaders(readers ...io.Reader) DecodeOption { + return func(d *Decoder) error { + d.referenceReaders = append(d.referenceReaders, readers...) + return nil + } +} + +// ReferenceFiles pass to Decoder that reference to anchor defined by passed files +func ReferenceFiles(files ...string) DecodeOption { + return func(d *Decoder) error { + d.referenceFiles = files + return nil + } +} + +// ReferenceDirs pass to Decoder that reference to anchor defined by files under the passed dirs +func ReferenceDirs(dirs ...string) DecodeOption { + return func(d *Decoder) error { + d.referenceDirs = dirs + return nil + } +} + +// RecursiveDir search yaml file recursively from passed dirs by ReferenceDirs option +func RecursiveDir(isRecursive bool) DecodeOption { + return func(d *Decoder) error { + d.isRecursiveDir = isRecursive + return nil + } +} + +// Validator set StructValidator instance to Decoder +func Validator(v StructValidator) DecodeOption { + return func(d *Decoder) error { + d.validator = v + return nil + } +} + +// Strict enable DisallowUnknownField and DisallowDuplicateKey +func Strict() DecodeOption { + return func(d *Decoder) error { + d.disallowUnknownField = true + d.disallowDuplicateKey = true + return nil + } +} + +// DisallowUnknownField causes the Decoder to return an error when the destination +// is a struct and the input contains object keys which do not match any +// non-ignored, exported fields in the destination. +func DisallowUnknownField() DecodeOption { + return func(d *Decoder) error { + d.disallowUnknownField = true + return nil + } +} + +// DisallowDuplicateKey causes an error when mapping keys that are duplicates +func DisallowDuplicateKey() DecodeOption { + return func(d *Decoder) error { + d.disallowDuplicateKey = true + return nil + } +} + +// UseOrderedMap can be interpreted as a map, +// and uses MapSlice ( ordered map ) aggressively if there is no type specification +func UseOrderedMap() DecodeOption { + return func(d *Decoder) error { + d.useOrderedMap = true + return nil + } +} + +// UseJSONUnmarshaler if neither `BytesUnmarshaler` nor `InterfaceUnmarshaler` is implemented +// and `UnmashalJSON([]byte)error` is implemented, convert the argument from `YAML` to `JSON` and then call it. +func UseJSONUnmarshaler() DecodeOption { + return func(d *Decoder) error { + d.useJSONUnmarshaler = true + return nil + } +} + +// CustomUnmarshaler overrides any decoding process for the type specified in generics. +// +// NOTE: If RegisterCustomUnmarshaler and CustomUnmarshaler of DecodeOption are specified for the same type, +// the CustomUnmarshaler specified in DecodeOption takes precedence. +func CustomUnmarshaler[T any](unmarshaler func(*T, []byte) error) DecodeOption { + return func(d *Decoder) error { + var typ *T + d.customUnmarshalerMap[reflect.TypeOf(typ)] = func(v interface{}, b []byte) error { + return unmarshaler(v.(*T), b) + } + return nil + } +} + +// EncodeOption functional option type for Encoder +type EncodeOption func(e *Encoder) error + +// Indent change indent number +func Indent(spaces int) EncodeOption { + return func(e *Encoder) error { + e.indent = spaces + return nil + } +} + +// IndentSequence causes sequence values to be indented the same value as Indent +func IndentSequence(indent bool) EncodeOption { + return func(e *Encoder) error { + e.indentSequence = indent + return nil + } +} + +// UseSingleQuote determines if single or double quotes should be preferred for strings. +func UseSingleQuote(sq bool) EncodeOption { + return func(e *Encoder) error { + e.singleQuote = sq + return nil + } +} + +// Flow encoding by flow style +func Flow(isFlowStyle bool) EncodeOption { + return func(e *Encoder) error { + e.isFlowStyle = isFlowStyle + return nil + } +} + +// UseLiteralStyleIfMultiline causes encoding multiline strings with a literal syntax, +// no matter what characters they include +func UseLiteralStyleIfMultiline(useLiteralStyleIfMultiline bool) EncodeOption { + return func(e *Encoder) error { + e.useLiteralStyleIfMultiline = useLiteralStyleIfMultiline + return nil + } +} + +// JSON encode in JSON format +func JSON() EncodeOption { + return func(e *Encoder) error { + e.isJSONStyle = true + e.isFlowStyle = true + return nil + } +} + +// MarshalAnchor call back if encoder find an anchor during encoding +func MarshalAnchor(callback func(*ast.AnchorNode, interface{}) error) EncodeOption { + return func(e *Encoder) error { + e.anchorCallback = callback + return nil + } +} + +// UseJSONMarshaler if neither `BytesMarshaler` nor `InterfaceMarshaler` +// nor `encoding.TextMarshaler` is implemented and `MarshalJSON()([]byte, error)` is implemented, +// call `MarshalJSON` to convert the returned `JSON` to `YAML` for processing. +func UseJSONMarshaler() EncodeOption { + return func(e *Encoder) error { + e.useJSONMarshaler = true + return nil + } +} + +// CustomMarshaler overrides any encoding process for the type specified in generics. +// +// NOTE: If type T implements MarshalYAML for pointer receiver, the type specified in CustomMarshaler must be *T. +// If RegisterCustomMarshaler and CustomMarshaler of EncodeOption are specified for the same type, +// the CustomMarshaler specified in EncodeOption takes precedence. +func CustomMarshaler[T any](marshaler func(T) ([]byte, error)) EncodeOption { + return func(e *Encoder) error { + var typ T + e.customMarshalerMap[reflect.TypeOf(typ)] = func(v interface{}) ([]byte, error) { + return marshaler(v.(T)) + } + return nil + } +} + +// CommentPosition type of the position for comment. +type CommentPosition int + +const ( + CommentHeadPosition CommentPosition = CommentPosition(iota) + CommentLinePosition + CommentFootPosition +) + +func (p CommentPosition) String() string { + switch p { + case CommentHeadPosition: + return "Head" + case CommentLinePosition: + return "Line" + case CommentFootPosition: + return "Foot" + default: + return "" + } +} + +// LineComment create a one-line comment for CommentMap. +func LineComment(text string) *Comment { + return &Comment{ + Texts: []string{text}, + Position: CommentLinePosition, + } +} + +// HeadComment create a multiline comment for CommentMap. +func HeadComment(texts ...string) *Comment { + return &Comment{ + Texts: texts, + Position: CommentHeadPosition, + } +} + +// FootComment create a multiline comment for CommentMap. +func FootComment(texts ...string) *Comment { + return &Comment{ + Texts: texts, + Position: CommentFootPosition, + } +} + +// Comment raw data for comment. +type Comment struct { + Texts []string + Position CommentPosition +} + +// CommentMap map of the position of the comment and the comment information. +type CommentMap map[string][]*Comment + +// WithComment add a comment using the location and text information given in the CommentMap. +func WithComment(cm CommentMap) EncodeOption { + return func(e *Encoder) error { + commentMap := map[*Path][]*Comment{} + for k, v := range cm { + path, err := PathString(k) + if err != nil { + return err + } + commentMap[path] = v + } + e.commentMap = commentMap + return nil + } +} + +// CommentToMap apply the position and content of comments in a YAML document to a CommentMap. +func CommentToMap(cm CommentMap) DecodeOption { + return func(d *Decoder) error { + if cm == nil { + return ErrInvalidCommentMapValue + } + d.toCommentMap = cm + return nil + } +} diff --git a/vendor/github.com/goccy/go-yaml/parser/context.go b/vendor/github.com/goccy/go-yaml/parser/context.go new file mode 100644 index 000000000..99f18b184 --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/parser/context.go @@ -0,0 +1,199 @@ +package parser + +import ( + "fmt" + "strings" + + "github.com/goccy/go-yaml/token" +) + +// context context at parsing +type context struct { + parent *context + idx int + size int + tokens token.Tokens + mode Mode + path string +} + +var pathSpecialChars = []string{ + "$", "*", ".", "[", "]", +} + +func containsPathSpecialChar(path string) bool { + for _, char := range pathSpecialChars { + if strings.Contains(path, char) { + return true + } + } + return false +} + +func normalizePath(path string) string { + if containsPathSpecialChar(path) { + return fmt.Sprintf("'%s'", path) + } + return path +} + +func (c *context) withChild(path string) *context { + ctx := c.copy() + path = normalizePath(path) + ctx.path += fmt.Sprintf(".%s", path) + return ctx +} + +func (c *context) withIndex(idx uint) *context { + ctx := c.copy() + ctx.path += fmt.Sprintf("[%d]", idx) + return ctx +} + +func (c *context) copy() *context { + return &context{ + parent: c, + idx: c.idx, + size: c.size, + tokens: append(token.Tokens{}, c.tokens...), + mode: c.mode, + path: c.path, + } +} + +func (c *context) next() bool { + return c.idx < c.size +} + +func (c *context) previousToken() *token.Token { + if c.idx > 0 { + return c.tokens[c.idx-1] + } + return nil +} + +func (c *context) insertToken(idx int, tk *token.Token) { + if c.parent != nil { + c.parent.insertToken(idx, tk) + } + if c.size < idx { + return + } + if c.size == idx { + curToken := c.tokens[c.size-1] + tk.Next = curToken + curToken.Prev = tk + + c.tokens = append(c.tokens, tk) + c.size = len(c.tokens) + return + } + + curToken := c.tokens[idx] + tk.Next = curToken + curToken.Prev = tk + + c.tokens = append(c.tokens[:idx+1], c.tokens[idx:]...) + c.tokens[idx] = tk + c.size = len(c.tokens) +} + +func (c *context) currentToken() *token.Token { + if c.idx >= c.size { + return nil + } + return c.tokens[c.idx] +} + +func (c *context) nextToken() *token.Token { + if c.idx+1 >= c.size { + return nil + } + return c.tokens[c.idx+1] +} + +func (c *context) afterNextToken() *token.Token { + if c.idx+2 >= c.size { + return nil + } + return c.tokens[c.idx+2] +} + +func (c *context) nextNotCommentToken() *token.Token { + for i := c.idx + 1; i < c.size; i++ { + tk := c.tokens[i] + if tk.Type == token.CommentType { + continue + } + return tk + } + return nil +} + +func (c *context) afterNextNotCommentToken() *token.Token { + notCommentTokenCount := 0 + for i := c.idx + 1; i < c.size; i++ { + tk := c.tokens[i] + if tk.Type == token.CommentType { + continue + } + notCommentTokenCount++ + if notCommentTokenCount == 2 { + return tk + } + } + return nil +} + +func (c *context) enabledComment() bool { + return c.mode&ParseComments != 0 +} + +func (c *context) isCurrentCommentToken() bool { + tk := c.currentToken() + if tk == nil { + return false + } + return tk.Type == token.CommentType +} + +func (c *context) progressIgnoreComment(num int) { + if c.parent != nil { + c.parent.progressIgnoreComment(num) + } + if c.size <= c.idx+num { + c.idx = c.size + } else { + c.idx += num + } +} + +func (c *context) progress(num int) { + if c.isCurrentCommentToken() { + return + } + c.progressIgnoreComment(num) +} + +func newContext(tokens token.Tokens, mode Mode) *context { + filteredTokens := []*token.Token{} + if mode&ParseComments != 0 { + filteredTokens = tokens + } else { + for _, tk := range tokens { + if tk.Type == token.CommentType { + continue + } + // keep prev/next reference between tokens containing comments + // https://github.com/goccy/go-yaml/issues/254 + filteredTokens = append(filteredTokens, tk) + } + } + return &context{ + idx: 0, + size: len(filteredTokens), + tokens: token.Tokens(filteredTokens), + mode: mode, + path: "$", + } +} diff --git a/vendor/github.com/goccy/go-yaml/parser/parser.go b/vendor/github.com/goccy/go-yaml/parser/parser.go new file mode 100644 index 000000000..568e6ad42 --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/parser/parser.go @@ -0,0 +1,714 @@ +package parser + +import ( + "fmt" + "io/ioutil" + "strings" + + "github.com/goccy/go-yaml/ast" + "github.com/goccy/go-yaml/internal/errors" + "github.com/goccy/go-yaml/lexer" + "github.com/goccy/go-yaml/token" + "golang.org/x/xerrors" +) + +type parser struct{} + +func (p *parser) parseMapping(ctx *context) (*ast.MappingNode, error) { + mapTk := ctx.currentToken() + node := ast.Mapping(mapTk, true) + node.SetPath(ctx.path) + ctx.progress(1) // skip MappingStart token + for ctx.next() { + tk := ctx.currentToken() + if tk.Type == token.MappingEndType { + node.End = tk + return node, nil + } else if tk.Type == token.CollectEntryType { + ctx.progress(1) + continue + } + + value, err := p.parseMappingValue(ctx) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse mapping value in mapping node") + } + mvnode, ok := value.(*ast.MappingValueNode) + if !ok { + return nil, errors.ErrSyntax("failed to parse flow mapping node", value.GetToken()) + } + node.Values = append(node.Values, mvnode) + ctx.progress(1) + } + return nil, errors.ErrSyntax("unterminated flow mapping", node.GetToken()) +} + +func (p *parser) parseSequence(ctx *context) (*ast.SequenceNode, error) { + node := ast.Sequence(ctx.currentToken(), true) + node.SetPath(ctx.path) + ctx.progress(1) // skip SequenceStart token + for ctx.next() { + tk := ctx.currentToken() + if tk.Type == token.SequenceEndType { + node.End = tk + break + } else if tk.Type == token.CollectEntryType { + ctx.progress(1) + continue + } + + value, err := p.parseToken(ctx.withIndex(uint(len(node.Values))), tk) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse sequence value in flow sequence node") + } + node.Values = append(node.Values, value) + ctx.progress(1) + } + return node, nil +} + +func (p *parser) parseTag(ctx *context) (*ast.TagNode, error) { + tagToken := ctx.currentToken() + node := ast.Tag(tagToken) + node.SetPath(ctx.path) + ctx.progress(1) // skip tag token + var ( + value ast.Node + err error + ) + switch token.ReservedTagKeyword(tagToken.Value) { + case token.MappingTag, + token.OrderedMapTag: + value, err = p.parseMapping(ctx) + case token.IntegerTag, + token.FloatTag, + token.StringTag, + token.BinaryTag, + token.TimestampTag, + token.NullTag: + typ := ctx.currentToken().Type + if typ == token.LiteralType || typ == token.FoldedType { + value, err = p.parseLiteral(ctx) + } else { + value = p.parseScalarValue(ctx.currentToken()) + } + case token.SequenceTag, + token.SetTag: + err = errors.ErrSyntax(fmt.Sprintf("sorry, currently not supported %s tag", tagToken.Value), tagToken) + default: + // custom tag + value, err = p.parseToken(ctx, ctx.currentToken()) + } + if err != nil { + return nil, errors.Wrapf(err, "failed to parse tag value") + } + node.Value = value + return node, nil +} + +func (p *parser) removeLeftSideNewLineCharacter(src string) string { + // CR or LF or CRLF + return strings.TrimLeft(strings.TrimLeft(strings.TrimLeft(src, "\r"), "\n"), "\r\n") +} + +func (p *parser) existsNewLineCharacter(src string) bool { + if strings.Index(src, "\n") > 0 { + return true + } + if strings.Index(src, "\r") > 0 { + return true + } + return false +} + +func (p *parser) validateMapKey(tk *token.Token) error { + if tk.Type != token.StringType { + return nil + } + origin := p.removeLeftSideNewLineCharacter(tk.Origin) + if p.existsNewLineCharacter(origin) { + return errors.ErrSyntax("unexpected key name", tk) + } + return nil +} + +func (p *parser) createNullToken(base *token.Token) *token.Token { + pos := *(base.Position) + pos.Column++ + return token.New("null", "null", &pos) +} + +func (p *parser) parseMapValue(ctx *context, key ast.MapKeyNode, colonToken *token.Token) (ast.Node, error) { + node, err := p.createMapValueNode(ctx, key, colonToken) + if err != nil { + return nil, errors.Wrapf(err, "failed to create map value node") + } + if node != nil && node.GetPath() == "" { + node.SetPath(ctx.path) + } + return node, nil +} + +func (p *parser) createMapValueNode(ctx *context, key ast.MapKeyNode, colonToken *token.Token) (ast.Node, error) { + tk := ctx.currentToken() + if tk == nil { + nullToken := p.createNullToken(colonToken) + ctx.insertToken(ctx.idx, nullToken) + return ast.Null(nullToken), nil + } + + if tk.Position.Column == key.GetToken().Position.Column && tk.Type == token.StringType { + // in this case, + // ---- + // key: + // next + nullToken := p.createNullToken(colonToken) + ctx.insertToken(ctx.idx, nullToken) + return ast.Null(nullToken), nil + } + + if tk.Position.Column < key.GetToken().Position.Column { + // in this case, + // ---- + // key: + // next + nullToken := p.createNullToken(colonToken) + ctx.insertToken(ctx.idx, nullToken) + return ast.Null(nullToken), nil + } + + value, err := p.parseToken(ctx, ctx.currentToken()) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse mapping 'value' node") + } + return value, nil +} + +func (p *parser) validateMapValue(ctx *context, key, value ast.Node) error { + keyColumn := key.GetToken().Position.Column + valueColumn := value.GetToken().Position.Column + if keyColumn != valueColumn { + return nil + } + if value.Type() != ast.StringType { + return nil + } + ntk := ctx.nextToken() + if ntk == nil || (ntk.Type != token.MappingValueType && ntk.Type != token.SequenceEntryType) { + return errors.ErrSyntax("could not found expected ':' token", value.GetToken()) + } + return nil +} + +func (p *parser) parseMappingValue(ctx *context) (ast.Node, error) { + key, err := p.parseMapKey(ctx) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse map key") + } + keyText := key.GetToken().Value + key.SetPath(ctx.withChild(keyText).path) + if err := p.validateMapKey(key.GetToken()); err != nil { + return nil, errors.Wrapf(err, "validate mapping key error") + } + ctx.progress(1) // progress to mapping value token + tk := ctx.currentToken() // get mapping value token + if tk == nil { + return nil, errors.ErrSyntax("unexpected map", key.GetToken()) + } + ctx.progress(1) // progress to value token + if err := p.setSameLineCommentIfExists(ctx.withChild(keyText), key); err != nil { + return nil, errors.Wrapf(err, "failed to set same line comment to node") + } + if key.GetComment() != nil { + // if current token is comment, GetComment() is not nil. + // then progress to value token + ctx.progressIgnoreComment(1) + } + + value, err := p.parseMapValue(ctx.withChild(keyText), key, tk) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse map value") + } + if err := p.validateMapValue(ctx, key, value); err != nil { + return nil, errors.Wrapf(err, "failed to validate map value") + } + + mvnode := ast.MappingValue(tk, key, value) + mvnode.SetPath(ctx.withChild(keyText).path) + node := ast.Mapping(tk, false, mvnode) + node.SetPath(ctx.withChild(keyText).path) + + ntk := ctx.nextNotCommentToken() + antk := ctx.afterNextNotCommentToken() + for antk != nil && antk.Type == token.MappingValueType && + ntk.Position.Column == key.GetToken().Position.Column { + ctx.progressIgnoreComment(1) + value, err := p.parseToken(ctx, ctx.currentToken()) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse mapping node") + } + switch value.Type() { + case ast.MappingType: + c := value.(*ast.MappingNode) + comment := c.GetComment() + for idx, v := range c.Values { + if idx == 0 && comment != nil { + if err := v.SetComment(comment); err != nil { + return nil, errors.Wrapf(err, "failed to set comment token to node") + } + } + node.Values = append(node.Values, v) + } + case ast.MappingValueType: + node.Values = append(node.Values, value.(*ast.MappingValueNode)) + default: + return nil, xerrors.Errorf("failed to parse mapping value node node is %s", value.Type()) + } + ntk = ctx.nextNotCommentToken() + antk = ctx.afterNextNotCommentToken() + } + if len(node.Values) == 1 { + mapKeyCol := mvnode.Key.GetToken().Position.Column + commentTk := ctx.nextToken() + if commentTk != nil && commentTk.Type == token.CommentType && mapKeyCol <= commentTk.Position.Column { + // If the comment is in the same or deeper column as the last element column in map value, + // treat it as a footer comment for the last element. + comment := p.parseFootComment(ctx, mapKeyCol) + mvnode.FootComment = comment + } + return mvnode, nil + } + mapCol := node.GetToken().Position.Column + commentTk := ctx.nextToken() + if commentTk != nil && commentTk.Type == token.CommentType && mapCol <= commentTk.Position.Column { + // If the comment is in the same or deeper column as the last element column in map value, + // treat it as a footer comment for the last element. + comment := p.parseFootComment(ctx, mapCol) + node.FootComment = comment + } + return node, nil +} + +func (p *parser) parseSequenceEntry(ctx *context) (*ast.SequenceNode, error) { + tk := ctx.currentToken() + sequenceNode := ast.Sequence(tk, false) + sequenceNode.SetPath(ctx.path) + curColumn := tk.Position.Column + for tk.Type == token.SequenceEntryType { + ctx.progress(1) // skip sequence token + tk = ctx.currentToken() + if tk == nil { + return nil, errors.ErrSyntax("empty sequence entry", ctx.previousToken()) + } + var comment *ast.CommentGroupNode + if tk.Type == token.CommentType { + comment = p.parseCommentOnly(ctx) + tk = ctx.currentToken() + if tk.Type != token.SequenceEntryType { + break + } + ctx.progress(1) // skip sequence token + } + value, err := p.parseToken(ctx.withIndex(uint(len(sequenceNode.Values))), ctx.currentToken()) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse sequence") + } + if comment != nil { + comment.SetPath(ctx.withIndex(uint(len(sequenceNode.Values))).path) + sequenceNode.ValueHeadComments = append(sequenceNode.ValueHeadComments, comment) + } else { + sequenceNode.ValueHeadComments = append(sequenceNode.ValueHeadComments, nil) + } + sequenceNode.Values = append(sequenceNode.Values, value) + tk = ctx.nextNotCommentToken() + if tk == nil { + break + } + if tk.Type != token.SequenceEntryType { + break + } + if tk.Position.Column != curColumn { + break + } + ctx.progressIgnoreComment(1) + } + commentTk := ctx.nextToken() + if commentTk != nil && commentTk.Type == token.CommentType && curColumn <= commentTk.Position.Column { + // If the comment is in the same or deeper column as the last element column in sequence value, + // treat it as a footer comment for the last element. + comment := p.parseFootComment(ctx, curColumn) + sequenceNode.FootComment = comment + } + return sequenceNode, nil +} + +func (p *parser) parseAnchor(ctx *context) (*ast.AnchorNode, error) { + tk := ctx.currentToken() + anchor := ast.Anchor(tk) + anchor.SetPath(ctx.path) + ntk := ctx.nextToken() + if ntk == nil { + return nil, errors.ErrSyntax("unexpected anchor. anchor name is undefined", tk) + } + ctx.progress(1) // skip anchor token + name, err := p.parseToken(ctx, ctx.currentToken()) + if err != nil { + return nil, errors.Wrapf(err, "failed to parser anchor name node") + } + anchor.Name = name + ntk = ctx.nextToken() + if ntk == nil { + return nil, errors.ErrSyntax("unexpected anchor. anchor value is undefined", ctx.currentToken()) + } + ctx.progress(1) + value, err := p.parseToken(ctx, ctx.currentToken()) + if err != nil { + return nil, errors.Wrapf(err, "failed to parser anchor name node") + } + anchor.Value = value + return anchor, nil +} + +func (p *parser) parseAlias(ctx *context) (*ast.AliasNode, error) { + tk := ctx.currentToken() + alias := ast.Alias(tk) + alias.SetPath(ctx.path) + ntk := ctx.nextToken() + if ntk == nil { + return nil, errors.ErrSyntax("unexpected alias. alias name is undefined", tk) + } + ctx.progress(1) // skip alias token + name, err := p.parseToken(ctx, ctx.currentToken()) + if err != nil { + return nil, errors.Wrapf(err, "failed to parser alias name node") + } + alias.Value = name + return alias, nil +} + +func (p *parser) parseMapKey(ctx *context) (ast.MapKeyNode, error) { + tk := ctx.currentToken() + if value := p.parseScalarValue(tk); value != nil { + return value, nil + } + switch tk.Type { + case token.MergeKeyType: + return ast.MergeKey(tk), nil + case token.MappingKeyType: + return p.parseMappingKey(ctx) + } + return nil, errors.ErrSyntax("unexpected mapping key", tk) +} + +func (p *parser) parseStringValue(tk *token.Token) *ast.StringNode { + switch tk.Type { + case token.StringType, + token.SingleQuoteType, + token.DoubleQuoteType: + return ast.String(tk) + } + return nil +} + +func (p *parser) parseScalarValueWithComment(ctx *context, tk *token.Token) (ast.ScalarNode, error) { + node := p.parseScalarValue(tk) + if node == nil { + return nil, nil + } + node.SetPath(ctx.path) + if p.isSameLineComment(ctx.nextToken(), node) { + ctx.progress(1) + if err := p.setSameLineCommentIfExists(ctx, node); err != nil { + return nil, errors.Wrapf(err, "failed to set same line comment to node") + } + } + return node, nil +} + +func (p *parser) parseScalarValue(tk *token.Token) ast.ScalarNode { + if node := p.parseStringValue(tk); node != nil { + return node + } + switch tk.Type { + case token.NullType: + return ast.Null(tk) + case token.BoolType: + return ast.Bool(tk) + case token.IntegerType, + token.BinaryIntegerType, + token.OctetIntegerType, + token.HexIntegerType: + return ast.Integer(tk) + case token.FloatType: + return ast.Float(tk) + case token.InfinityType: + return ast.Infinity(tk) + case token.NanType: + return ast.Nan(tk) + } + return nil +} + +func (p *parser) parseDirective(ctx *context) (*ast.DirectiveNode, error) { + node := ast.Directive(ctx.currentToken()) + ctx.progress(1) // skip directive token + value, err := p.parseToken(ctx, ctx.currentToken()) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse directive value") + } + node.Value = value + ctx.progress(1) + tk := ctx.currentToken() + if tk == nil { + // Since current token is nil, use the previous token to specify + // the syntax error location. + return nil, errors.ErrSyntax("unexpected directive value. document not started", ctx.previousToken()) + } + if tk.Type != token.DocumentHeaderType { + return nil, errors.ErrSyntax("unexpected directive value. document not started", ctx.currentToken()) + } + return node, nil +} + +func (p *parser) parseLiteral(ctx *context) (*ast.LiteralNode, error) { + node := ast.Literal(ctx.currentToken()) + ctx.progress(1) // skip literal/folded token + + tk := ctx.currentToken() + var comment *ast.CommentGroupNode + if tk.Type == token.CommentType { + comment = p.parseCommentOnly(ctx) + comment.SetPath(ctx.path) + if err := node.SetComment(comment); err != nil { + return nil, errors.Wrapf(err, "failed to set comment to literal") + } + tk = ctx.currentToken() + } + value, err := p.parseToken(ctx, tk) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse literal/folded value") + } + snode, ok := value.(*ast.StringNode) + if !ok { + return nil, errors.ErrSyntax("unexpected token. required string token", value.GetToken()) + } + node.Value = snode + return node, nil +} + +func (p *parser) isSameLineComment(tk *token.Token, node ast.Node) bool { + if tk == nil { + return false + } + if tk.Type != token.CommentType { + return false + } + return tk.Position.Line == node.GetToken().Position.Line +} + +func (p *parser) setSameLineCommentIfExists(ctx *context, node ast.Node) error { + tk := ctx.currentToken() + if !p.isSameLineComment(tk, node) { + return nil + } + comment := ast.CommentGroup([]*token.Token{tk}) + comment.SetPath(ctx.path) + if err := node.SetComment(comment); err != nil { + return errors.Wrapf(err, "failed to set comment token to ast.Node") + } + return nil +} + +func (p *parser) parseDocument(ctx *context) (*ast.DocumentNode, error) { + startTk := ctx.currentToken() + ctx.progress(1) // skip document header token + body, err := p.parseToken(ctx, ctx.currentToken()) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse document body") + } + node := ast.Document(startTk, body) + if ntk := ctx.nextToken(); ntk != nil && ntk.Type == token.DocumentEndType { + node.End = ntk + ctx.progress(1) + } + return node, nil +} + +func (p *parser) parseCommentOnly(ctx *context) *ast.CommentGroupNode { + commentTokens := []*token.Token{} + for { + tk := ctx.currentToken() + if tk == nil { + break + } + if tk.Type != token.CommentType { + break + } + commentTokens = append(commentTokens, tk) + ctx.progressIgnoreComment(1) // skip comment token + } + return ast.CommentGroup(commentTokens) +} + +func (p *parser) parseFootComment(ctx *context, col int) *ast.CommentGroupNode { + commentTokens := []*token.Token{} + for { + ctx.progressIgnoreComment(1) + commentTokens = append(commentTokens, ctx.currentToken()) + + nextTk := ctx.nextToken() + if nextTk == nil { + break + } + if nextTk.Type != token.CommentType { + break + } + if col > nextTk.Position.Column { + break + } + } + return ast.CommentGroup(commentTokens) +} + +func (p *parser) parseComment(ctx *context) (ast.Node, error) { + group := p.parseCommentOnly(ctx) + node, err := p.parseToken(ctx, ctx.currentToken()) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse node after comment") + } + if node == nil { + return group, nil + } + group.SetPath(node.GetPath()) + if err := node.SetComment(group); err != nil { + return nil, errors.Wrapf(err, "failed to set comment token to node") + } + return node, nil +} + +func (p *parser) parseMappingKey(ctx *context) (*ast.MappingKeyNode, error) { + keyTk := ctx.currentToken() + node := ast.MappingKey(keyTk) + node.SetPath(ctx.path) + ctx.progress(1) // skip mapping key token + value, err := p.parseToken(ctx.withChild(keyTk.Value), ctx.currentToken()) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse map key") + } + node.Value = value + return node, nil +} + +func (p *parser) parseToken(ctx *context, tk *token.Token) (ast.Node, error) { + node, err := p.createNodeFromToken(ctx, tk) + if err != nil { + return nil, errors.Wrapf(err, "failed to create node from token") + } + if node != nil && node.GetPath() == "" { + node.SetPath(ctx.path) + } + return node, nil +} + +func (p *parser) createNodeFromToken(ctx *context, tk *token.Token) (ast.Node, error) { + if tk == nil { + return nil, nil + } + if tk.NextType() == token.MappingValueType { + node, err := p.parseMappingValue(ctx) + return node, err + } + node, err := p.parseScalarValueWithComment(ctx, tk) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse scalar value") + } + if node != nil { + return node, nil + } + switch tk.Type { + case token.CommentType: + return p.parseComment(ctx) + case token.MappingKeyType: + return p.parseMappingKey(ctx) + case token.DocumentHeaderType: + return p.parseDocument(ctx) + case token.MappingStartType: + return p.parseMapping(ctx) + case token.SequenceStartType: + return p.parseSequence(ctx) + case token.SequenceEntryType: + return p.parseSequenceEntry(ctx) + case token.AnchorType: + return p.parseAnchor(ctx) + case token.AliasType: + return p.parseAlias(ctx) + case token.DirectiveType: + return p.parseDirective(ctx) + case token.TagType: + return p.parseTag(ctx) + case token.LiteralType, token.FoldedType: + return p.parseLiteral(ctx) + } + return nil, nil +} + +func (p *parser) parse(tokens token.Tokens, mode Mode) (*ast.File, error) { + ctx := newContext(tokens, mode) + file := &ast.File{Docs: []*ast.DocumentNode{}} + for ctx.next() { + node, err := p.parseToken(ctx, ctx.currentToken()) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse") + } + ctx.progressIgnoreComment(1) + if node == nil { + continue + } + if doc, ok := node.(*ast.DocumentNode); ok { + file.Docs = append(file.Docs, doc) + } else { + file.Docs = append(file.Docs, ast.Document(nil, node)) + } + } + return file, nil +} + +type Mode uint + +const ( + ParseComments Mode = 1 << iota // parse comments and add them to AST +) + +// ParseBytes parse from byte slice, and returns ast.File +func ParseBytes(bytes []byte, mode Mode) (*ast.File, error) { + tokens := lexer.Tokenize(string(bytes)) + f, err := Parse(tokens, mode) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse") + } + return f, nil +} + +// Parse parse from token instances, and returns ast.File +func Parse(tokens token.Tokens, mode Mode) (*ast.File, error) { + var p parser + f, err := p.parse(tokens, mode) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse") + } + return f, nil +} + +// Parse parse from filename, and returns ast.File +func ParseFile(filename string, mode Mode) (*ast.File, error) { + file, err := ioutil.ReadFile(filename) + if err != nil { + return nil, errors.Wrapf(err, "failed to read file: %s", filename) + } + f, err := ParseBytes(file, mode) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse") + } + f.Name = filename + return f, nil +} diff --git a/vendor/github.com/goccy/go-yaml/path.go b/vendor/github.com/goccy/go-yaml/path.go new file mode 100644 index 000000000..7a0c3b115 --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/path.go @@ -0,0 +1,794 @@ +package yaml + +import ( + "bytes" + "fmt" + "io" + "strconv" + "strings" + + "github.com/goccy/go-yaml/ast" + "github.com/goccy/go-yaml/internal/errors" + "github.com/goccy/go-yaml/parser" + "github.com/goccy/go-yaml/printer" +) + +// PathString create Path from string +// +// YAMLPath rule +// $ : the root object/element +// . : child operator +// .. : recursive descent +// [num] : object/element of array by number +// [*] : all objects/elements for array. +// +// If you want to use reserved characters such as `.` and `*` as a key name, +// enclose them in single quotation as follows ( $.foo.'bar.baz-*'.hoge ). +// If you want to use a single quote with reserved characters, escape it with `\` ( $.foo.'bar.baz\'s value'.hoge ). +func PathString(s string) (*Path, error) { + buf := []rune(s) + length := len(buf) + cursor := 0 + builder := &PathBuilder{} + for cursor < length { + c := buf[cursor] + switch c { + case '$': + builder = builder.Root() + cursor++ + case '.': + b, buf, c, err := parsePathDot(builder, buf, cursor) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse path of dot") + } + length = len(buf) + builder = b + cursor = c + case '[': + b, buf, c, err := parsePathIndex(builder, buf, cursor) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse path of index") + } + length = len(buf) + builder = b + cursor = c + default: + return nil, errors.Wrapf(ErrInvalidPathString, "invalid path at %d", cursor) + } + } + return builder.Build(), nil +} + +func parsePathRecursive(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []rune, int, error) { + length := len(buf) + cursor += 2 // skip .. characters + start := cursor + for ; cursor < length; cursor++ { + c := buf[cursor] + switch c { + case '$': + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '$' after '..' character") + case '*': + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '*' after '..' character") + case '.', '[': + goto end + case ']': + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified ']' after '..' character") + } + } +end: + if start == cursor { + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "not found recursive selector") + } + return b.Recursive(string(buf[start:cursor])), buf, cursor, nil +} + +func parsePathDot(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []rune, int, error) { + length := len(buf) + if cursor+1 < length && buf[cursor+1] == '.' { + b, buf, c, err := parsePathRecursive(b, buf, cursor) + if err != nil { + return nil, nil, 0, errors.Wrapf(err, "failed to parse path of recursive") + } + return b, buf, c, nil + } + cursor++ // skip . character + start := cursor + + // if started single quote, looking for end single quote char + if cursor < length && buf[cursor] == '\'' { + return parseQuotedKey(b, buf, cursor) + } + for ; cursor < length; cursor++ { + c := buf[cursor] + switch c { + case '$': + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '$' after '.' character") + case '*': + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '*' after '.' character") + case '.', '[': + goto end + case ']': + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified ']' after '.' character") + } + } +end: + if start == cursor { + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "cloud not find by empty key") + } + return b.child(string(buf[start:cursor])), buf, cursor, nil +} + +func parseQuotedKey(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []rune, int, error) { + cursor++ // skip single quote + start := cursor + length := len(buf) + var foundEndDelim bool + for ; cursor < length; cursor++ { + switch buf[cursor] { + case '\\': + buf = append(append([]rune{}, buf[:cursor]...), buf[cursor+1:]...) + length = len(buf) + case '\'': + foundEndDelim = true + goto end + } + } +end: + if !foundEndDelim { + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "could not find end delimiter for key") + } + if start == cursor { + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "could not find by empty key") + } + selector := buf[start:cursor] + cursor++ + if cursor < length { + switch buf[cursor] { + case '$': + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '$' after '.' character") + case '*': + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '*' after '.' character") + case ']': + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified ']' after '.' character") + } + } + return b.child(string(selector)), buf, cursor, nil +} + +func parsePathIndex(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []rune, int, error) { + length := len(buf) + cursor++ // skip '[' character + if length <= cursor { + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "unexpected end of YAML Path") + } + c := buf[cursor] + switch c { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*': + start := cursor + cursor++ + for ; cursor < length; cursor++ { + c := buf[cursor] + switch c { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + continue + } + break + } + if buf[cursor] != ']' { + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "invalid character %s at %d", string(buf[cursor]), cursor) + } + numOrAll := string(buf[start:cursor]) + if numOrAll == "*" { + return b.IndexAll(), buf, cursor + 1, nil + } + num, err := strconv.ParseInt(numOrAll, 10, 64) + if err != nil { + return nil, nil, 0, errors.Wrapf(err, "failed to parse number") + } + return b.Index(uint(num)), buf, cursor + 1, nil + } + return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "invalid character %s at %d", c, cursor) +} + +// Path represent YAMLPath ( like a JSONPath ). +type Path struct { + node pathNode +} + +// String path to text. +func (p *Path) String() string { + return p.node.String() +} + +// Read decode from r and set extracted value by YAMLPath to v. +func (p *Path) Read(r io.Reader, v interface{}) error { + node, err := p.ReadNode(r) + if err != nil { + return errors.Wrapf(err, "failed to read node") + } + if err := Unmarshal([]byte(node.String()), v); err != nil { + return errors.Wrapf(err, "failed to unmarshal") + } + return nil +} + +// ReadNode create AST from r and extract node by YAMLPath. +func (p *Path) ReadNode(r io.Reader) (ast.Node, error) { + if p.node == nil { + return nil, ErrInvalidPath + } + var buf bytes.Buffer + if _, err := io.Copy(&buf, r); err != nil { + return nil, errors.Wrapf(err, "failed to copy from reader") + } + f, err := parser.ParseBytes(buf.Bytes(), 0) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse yaml") + } + node, err := p.FilterFile(f) + if err != nil { + return nil, errors.Wrapf(err, "failed to filter from ast.File") + } + return node, nil +} + +// Filter filter from target by YAMLPath and set it to v. +func (p *Path) Filter(target, v interface{}) error { + b, err := Marshal(target) + if err != nil { + return errors.Wrapf(err, "failed to marshal target value") + } + if err := p.Read(bytes.NewBuffer(b), v); err != nil { + return errors.Wrapf(err, "failed to read") + } + return nil +} + +// FilterFile filter from ast.File by YAMLPath. +func (p *Path) FilterFile(f *ast.File) (ast.Node, error) { + for _, doc := range f.Docs { + node, err := p.FilterNode(doc.Body) + if err != nil { + return nil, errors.Wrapf(err, "failed to filter node by path ( %s )", p.node) + } + if node != nil { + return node, nil + } + } + return nil, errors.Wrapf(ErrNotFoundNode, "failed to find path ( %s )", p.node) +} + +// FilterNode filter from node by YAMLPath. +func (p *Path) FilterNode(node ast.Node) (ast.Node, error) { + n, err := p.node.filter(node) + if err != nil { + return nil, errors.Wrapf(err, "failed to filter node by path ( %s )", p.node) + } + return n, nil +} + +// MergeFromReader merge YAML text into ast.File. +func (p *Path) MergeFromReader(dst *ast.File, src io.Reader) error { + var buf bytes.Buffer + if _, err := io.Copy(&buf, src); err != nil { + return errors.Wrapf(err, "failed to copy from reader") + } + file, err := parser.ParseBytes(buf.Bytes(), 0) + if err != nil { + return errors.Wrapf(err, "failed to parse") + } + if err := p.MergeFromFile(dst, file); err != nil { + return errors.Wrapf(err, "failed to merge file") + } + return nil +} + +// MergeFromFile merge ast.File into ast.File. +func (p *Path) MergeFromFile(dst *ast.File, src *ast.File) error { + base, err := p.FilterFile(dst) + if err != nil { + return errors.Wrapf(err, "failed to filter file") + } + for _, doc := range src.Docs { + if err := ast.Merge(base, doc); err != nil { + return errors.Wrapf(err, "failed to merge") + } + } + return nil +} + +// MergeFromNode merge ast.Node into ast.File. +func (p *Path) MergeFromNode(dst *ast.File, src ast.Node) error { + base, err := p.FilterFile(dst) + if err != nil { + return errors.Wrapf(err, "failed to filter file") + } + if err := ast.Merge(base, src); err != nil { + return errors.Wrapf(err, "failed to merge") + } + return nil +} + +// ReplaceWithReader replace ast.File with io.Reader. +func (p *Path) ReplaceWithReader(dst *ast.File, src io.Reader) error { + var buf bytes.Buffer + if _, err := io.Copy(&buf, src); err != nil { + return errors.Wrapf(err, "failed to copy from reader") + } + file, err := parser.ParseBytes(buf.Bytes(), 0) + if err != nil { + return errors.Wrapf(err, "failed to parse") + } + if err := p.ReplaceWithFile(dst, file); err != nil { + return errors.Wrapf(err, "failed to replace file") + } + return nil +} + +// ReplaceWithFile replace ast.File with ast.File. +func (p *Path) ReplaceWithFile(dst *ast.File, src *ast.File) error { + for _, doc := range src.Docs { + if err := p.ReplaceWithNode(dst, doc); err != nil { + return errors.Wrapf(err, "failed to replace file by path ( %s )", p.node) + } + } + return nil +} + +// ReplaceNode replace ast.File with ast.Node. +func (p *Path) ReplaceWithNode(dst *ast.File, node ast.Node) error { + for _, doc := range dst.Docs { + if node.Type() == ast.DocumentType { + node = node.(*ast.DocumentNode).Body + } + if err := p.node.replace(doc.Body, node); err != nil { + return errors.Wrapf(err, "failed to replace node by path ( %s )", p.node) + } + } + return nil +} + +// AnnotateSource add annotation to passed source ( see section 5.1 in README.md ). +func (p *Path) AnnotateSource(source []byte, colored bool) ([]byte, error) { + file, err := parser.ParseBytes([]byte(source), 0) + if err != nil { + return nil, err + } + node, err := p.FilterFile(file) + if err != nil { + return nil, err + } + var pp printer.Printer + return []byte(pp.PrintErrorToken(node.GetToken(), colored)), nil +} + +// PathBuilder represent builder for YAMLPath. +type PathBuilder struct { + root *rootNode + node pathNode +} + +// Root add '$' to current path. +func (b *PathBuilder) Root() *PathBuilder { + root := newRootNode() + return &PathBuilder{root: root, node: root} +} + +// IndexAll add '[*]' to current path. +func (b *PathBuilder) IndexAll() *PathBuilder { + b.node = b.node.chain(newIndexAllNode()) + return b +} + +// Recursive add '..selector' to current path. +func (b *PathBuilder) Recursive(selector string) *PathBuilder { + b.node = b.node.chain(newRecursiveNode(selector)) + return b +} + +func (b *PathBuilder) containsReservedPathCharacters(path string) bool { + if strings.Contains(path, ".") { + return true + } + if strings.Contains(path, "*") { + return true + } + return false +} + +func (b *PathBuilder) enclosedSingleQuote(name string) bool { + return strings.HasPrefix(name, "'") && strings.HasSuffix(name, "'") +} + +func (b *PathBuilder) normalizeSelectorName(name string) string { + if b.enclosedSingleQuote(name) { + // already escaped name + return name + } + if b.containsReservedPathCharacters(name) { + escapedName := strings.ReplaceAll(name, `'`, `\'`) + return "'" + escapedName + "'" + } + return name +} + +func (b *PathBuilder) child(name string) *PathBuilder { + b.node = b.node.chain(newSelectorNode(name)) + return b +} + +// Child add '.name' to current path. +func (b *PathBuilder) Child(name string) *PathBuilder { + return b.child(b.normalizeSelectorName(name)) +} + +// Index add '[idx]' to current path. +func (b *PathBuilder) Index(idx uint) *PathBuilder { + b.node = b.node.chain(newIndexNode(idx)) + return b +} + +// Build build YAMLPath. +func (b *PathBuilder) Build() *Path { + return &Path{node: b.root} +} + +type pathNode interface { + fmt.Stringer + chain(pathNode) pathNode + filter(ast.Node) (ast.Node, error) + replace(ast.Node, ast.Node) error +} + +type basePathNode struct { + child pathNode +} + +func (n *basePathNode) chain(node pathNode) pathNode { + n.child = node + return node +} + +type rootNode struct { + *basePathNode +} + +func newRootNode() *rootNode { + return &rootNode{basePathNode: &basePathNode{}} +} + +func (n *rootNode) String() string { + s := "$" + if n.child != nil { + s += n.child.String() + } + return s +} + +func (n *rootNode) filter(node ast.Node) (ast.Node, error) { + if n.child == nil { + return nil, nil + } + filtered, err := n.child.filter(node) + if err != nil { + return nil, errors.Wrapf(err, "failed to filter") + } + return filtered, nil +} + +func (n *rootNode) replace(node ast.Node, target ast.Node) error { + if n.child == nil { + return nil + } + if err := n.child.replace(node, target); err != nil { + return errors.Wrapf(err, "failed to replace") + } + return nil +} + +type selectorNode struct { + *basePathNode + selector string +} + +func newSelectorNode(selector string) *selectorNode { + return &selectorNode{ + basePathNode: &basePathNode{}, + selector: selector, + } +} + +func (n *selectorNode) filter(node ast.Node) (ast.Node, error) { + switch node.Type() { + case ast.MappingType: + for _, value := range node.(*ast.MappingNode).Values { + key := value.Key.GetToken().Value + if key == n.selector { + if n.child == nil { + return value.Value, nil + } + filtered, err := n.child.filter(value.Value) + if err != nil { + return nil, errors.Wrapf(err, "failed to filter") + } + return filtered, nil + } + } + case ast.MappingValueType: + value := node.(*ast.MappingValueNode) + key := value.Key.GetToken().Value + if key == n.selector { + if n.child == nil { + return value.Value, nil + } + filtered, err := n.child.filter(value.Value) + if err != nil { + return nil, errors.Wrapf(err, "failed to filter") + } + return filtered, nil + } + default: + return nil, errors.Wrapf(ErrInvalidQuery, "expected node type is map or map value. but got %s", node.Type()) + } + return nil, nil +} + +func (n *selectorNode) replaceMapValue(value *ast.MappingValueNode, target ast.Node) error { + key := value.Key.GetToken().Value + if key != n.selector { + return nil + } + if n.child == nil { + if err := value.Replace(target); err != nil { + return errors.Wrapf(err, "failed to replace") + } + } else { + if err := n.child.replace(value.Value, target); err != nil { + return errors.Wrapf(err, "failed to replace") + } + } + return nil +} + +func (n *selectorNode) replace(node ast.Node, target ast.Node) error { + switch node.Type() { + case ast.MappingType: + for _, value := range node.(*ast.MappingNode).Values { + if err := n.replaceMapValue(value, target); err != nil { + return errors.Wrapf(err, "failed to replace map value") + } + } + case ast.MappingValueType: + value := node.(*ast.MappingValueNode) + if err := n.replaceMapValue(value, target); err != nil { + return errors.Wrapf(err, "failed to replace map value") + } + default: + return errors.Wrapf(ErrInvalidQuery, "expected node type is map or map value. but got %s", node.Type()) + } + return nil +} + +func (n *selectorNode) String() string { + s := fmt.Sprintf(".%s", n.selector) + if n.child != nil { + s += n.child.String() + } + return s +} + +type indexNode struct { + *basePathNode + selector uint +} + +func newIndexNode(selector uint) *indexNode { + return &indexNode{ + basePathNode: &basePathNode{}, + selector: selector, + } +} + +func (n *indexNode) filter(node ast.Node) (ast.Node, error) { + if node.Type() != ast.SequenceType { + return nil, errors.Wrapf(ErrInvalidQuery, "expected sequence type node. but got %s", node.Type()) + } + sequence := node.(*ast.SequenceNode) + if n.selector >= uint(len(sequence.Values)) { + return nil, errors.Wrapf(ErrInvalidQuery, "expected index is %d. but got sequences has %d items", n.selector, sequence.Values) + } + value := sequence.Values[n.selector] + if n.child == nil { + return value, nil + } + filtered, err := n.child.filter(value) + if err != nil { + return nil, errors.Wrapf(err, "failed to filter") + } + return filtered, nil +} + +func (n *indexNode) replace(node ast.Node, target ast.Node) error { + if node.Type() != ast.SequenceType { + return errors.Wrapf(ErrInvalidQuery, "expected sequence type node. but got %s", node.Type()) + } + sequence := node.(*ast.SequenceNode) + if n.selector >= uint(len(sequence.Values)) { + return errors.Wrapf(ErrInvalidQuery, "expected index is %d. but got sequences has %d items", n.selector, sequence.Values) + } + if n.child == nil { + if err := sequence.Replace(int(n.selector), target); err != nil { + return errors.Wrapf(err, "failed to replace") + } + return nil + } + if err := n.child.replace(sequence.Values[n.selector], target); err != nil { + return errors.Wrapf(err, "failed to replace") + } + return nil +} + +func (n *indexNode) String() string { + s := fmt.Sprintf("[%d]", n.selector) + if n.child != nil { + s += n.child.String() + } + return s +} + +type indexAllNode struct { + *basePathNode +} + +func newIndexAllNode() *indexAllNode { + return &indexAllNode{ + basePathNode: &basePathNode{}, + } +} + +func (n *indexAllNode) String() string { + s := "[*]" + if n.child != nil { + s += n.child.String() + } + return s +} + +func (n *indexAllNode) filter(node ast.Node) (ast.Node, error) { + if node.Type() != ast.SequenceType { + return nil, errors.Wrapf(ErrInvalidQuery, "expected sequence type node. but got %s", node.Type()) + } + sequence := node.(*ast.SequenceNode) + if n.child == nil { + return sequence, nil + } + out := *sequence + out.Values = []ast.Node{} + for _, value := range sequence.Values { + filtered, err := n.child.filter(value) + if err != nil { + return nil, errors.Wrapf(err, "failed to filter") + } + out.Values = append(out.Values, filtered) + } + return &out, nil +} + +func (n *indexAllNode) replace(node ast.Node, target ast.Node) error { + if node.Type() != ast.SequenceType { + return errors.Wrapf(ErrInvalidQuery, "expected sequence type node. but got %s", node.Type()) + } + sequence := node.(*ast.SequenceNode) + if n.child == nil { + for idx := range sequence.Values { + if err := sequence.Replace(idx, target); err != nil { + return errors.Wrapf(err, "failed to replace") + } + } + return nil + } + for _, value := range sequence.Values { + if err := n.child.replace(value, target); err != nil { + return errors.Wrapf(err, "failed to replace") + } + } + return nil +} + +type recursiveNode struct { + *basePathNode + selector string +} + +func newRecursiveNode(selector string) *recursiveNode { + return &recursiveNode{ + basePathNode: &basePathNode{}, + selector: selector, + } +} + +func (n *recursiveNode) String() string { + s := fmt.Sprintf("..%s", n.selector) + if n.child != nil { + s += n.child.String() + } + return s +} + +func (n *recursiveNode) filterNode(node ast.Node) (*ast.SequenceNode, error) { + sequence := &ast.SequenceNode{BaseNode: &ast.BaseNode{}} + switch typedNode := node.(type) { + case *ast.MappingNode: + for _, value := range typedNode.Values { + seq, err := n.filterNode(value) + if err != nil { + return nil, errors.Wrapf(err, "failed to filter") + } + sequence.Values = append(sequence.Values, seq.Values...) + } + case *ast.MappingValueNode: + key := typedNode.Key.GetToken().Value + if n.selector == key { + sequence.Values = append(sequence.Values, typedNode.Value) + } + seq, err := n.filterNode(typedNode.Value) + if err != nil { + return nil, errors.Wrapf(err, "failed to filter") + } + sequence.Values = append(sequence.Values, seq.Values...) + case *ast.SequenceNode: + for _, value := range typedNode.Values { + seq, err := n.filterNode(value) + if err != nil { + return nil, errors.Wrapf(err, "failed to filter") + } + sequence.Values = append(sequence.Values, seq.Values...) + } + } + return sequence, nil +} + +func (n *recursiveNode) filter(node ast.Node) (ast.Node, error) { + sequence, err := n.filterNode(node) + if err != nil { + return nil, errors.Wrapf(err, "failed to filter") + } + sequence.Start = node.GetToken() + return sequence, nil +} + +func (n *recursiveNode) replaceNode(node ast.Node, target ast.Node) error { + switch typedNode := node.(type) { + case *ast.MappingNode: + for _, value := range typedNode.Values { + if err := n.replaceNode(value, target); err != nil { + return errors.Wrapf(err, "failed to replace") + } + } + case *ast.MappingValueNode: + key := typedNode.Key.GetToken().Value + if n.selector == key { + if err := typedNode.Replace(target); err != nil { + return errors.Wrapf(err, "failed to replace") + } + } + if err := n.replaceNode(typedNode.Value, target); err != nil { + return errors.Wrapf(err, "failed to replace") + } + case *ast.SequenceNode: + for _, value := range typedNode.Values { + if err := n.replaceNode(value, target); err != nil { + return errors.Wrapf(err, "failed to replace") + } + } + } + return nil +} + +func (n *recursiveNode) replace(node ast.Node, target ast.Node) error { + if err := n.replaceNode(node, target); err != nil { + return errors.Wrapf(err, "failed to replace") + } + return nil +} diff --git a/vendor/github.com/goccy/go-yaml/printer/printer.go b/vendor/github.com/goccy/go-yaml/printer/printer.go new file mode 100644 index 000000000..d5e25dc91 --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/printer/printer.go @@ -0,0 +1,352 @@ +package printer + +import ( + "fmt" + "math" + "strings" + + "github.com/fatih/color" + "github.com/goccy/go-yaml/ast" + "github.com/goccy/go-yaml/token" +) + +// Property additional property set for each the token +type Property struct { + Prefix string + Suffix string +} + +// PrintFunc returns property instance +type PrintFunc func() *Property + +// Printer create text from token collection or ast +type Printer struct { + LineNumber bool + LineNumberFormat func(num int) string + MapKey PrintFunc + Anchor PrintFunc + Alias PrintFunc + Bool PrintFunc + String PrintFunc + Number PrintFunc +} + +func defaultLineNumberFormat(num int) string { + return fmt.Sprintf("%2d | ", num) +} + +func (p *Printer) property(tk *token.Token) *Property { + prop := &Property{} + switch tk.PreviousType() { + case token.AnchorType: + if p.Anchor != nil { + return p.Anchor() + } + return prop + case token.AliasType: + if p.Alias != nil { + return p.Alias() + } + return prop + } + switch tk.NextType() { + case token.MappingValueType: + if p.MapKey != nil { + return p.MapKey() + } + return prop + } + switch tk.Type { + case token.BoolType: + if p.Bool != nil { + return p.Bool() + } + return prop + case token.AnchorType: + if p.Anchor != nil { + return p.Anchor() + } + return prop + case token.AliasType: + if p.Anchor != nil { + return p.Alias() + } + return prop + case token.StringType, token.SingleQuoteType, token.DoubleQuoteType: + if p.String != nil { + return p.String() + } + return prop + case token.IntegerType, token.FloatType: + if p.Number != nil { + return p.Number() + } + return prop + default: + } + return prop +} + +// PrintTokens create text from token collection +func (p *Printer) PrintTokens(tokens token.Tokens) string { + if len(tokens) == 0 { + return "" + } + if p.LineNumber { + if p.LineNumberFormat == nil { + p.LineNumberFormat = defaultLineNumberFormat + } + } + texts := []string{} + lineNumber := tokens[0].Position.Line + for _, tk := range tokens { + lines := strings.Split(tk.Origin, "\n") + prop := p.property(tk) + header := "" + if p.LineNumber { + header = p.LineNumberFormat(lineNumber) + } + if len(lines) == 1 { + line := prop.Prefix + lines[0] + prop.Suffix + if len(texts) == 0 { + texts = append(texts, header+line) + lineNumber++ + } else { + text := texts[len(texts)-1] + texts[len(texts)-1] = text + line + } + } else { + for idx, src := range lines { + if p.LineNumber { + header = p.LineNumberFormat(lineNumber) + } + line := prop.Prefix + src + prop.Suffix + if idx == 0 { + if len(texts) == 0 { + texts = append(texts, header+line) + lineNumber++ + } else { + text := texts[len(texts)-1] + texts[len(texts)-1] = text + line + } + } else { + texts = append(texts, fmt.Sprintf("%s%s", header, line)) + lineNumber++ + } + } + } + } + return strings.Join(texts, "\n") +} + +// PrintNode create text from ast.Node +func (p *Printer) PrintNode(node ast.Node) []byte { + return []byte(fmt.Sprintf("%+v\n", node)) +} + +const escape = "\x1b" + +func format(attr color.Attribute) string { + return fmt.Sprintf("%s[%dm", escape, attr) +} + +func (p *Printer) setDefaultColorSet() { + p.Bool = func() *Property { + return &Property{ + Prefix: format(color.FgHiMagenta), + Suffix: format(color.Reset), + } + } + p.Number = func() *Property { + return &Property{ + Prefix: format(color.FgHiMagenta), + Suffix: format(color.Reset), + } + } + p.MapKey = func() *Property { + return &Property{ + Prefix: format(color.FgHiCyan), + Suffix: format(color.Reset), + } + } + p.Anchor = func() *Property { + return &Property{ + Prefix: format(color.FgHiYellow), + Suffix: format(color.Reset), + } + } + p.Alias = func() *Property { + return &Property{ + Prefix: format(color.FgHiYellow), + Suffix: format(color.Reset), + } + } + p.String = func() *Property { + return &Property{ + Prefix: format(color.FgHiGreen), + Suffix: format(color.Reset), + } + } +} + +func (p *Printer) PrintErrorMessage(msg string, isColored bool) string { + if isColored { + return fmt.Sprintf("%s%s%s", + format(color.FgHiRed), + msg, + format(color.Reset), + ) + } + return msg +} + +func (p *Printer) removeLeftSideNewLineChar(src string) string { + return strings.TrimLeft(strings.TrimLeft(strings.TrimLeft(src, "\r"), "\n"), "\r\n") +} + +func (p *Printer) removeRightSideNewLineChar(src string) string { + return strings.TrimRight(strings.TrimRight(strings.TrimRight(src, "\r"), "\n"), "\r\n") +} + +func (p *Printer) removeRightSideWhiteSpaceChar(src string) string { + return p.removeRightSideNewLineChar(strings.TrimRight(src, " ")) +} + +func (p *Printer) newLineCount(s string) int { + src := []rune(s) + size := len(src) + cnt := 0 + for i := 0; i < size; i++ { + c := src[i] + switch c { + case '\r': + if i+1 < size && src[i+1] == '\n' { + i++ + } + cnt++ + case '\n': + cnt++ + } + } + return cnt +} + +func (p *Printer) isNewLineLastChar(s string) bool { + for i := len(s) - 1; i > 0; i-- { + c := s[i] + switch c { + case ' ': + continue + case '\n', '\r': + return true + } + break + } + return false +} + +func (p *Printer) printBeforeTokens(tk *token.Token, minLine, extLine int) token.Tokens { + for { + if tk.Prev == nil { + break + } + if tk.Prev.Position.Line < minLine { + break + } + tk = tk.Prev + } + minTk := tk.Clone() + if minTk.Prev != nil { + // add white spaces to minTk by prev token + prev := minTk.Prev + whiteSpaceLen := len(prev.Origin) - len(strings.TrimRight(prev.Origin, " ")) + minTk.Origin = strings.Repeat(" ", whiteSpaceLen) + minTk.Origin + } + minTk.Origin = p.removeLeftSideNewLineChar(minTk.Origin) + tokens := token.Tokens{minTk} + tk = minTk.Next + for tk != nil && tk.Position.Line <= extLine { + clonedTk := tk.Clone() + tokens.Add(clonedTk) + tk = clonedTk.Next + } + lastTk := tokens[len(tokens)-1] + trimmedOrigin := p.removeRightSideWhiteSpaceChar(lastTk.Origin) + suffix := lastTk.Origin[len(trimmedOrigin):] + lastTk.Origin = trimmedOrigin + + if lastTk.Next != nil && len(suffix) > 1 { + next := lastTk.Next.Clone() + // add suffix to header of next token + if suffix[0] == '\n' || suffix[0] == '\r' { + suffix = suffix[1:] + } + next.Origin = suffix + next.Origin + lastTk.Next = next + } + return tokens +} + +func (p *Printer) printAfterTokens(tk *token.Token, maxLine int) token.Tokens { + tokens := token.Tokens{} + if tk == nil { + return tokens + } + if tk.Position.Line > maxLine { + return tokens + } + minTk := tk.Clone() + minTk.Origin = p.removeLeftSideNewLineChar(minTk.Origin) + tokens.Add(minTk) + tk = minTk.Next + for tk != nil && tk.Position.Line <= maxLine { + clonedTk := tk.Clone() + tokens.Add(clonedTk) + tk = clonedTk.Next + } + return tokens +} + +func (p *Printer) setupErrorTokenFormat(annotateLine int, isColored bool) { + prefix := func(annotateLine, num int) string { + if annotateLine == num { + return fmt.Sprintf("> %2d | ", num) + } + return fmt.Sprintf(" %2d | ", num) + } + p.LineNumber = true + p.LineNumberFormat = func(num int) string { + if isColored { + fn := color.New(color.Bold, color.FgHiWhite).SprintFunc() + return fn(prefix(annotateLine, num)) + } + return prefix(annotateLine, num) + } + if isColored { + p.setDefaultColorSet() + } +} + +func (p *Printer) PrintErrorToken(tk *token.Token, isColored bool) string { + errToken := tk + curLine := tk.Position.Line + curExtLine := curLine + p.newLineCount(p.removeLeftSideNewLineChar(tk.Origin)) + if p.isNewLineLastChar(tk.Origin) { + // if last character ( exclude white space ) is new line character, ignore it. + curExtLine-- + } + + minLine := int(math.Max(float64(curLine-3), 1)) + maxLine := curExtLine + 3 + p.setupErrorTokenFormat(curLine, isColored) + + beforeTokens := p.printBeforeTokens(tk, minLine, curExtLine) + lastTk := beforeTokens[len(beforeTokens)-1] + afterTokens := p.printAfterTokens(lastTk.Next, maxLine) + + beforeSource := p.PrintTokens(beforeTokens) + prefixSpaceNum := len(fmt.Sprintf(" %2d | ", curLine)) + annotateLine := strings.Repeat(" ", prefixSpaceNum+errToken.Position.Column-1) + "^" + afterSource := p.PrintTokens(afterTokens) + return fmt.Sprintf("%s\n%s\n%s", beforeSource, annotateLine, afterSource) +} diff --git a/vendor/github.com/goccy/go-yaml/scanner/context.go b/vendor/github.com/goccy/go-yaml/scanner/context.go new file mode 100644 index 000000000..09d0a2daa --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/scanner/context.go @@ -0,0 +1,229 @@ +package scanner + +import ( + "sync" + + "github.com/goccy/go-yaml/token" +) + +const whitespace = ' ' + +// Context context at scanning +type Context struct { + idx int + size int + notSpaceCharPos int + notSpaceOrgCharPos int + src []rune + buf []rune + obuf []rune + tokens token.Tokens + isRawFolded bool + isLiteral bool + isFolded bool + isSingleLine bool + literalOpt string +} + +var ( + ctxPool = sync.Pool{ + New: func() interface{} { + return createContext() + }, + } +) + +func createContext() *Context { + return &Context{ + idx: 0, + tokens: token.Tokens{}, + isSingleLine: true, + } +} + +func newContext(src []rune) *Context { + ctx := ctxPool.Get().(*Context) + ctx.reset(src) + return ctx +} + +func (c *Context) release() { + ctxPool.Put(c) +} + +func (c *Context) reset(src []rune) { + c.idx = 0 + c.size = len(src) + c.src = src + c.tokens = c.tokens[:0] + c.resetBuffer() + c.isRawFolded = false + c.isSingleLine = true + c.isLiteral = false + c.isFolded = false + c.literalOpt = "" +} + +func (c *Context) resetBuffer() { + c.buf = c.buf[:0] + c.obuf = c.obuf[:0] + c.notSpaceCharPos = 0 + c.notSpaceOrgCharPos = 0 +} + +func (c *Context) isSaveIndentMode() bool { + return c.isLiteral || c.isFolded || c.isRawFolded +} + +func (c *Context) breakLiteral() { + c.isLiteral = false + c.isRawFolded = false + c.isFolded = false + c.literalOpt = "" +} + +func (c *Context) addToken(tk *token.Token) { + if tk == nil { + return + } + c.tokens = append(c.tokens, tk) +} + +func (c *Context) addBuf(r rune) { + if len(c.buf) == 0 && r == ' ' { + return + } + c.buf = append(c.buf, r) + if r != ' ' && r != '\t' { + c.notSpaceCharPos = len(c.buf) + } +} + +func (c *Context) addOriginBuf(r rune) { + c.obuf = append(c.obuf, r) + if r != ' ' && r != '\t' { + c.notSpaceOrgCharPos = len(c.obuf) + } +} + +func (c *Context) removeRightSpaceFromBuf() int { + trimmedBuf := c.obuf[:c.notSpaceOrgCharPos] + buflen := len(trimmedBuf) + diff := len(c.obuf) - buflen + if diff > 0 { + c.obuf = c.obuf[:buflen] + c.buf = c.bufferedSrc() + } + return diff +} + +func (c *Context) isDocument() bool { + return c.isLiteral || c.isFolded || c.isRawFolded +} + +func (c *Context) isEOS() bool { + return len(c.src)-1 <= c.idx +} + +func (c *Context) isNextEOS() bool { + return len(c.src)-1 <= c.idx+1 +} + +func (c *Context) next() bool { + return c.idx < c.size +} + +func (c *Context) source(s, e int) string { + return string(c.src[s:e]) +} + +func (c *Context) previousChar() rune { + if c.idx > 0 { + return c.src[c.idx-1] + } + return rune(0) +} + +func (c *Context) currentChar() rune { + if c.size > c.idx { + return c.src[c.idx] + } + return rune(0) +} + +func (c *Context) currentCharWithSkipWhitespace() rune { + idx := c.idx + for c.size > idx { + ch := c.src[idx] + if ch != whitespace { + return ch + } + idx++ + } + return rune(0) +} + +func (c *Context) nextChar() rune { + if c.size > c.idx+1 { + return c.src[c.idx+1] + } + return rune(0) +} + +func (c *Context) repeatNum(r rune) int { + cnt := 0 + for i := c.idx; i < c.size; i++ { + if c.src[i] == r { + cnt++ + } else { + break + } + } + return cnt +} + +func (c *Context) progress(num int) { + c.idx += num +} + +func (c *Context) nextPos() int { + return c.idx + 1 +} + +func (c *Context) existsBuffer() bool { + return len(c.bufferedSrc()) != 0 +} + +func (c *Context) bufferedSrc() []rune { + src := c.buf[:c.notSpaceCharPos] + if len(src) > 0 && src[len(src)-1] == '\n' && c.isDocument() && c.literalOpt == "-" { + // remove end '\n' character + src = src[:len(src)-1] + } + return src +} + +func (c *Context) bufferedToken(pos *token.Position) *token.Token { + if c.idx == 0 { + return nil + } + source := c.bufferedSrc() + if len(source) == 0 { + return nil + } + var tk *token.Token + if c.isDocument() { + tk = token.String(string(source), string(c.obuf), pos) + } else { + tk = token.New(string(source), string(c.obuf), pos) + } + c.resetBuffer() + return tk +} + +func (c *Context) lastToken() *token.Token { + if len(c.tokens) != 0 { + return c.tokens[len(c.tokens)-1] + } + return nil +} diff --git a/vendor/github.com/goccy/go-yaml/scanner/scanner.go b/vendor/github.com/goccy/go-yaml/scanner/scanner.go new file mode 100644 index 000000000..ce9c6654e --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/scanner/scanner.go @@ -0,0 +1,903 @@ +package scanner + +import ( + "io" + "strings" + + "github.com/goccy/go-yaml/token" + "golang.org/x/xerrors" +) + +// IndentState state for indent +type IndentState int + +const ( + // IndentStateEqual equals previous indent + IndentStateEqual IndentState = iota + // IndentStateUp more indent than previous + IndentStateUp + // IndentStateDown less indent than previous + IndentStateDown + // IndentStateKeep uses not indent token + IndentStateKeep +) + +// Scanner holds the scanner's internal state while processing a given text. +// It can be allocated as part of another data structure but must be initialized via Init before use. +type Scanner struct { + source []rune + sourcePos int + sourceSize int + line int + column int + offset int + prevIndentLevel int + prevIndentNum int + prevIndentColumn int + docStartColumn int + indentLevel int + indentNum int + isFirstCharAtLine bool + isAnchor bool + startedFlowSequenceNum int + startedFlowMapNum int + indentState IndentState + savedPos *token.Position +} + +func (s *Scanner) pos() *token.Position { + return &token.Position{ + Line: s.line, + Column: s.column, + Offset: s.offset, + IndentNum: s.indentNum, + IndentLevel: s.indentLevel, + } +} + +func (s *Scanner) bufferedToken(ctx *Context) *token.Token { + if s.savedPos != nil { + tk := ctx.bufferedToken(s.savedPos) + s.savedPos = nil + return tk + } + line := s.line + column := s.column - len(ctx.buf) + level := s.indentLevel + if ctx.isSaveIndentMode() { + line -= s.newLineCount(ctx.buf) + column = strings.Index(string(ctx.obuf), string(ctx.buf)) + 1 + // Since we are in a literal, folded or raw folded + // we can use the indent level from the last token. + last := ctx.lastToken() + if last != nil { // The last token should never be nil here. + level = last.Position.IndentLevel + 1 + } + } + return ctx.bufferedToken(&token.Position{ + Line: line, + Column: column, + Offset: s.offset - len(ctx.buf), + IndentNum: s.indentNum, + IndentLevel: level, + }) +} + +func (s *Scanner) progressColumn(ctx *Context, num int) { + s.column += num + s.offset += num + ctx.progress(num) +} + +func (s *Scanner) progressLine(ctx *Context) { + s.column = 1 + s.line++ + s.offset++ + s.indentNum = 0 + s.isFirstCharAtLine = true + s.isAnchor = false + ctx.progress(1) +} + +func (s *Scanner) isNeededKeepPreviousIndentNum(ctx *Context, c rune) bool { + if !s.isChangedToIndentStateUp() { + return false + } + if ctx.isDocument() { + return true + } + if c == '-' && ctx.existsBuffer() { + return true + } + return false +} + +func (s *Scanner) isNewLineChar(c rune) bool { + if c == '\n' { + return true + } + if c == '\r' { + return true + } + return false +} + +func (s *Scanner) newLineCount(src []rune) int { + size := len(src) + cnt := 0 + for i := 0; i < size; i++ { + c := src[i] + switch c { + case '\r': + if i+1 < size && src[i+1] == '\n' { + i++ + } + cnt++ + case '\n': + cnt++ + } + } + return cnt +} + +func (s *Scanner) updateIndentState(ctx *Context) { + indentNumBasedIndentState := s.indentState + if s.prevIndentNum < s.indentNum { + s.indentLevel = s.prevIndentLevel + 1 + indentNumBasedIndentState = IndentStateUp + } else if s.prevIndentNum == s.indentNum { + s.indentLevel = s.prevIndentLevel + indentNumBasedIndentState = IndentStateEqual + } else { + indentNumBasedIndentState = IndentStateDown + if s.prevIndentLevel > 0 { + s.indentLevel = s.prevIndentLevel - 1 + } + } + + if s.prevIndentColumn > 0 { + if s.prevIndentColumn < s.column { + s.indentState = IndentStateUp + } else if s.prevIndentColumn != s.column || indentNumBasedIndentState != IndentStateEqual { + // The following case ( current position is 'd' ), some variables becomes like here + // - prevIndentColumn: 1 of 'a' + // - indentNumBasedIndentState: IndentStateDown because d's indentNum(1) is less than c's indentNum(3). + // Therefore, s.prevIndentColumn(1) == s.column(1) is true, but we want to treat this as IndentStateDown. + // So, we look also current indentState value by the above prevIndentNum based logic, and determins finally indentState. + // --- + // a: + // b + // c + // d: e + // ^ + s.indentState = IndentStateDown + } else { + s.indentState = IndentStateEqual + } + } else { + s.indentState = indentNumBasedIndentState + } +} + +func (s *Scanner) updateIndent(ctx *Context, c rune) { + if s.isFirstCharAtLine && s.isNewLineChar(c) && ctx.isDocument() { + return + } + if s.isFirstCharAtLine && c == ' ' { + s.indentNum++ + return + } + if !s.isFirstCharAtLine { + s.indentState = IndentStateKeep + return + } + s.updateIndentState(ctx) + s.isFirstCharAtLine = false + if s.isNeededKeepPreviousIndentNum(ctx, c) { + return + } + if s.indentState != IndentStateUp { + s.prevIndentColumn = 0 + } + s.prevIndentNum = s.indentNum + s.prevIndentLevel = s.indentLevel +} + +func (s *Scanner) isChangedToIndentStateDown() bool { + return s.indentState == IndentStateDown +} + +func (s *Scanner) isChangedToIndentStateUp() bool { + return s.indentState == IndentStateUp +} + +func (s *Scanner) isChangedToIndentStateEqual() bool { + return s.indentState == IndentStateEqual +} + +func (s *Scanner) addBufferedTokenIfExists(ctx *Context) { + ctx.addToken(s.bufferedToken(ctx)) +} + +func (s *Scanner) breakLiteral(ctx *Context) { + s.docStartColumn = 0 + ctx.breakLiteral() +} + +func (s *Scanner) scanSingleQuote(ctx *Context) (tk *token.Token, pos int) { + ctx.addOriginBuf('\'') + srcpos := s.pos() + startIndex := ctx.idx + 1 + src := ctx.src + size := len(src) + value := []rune{} + isFirstLineChar := false + isNewLine := false + for idx := startIndex; idx < size; idx++ { + if !isNewLine { + s.progressColumn(ctx, 1) + } else { + isNewLine = false + } + c := src[idx] + pos = idx + 1 + ctx.addOriginBuf(c) + if s.isNewLineChar(c) { + value = append(value, ' ') + isFirstLineChar = true + isNewLine = true + s.progressLine(ctx) + continue + } else if c == ' ' && isFirstLineChar { + continue + } else if c != '\'' { + value = append(value, c) + isFirstLineChar = false + continue + } + if idx+1 < len(ctx.src) && ctx.src[idx+1] == '\'' { + // '' handle as ' character + value = append(value, c) + ctx.addOriginBuf(c) + idx++ + continue + } + s.progressColumn(ctx, 1) + tk = token.SingleQuote(string(value), string(ctx.obuf), srcpos) + pos = idx - startIndex + 1 + return + } + return +} + +func hexToInt(b rune) int { + if b >= 'A' && b <= 'F' { + return int(b) - 'A' + 10 + } + if b >= 'a' && b <= 'f' { + return int(b) - 'a' + 10 + } + return int(b) - '0' +} + +func hexRunesToInt(b []rune) int { + sum := 0 + for i := 0; i < len(b); i++ { + sum += hexToInt(b[i]) << (uint(len(b)-i-1) * 4) + } + return sum +} + +func (s *Scanner) scanDoubleQuote(ctx *Context) (tk *token.Token, pos int) { + ctx.addOriginBuf('"') + srcpos := s.pos() + startIndex := ctx.idx + 1 + src := ctx.src + size := len(src) + value := []rune{} + isFirstLineChar := false + isNewLine := false + for idx := startIndex; idx < size; idx++ { + if !isNewLine { + s.progressColumn(ctx, 1) + } else { + isNewLine = false + } + c := src[idx] + pos = idx + 1 + ctx.addOriginBuf(c) + if s.isNewLineChar(c) { + value = append(value, ' ') + isFirstLineChar = true + isNewLine = true + s.progressLine(ctx) + continue + } else if c == ' ' && isFirstLineChar { + continue + } else if c == '\\' { + isFirstLineChar = false + if idx+1 < size { + nextChar := src[idx+1] + switch nextChar { + case 'b': + ctx.addOriginBuf(nextChar) + value = append(value, '\b') + idx++ + continue + case 'e': + ctx.addOriginBuf(nextChar) + value = append(value, '\x1B') + idx++ + continue + case 'f': + ctx.addOriginBuf(nextChar) + value = append(value, '\f') + idx++ + continue + case 'n': + ctx.addOriginBuf(nextChar) + value = append(value, '\n') + idx++ + continue + case 'v': + ctx.addOriginBuf(nextChar) + value = append(value, '\v') + idx++ + continue + case 'L': // LS (#x2028) + ctx.addOriginBuf(nextChar) + value = append(value, []rune{'\xE2', '\x80', '\xA8'}...) + idx++ + continue + case 'N': // NEL (#x85) + ctx.addOriginBuf(nextChar) + value = append(value, []rune{'\xC2', '\x85'}...) + idx++ + continue + case 'P': // PS (#x2029) + ctx.addOriginBuf(nextChar) + value = append(value, []rune{'\xE2', '\x80', '\xA9'}...) + idx++ + continue + case '_': // #xA0 + ctx.addOriginBuf(nextChar) + value = append(value, []rune{'\xC2', '\xA0'}...) + idx++ + continue + case '"': + ctx.addOriginBuf(nextChar) + value = append(value, nextChar) + idx++ + continue + case 'x': + if idx+3 >= size { + // TODO: need to return error + //err = xerrors.New("invalid escape character \\x") + return + } + codeNum := hexRunesToInt(src[idx+2 : idx+4]) + value = append(value, rune(codeNum)) + idx += 3 + continue + case 'u': + if idx+5 >= size { + // TODO: need to return error + //err = xerrors.New("invalid escape character \\u") + return + } + codeNum := hexRunesToInt(src[idx+2 : idx+6]) + value = append(value, rune(codeNum)) + idx += 5 + continue + case 'U': + if idx+9 >= size { + // TODO: need to return error + //err = xerrors.New("invalid escape character \\U") + return + } + codeNum := hexRunesToInt(src[idx+2 : idx+10]) + value = append(value, rune(codeNum)) + idx += 9 + continue + case '\\': + ctx.addOriginBuf(nextChar) + idx++ + } + } + value = append(value, c) + continue + } else if c != '"' { + value = append(value, c) + isFirstLineChar = false + continue + } + s.progressColumn(ctx, 1) + tk = token.DoubleQuote(string(value), string(ctx.obuf), srcpos) + pos = idx - startIndex + 1 + return + } + return +} + +func (s *Scanner) scanQuote(ctx *Context, ch rune) (tk *token.Token, pos int) { + if ch == '\'' { + return s.scanSingleQuote(ctx) + } + return s.scanDoubleQuote(ctx) +} + +func (s *Scanner) isMergeKey(ctx *Context) bool { + if ctx.repeatNum('<') != 2 { + return false + } + src := ctx.src + size := len(src) + for idx := ctx.idx + 2; idx < size; idx++ { + c := src[idx] + if c == ' ' { + continue + } + if c != ':' { + return false + } + if idx+1 < size { + nc := src[idx+1] + if nc == ' ' || s.isNewLineChar(nc) { + return true + } + } + } + return false +} + +func (s *Scanner) scanTag(ctx *Context) (tk *token.Token, pos int) { + ctx.addOriginBuf('!') + ctx.progress(1) // skip '!' character + for idx, c := range ctx.src[ctx.idx:] { + pos = idx + 1 + ctx.addOriginBuf(c) + switch c { + case ' ', '\n', '\r': + value := ctx.source(ctx.idx-1, ctx.idx+idx) + tk = token.Tag(value, string(ctx.obuf), s.pos()) + pos = len([]rune(value)) + return + } + } + return +} + +func (s *Scanner) scanComment(ctx *Context) (tk *token.Token, pos int) { + ctx.addOriginBuf('#') + ctx.progress(1) // skip '#' character + for idx, c := range ctx.src[ctx.idx:] { + pos = idx + 1 + ctx.addOriginBuf(c) + switch c { + case '\n', '\r': + if ctx.previousChar() == '\\' { + continue + } + value := ctx.source(ctx.idx, ctx.idx+idx) + tk = token.Comment(value, string(ctx.obuf), s.pos()) + pos = len([]rune(value)) + 1 + return + } + } + // document ends with comment. + value := string(ctx.src[ctx.idx:]) + tk = token.Comment(value, string(ctx.obuf), s.pos()) + pos = len([]rune(value)) + 1 + return +} + +func trimCommentFromLiteralOpt(text string) (string, error) { + idx := strings.Index(text, "#") + if idx < 0 { + return text, nil + } + if idx == 0 { + return "", xerrors.New("invalid literal header") + } + return text[:idx-1], nil +} + +func (s *Scanner) scanLiteral(ctx *Context, c rune) { + ctx.addOriginBuf(c) + if ctx.isEOS() { + if ctx.isLiteral { + ctx.addBuf(c) + } + value := ctx.bufferedSrc() + ctx.addToken(token.String(string(value), string(ctx.obuf), s.pos())) + ctx.resetBuffer() + s.progressColumn(ctx, 1) + } else if s.isNewLineChar(c) { + if ctx.isLiteral { + ctx.addBuf(c) + } else { + ctx.addBuf(' ') + } + s.progressLine(ctx) + } else if s.isFirstCharAtLine && c == ' ' { + if 0 < s.docStartColumn && s.docStartColumn <= s.column { + ctx.addBuf(c) + } + s.progressColumn(ctx, 1) + } else { + if s.docStartColumn == 0 { + s.docStartColumn = s.column + } + ctx.addBuf(c) + s.progressColumn(ctx, 1) + } +} + +func (s *Scanner) scanLiteralHeader(ctx *Context) (pos int, err error) { + header := ctx.currentChar() + ctx.addOriginBuf(header) + ctx.progress(1) // skip '|' or '>' character + for idx, c := range ctx.src[ctx.idx:] { + pos = idx + ctx.addOriginBuf(c) + switch c { + case '\n', '\r': + value := ctx.source(ctx.idx, ctx.idx+idx) + opt := strings.TrimRight(value, " ") + orgOptLen := len(opt) + opt, err = trimCommentFromLiteralOpt(opt) + if err != nil { + return + } + switch opt { + case "", "+", "-", + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9": + hasComment := len(opt) < orgOptLen + if header == '|' { + if hasComment { + commentLen := orgOptLen - len(opt) + headerPos := strings.Index(string(ctx.obuf), "|") + litBuf := ctx.obuf[:len(ctx.obuf)-commentLen-headerPos] + commentBuf := ctx.obuf[len(litBuf):] + ctx.addToken(token.Literal("|"+opt, string(litBuf), s.pos())) + s.column += len(litBuf) + s.offset += len(litBuf) + commentHeader := strings.Index(value, "#") + ctx.addToken(token.Comment(string(value[commentHeader+1:]), string(commentBuf), s.pos())) + } else { + ctx.addToken(token.Literal("|"+opt, string(ctx.obuf), s.pos())) + } + ctx.isLiteral = true + } else if header == '>' { + if hasComment { + commentLen := orgOptLen - len(opt) + headerPos := strings.Index(string(ctx.obuf), ">") + foldedBuf := ctx.obuf[:len(ctx.obuf)-commentLen-headerPos] + commentBuf := ctx.obuf[len(foldedBuf):] + ctx.addToken(token.Folded(">"+opt, string(foldedBuf), s.pos())) + s.column += len(foldedBuf) + s.offset += len(foldedBuf) + commentHeader := strings.Index(value, "#") + ctx.addToken(token.Comment(string(value[commentHeader+1:]), string(commentBuf), s.pos())) + } else { + ctx.addToken(token.Folded(">"+opt, string(ctx.obuf), s.pos())) + } + ctx.isFolded = true + } + s.indentState = IndentStateKeep + ctx.resetBuffer() + ctx.literalOpt = opt + return + } + break + } + } + err = xerrors.New("invalid literal header") + return +} + +func (s *Scanner) scanNewLine(ctx *Context, c rune) { + if len(ctx.buf) > 0 && s.savedPos == nil { + s.savedPos = s.pos() + s.savedPos.Column -= len(ctx.bufferedSrc()) + } + + // if the following case, origin buffer has unnecessary two spaces. + // So, `removeRightSpaceFromOriginBuf` remove them, also fix column number too. + // --- + // a:[space][space] + // b: c + removedNum := ctx.removeRightSpaceFromBuf() + if removedNum > 0 { + s.column -= removedNum + s.offset -= removedNum + if s.savedPos != nil { + s.savedPos.Column -= removedNum + } + } + + if ctx.isEOS() { + s.addBufferedTokenIfExists(ctx) + } else if s.isAnchor { + s.addBufferedTokenIfExists(ctx) + } + ctx.addBuf(' ') + ctx.addOriginBuf(c) + ctx.isSingleLine = false + s.progressLine(ctx) +} + +func (s *Scanner) scan(ctx *Context) (pos int) { + for ctx.next() { + pos = ctx.nextPos() + c := ctx.currentChar() + s.updateIndent(ctx, c) + if ctx.isDocument() { + if s.isChangedToIndentStateEqual() || + s.isChangedToIndentStateDown() { + s.addBufferedTokenIfExists(ctx) + s.breakLiteral(ctx) + } else { + s.scanLiteral(ctx, c) + continue + } + } else if s.isChangedToIndentStateDown() { + s.addBufferedTokenIfExists(ctx) + } else if s.isChangedToIndentStateEqual() { + // if first character is new line character, buffer expect to raw folded literal + if len(ctx.obuf) > 0 && s.newLineCount(ctx.obuf) <= 1 { + // doesn't raw folded literal + s.addBufferedTokenIfExists(ctx) + } + } + switch c { + case '{': + if !ctx.existsBuffer() { + ctx.addOriginBuf(c) + ctx.addToken(token.MappingStart(string(ctx.obuf), s.pos())) + s.startedFlowMapNum++ + s.progressColumn(ctx, 1) + return + } + case '}': + if !ctx.existsBuffer() || s.startedFlowMapNum > 0 { + ctx.addToken(s.bufferedToken(ctx)) + ctx.addOriginBuf(c) + ctx.addToken(token.MappingEnd(string(ctx.obuf), s.pos())) + s.startedFlowMapNum-- + s.progressColumn(ctx, 1) + return + } + case '.': + if s.indentNum == 0 && s.column == 1 && ctx.repeatNum('.') == 3 { + ctx.addToken(token.DocumentEnd(string(ctx.obuf)+"...", s.pos())) + s.progressColumn(ctx, 3) + pos += 2 + return + } + case '<': + if s.isMergeKey(ctx) { + s.prevIndentColumn = s.column + ctx.addToken(token.MergeKey(string(ctx.obuf)+"<<", s.pos())) + s.progressColumn(ctx, 1) + pos++ + return + } + case '-': + if s.indentNum == 0 && s.column == 1 && ctx.repeatNum('-') == 3 { + s.addBufferedTokenIfExists(ctx) + ctx.addToken(token.DocumentHeader(string(ctx.obuf)+"---", s.pos())) + s.progressColumn(ctx, 3) + pos += 2 + return + } + if ctx.existsBuffer() && s.isChangedToIndentStateUp() { + // raw folded + ctx.isRawFolded = true + ctx.addBuf(c) + ctx.addOriginBuf(c) + s.progressColumn(ctx, 1) + continue + } + if ctx.existsBuffer() { + // '-' is literal + ctx.addBuf(c) + ctx.addOriginBuf(c) + s.progressColumn(ctx, 1) + continue + } + nc := ctx.nextChar() + if nc == ' ' || s.isNewLineChar(nc) { + s.addBufferedTokenIfExists(ctx) + ctx.addOriginBuf(c) + tk := token.SequenceEntry(string(ctx.obuf), s.pos()) + s.prevIndentColumn = tk.Position.Column + ctx.addToken(tk) + s.progressColumn(ctx, 1) + return + } + case '[': + if !ctx.existsBuffer() { + ctx.addOriginBuf(c) + ctx.addToken(token.SequenceStart(string(ctx.obuf), s.pos())) + s.startedFlowSequenceNum++ + s.progressColumn(ctx, 1) + return + } + case ']': + if !ctx.existsBuffer() || s.startedFlowSequenceNum > 0 { + s.addBufferedTokenIfExists(ctx) + ctx.addOriginBuf(c) + ctx.addToken(token.SequenceEnd(string(ctx.obuf), s.pos())) + s.startedFlowSequenceNum-- + s.progressColumn(ctx, 1) + return + } + case ',': + if s.startedFlowSequenceNum > 0 || s.startedFlowMapNum > 0 { + s.addBufferedTokenIfExists(ctx) + ctx.addOriginBuf(c) + ctx.addToken(token.CollectEntry(string(ctx.obuf), s.pos())) + s.progressColumn(ctx, 1) + return + } + case ':': + nc := ctx.nextChar() + if s.startedFlowMapNum > 0 || nc == ' ' || s.isNewLineChar(nc) || ctx.isNextEOS() { + // mapping value + tk := s.bufferedToken(ctx) + if tk != nil { + s.prevIndentColumn = tk.Position.Column + ctx.addToken(tk) + } else if tk := ctx.lastToken(); tk != nil { + // If the map key is quote, the buffer does not exist because it has already been cut into tokens. + // Therefore, we need to check the last token. + if tk.Indicator == token.QuotedScalarIndicator { + s.prevIndentColumn = tk.Position.Column + } + } + ctx.addToken(token.MappingValue(s.pos())) + s.progressColumn(ctx, 1) + return + } + case '|', '>': + if !ctx.existsBuffer() { + progress, err := s.scanLiteralHeader(ctx) + if err != nil { + // TODO: returns syntax error object + return + } + s.progressColumn(ctx, progress) + s.progressLine(ctx) + continue + } + case '!': + if !ctx.existsBuffer() { + token, progress := s.scanTag(ctx) + ctx.addToken(token) + s.progressColumn(ctx, progress) + if c := ctx.previousChar(); s.isNewLineChar(c) { + s.progressLine(ctx) + } + pos += progress + return + } + case '%': + if !ctx.existsBuffer() && s.indentNum == 0 { + ctx.addToken(token.Directive(string(ctx.obuf)+"%", s.pos())) + s.progressColumn(ctx, 1) + return + } + case '?': + nc := ctx.nextChar() + if !ctx.existsBuffer() && nc == ' ' { + ctx.addToken(token.MappingKey(s.pos())) + s.progressColumn(ctx, 1) + return + } + case '&': + if !ctx.existsBuffer() { + s.addBufferedTokenIfExists(ctx) + ctx.addOriginBuf(c) + ctx.addToken(token.Anchor(string(ctx.obuf), s.pos())) + s.progressColumn(ctx, 1) + s.isAnchor = true + return + } + case '*': + if !ctx.existsBuffer() { + s.addBufferedTokenIfExists(ctx) + ctx.addOriginBuf(c) + ctx.addToken(token.Alias(string(ctx.obuf), s.pos())) + s.progressColumn(ctx, 1) + return + } + case '#': + if !ctx.existsBuffer() || ctx.previousChar() == ' ' { + s.addBufferedTokenIfExists(ctx) + token, progress := s.scanComment(ctx) + ctx.addToken(token) + s.progressColumn(ctx, progress) + s.progressLine(ctx) + pos += progress + return + } + case '\'', '"': + if !ctx.existsBuffer() { + token, progress := s.scanQuote(ctx, c) + ctx.addToken(token) + pos += progress + // If the non-whitespace character immediately following the quote is ':', the quote should be treated as a map key. + // Therefore, do not return and continue processing as a normal map key. + if ctx.currentCharWithSkipWhitespace() == ':' { + continue + } + return + } + case '\r', '\n': + // There is no problem that we ignore CR which followed by LF and normalize it to LF, because of following YAML1.2 spec. + // > Line breaks inside scalar content must be normalized by the YAML processor. Each such line break must be parsed into a single line feed character. + // > Outside scalar content, YAML allows any line break to be used to terminate lines. + // > -- https://yaml.org/spec/1.2/spec.html + if c == '\r' && ctx.nextChar() == '\n' { + ctx.addOriginBuf('\r') + ctx.progress(1) + c = '\n' + } + s.scanNewLine(ctx, c) + continue + case ' ': + if ctx.isSaveIndentMode() || (!s.isAnchor && !s.isFirstCharAtLine) { + ctx.addBuf(c) + ctx.addOriginBuf(c) + s.progressColumn(ctx, 1) + continue + } + if s.isFirstCharAtLine { + s.progressColumn(ctx, 1) + ctx.addOriginBuf(c) + continue + } + s.addBufferedTokenIfExists(ctx) + pos-- // to rescan white space at next scanning for adding white space to next buffer. + s.isAnchor = false + return + } + ctx.addBuf(c) + ctx.addOriginBuf(c) + s.progressColumn(ctx, 1) + } + s.addBufferedTokenIfExists(ctx) + return +} + +// Init prepares the scanner s to tokenize the text src by setting the scanner at the beginning of src. +func (s *Scanner) Init(text string) { + src := []rune(text) + s.source = src + s.sourcePos = 0 + s.sourceSize = len(src) + s.line = 1 + s.column = 1 + s.offset = 1 + s.prevIndentLevel = 0 + s.prevIndentNum = 0 + s.prevIndentColumn = 0 + s.indentLevel = 0 + s.indentNum = 0 + s.isFirstCharAtLine = true +} + +// Scan scans the next token and returns the token collection. The source end is indicated by io.EOF. +func (s *Scanner) Scan() (token.Tokens, error) { + if s.sourcePos >= s.sourceSize { + return nil, io.EOF + } + ctx := newContext(s.source[s.sourcePos:]) + defer ctx.release() + progress := s.scan(ctx) + s.sourcePos += progress + var tokens token.Tokens + tokens = append(tokens, ctx.tokens...) + return tokens, nil +} diff --git a/vendor/github.com/goccy/go-yaml/stdlib_quote.go b/vendor/github.com/goccy/go-yaml/stdlib_quote.go new file mode 100644 index 000000000..be50ae612 --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/stdlib_quote.go @@ -0,0 +1,103 @@ +// Copied and trimmed down from https://github.com/golang/go/blob/e3769299cd3484e018e0e2a6e1b95c2b18ce4f41/src/strconv/quote.go +// We want to use the standard library's private "quoteWith" function rather than write our own so that we get robust unicode support. +// Every private function called by quoteWith was copied. +// There are 2 modifications to simplify the code: +// 1. The unicode.IsPrint function was substituted for the custom implementation of IsPrint +// 2. All code paths reachable only when ASCIIonly or grphicOnly are set to true were removed. + +// 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. + +package yaml + +import ( + "unicode" + "unicode/utf8" +) + +const ( + lowerhex = "0123456789abcdef" +) + +func quoteWith(s string, quote byte) string { + return string(appendQuotedWith(make([]byte, 0, 3*len(s)/2), s, quote)) +} + +func appendQuotedWith(buf []byte, s string, quote byte) []byte { + // Often called with big strings, so preallocate. If there's quoting, + // this is conservative but still helps a lot. + if cap(buf)-len(buf) < len(s) { + nBuf := make([]byte, len(buf), len(buf)+1+len(s)+1) + copy(nBuf, buf) + buf = nBuf + } + buf = append(buf, quote) + for width := 0; len(s) > 0; s = s[width:] { + r := rune(s[0]) + width = 1 + if r >= utf8.RuneSelf { + r, width = utf8.DecodeRuneInString(s) + } + if width == 1 && r == utf8.RuneError { + buf = append(buf, `\x`...) + buf = append(buf, lowerhex[s[0]>>4]) + buf = append(buf, lowerhex[s[0]&0xF]) + continue + } + buf = appendEscapedRune(buf, r, quote) + } + buf = append(buf, quote) + return buf +} + +func appendEscapedRune(buf []byte, r rune, quote byte) []byte { + var runeTmp [utf8.UTFMax]byte + if r == rune(quote) || r == '\\' { // always backslashed + buf = append(buf, '\\') + buf = append(buf, byte(r)) + return buf + } + if unicode.IsPrint(r) { + n := utf8.EncodeRune(runeTmp[:], r) + buf = append(buf, runeTmp[:n]...) + return buf + } + switch r { + case '\a': + buf = append(buf, `\a`...) + case '\b': + buf = append(buf, `\b`...) + case '\f': + buf = append(buf, `\f`...) + case '\n': + buf = append(buf, `\n`...) + case '\r': + buf = append(buf, `\r`...) + case '\t': + buf = append(buf, `\t`...) + case '\v': + buf = append(buf, `\v`...) + default: + switch { + case r < ' ': + buf = append(buf, `\x`...) + buf = append(buf, lowerhex[byte(r)>>4]) + buf = append(buf, lowerhex[byte(r)&0xF]) + case r > utf8.MaxRune: + r = 0xFFFD + fallthrough + case r < 0x10000: + buf = append(buf, `\u`...) + for s := 12; s >= 0; s -= 4 { + buf = append(buf, lowerhex[r>>uint(s)&0xF]) + } + default: + buf = append(buf, `\U`...) + for s := 28; s >= 0; s -= 4 { + buf = append(buf, lowerhex[r>>uint(s)&0xF]) + } + } + } + return buf +} diff --git a/vendor/github.com/goccy/go-yaml/struct.go b/vendor/github.com/goccy/go-yaml/struct.go new file mode 100644 index 000000000..a3da8ddd3 --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/struct.go @@ -0,0 +1,130 @@ +package yaml + +import ( + "reflect" + "strings" + + "golang.org/x/xerrors" +) + +const ( + // StructTagName tag keyword for Marshal/Unmarshal + StructTagName = "yaml" +) + +// StructField information for each the field in structure +type StructField struct { + FieldName string + RenderName string + AnchorName string + AliasName string + IsAutoAnchor bool + IsAutoAlias bool + IsOmitEmpty bool + IsFlow bool + IsInline bool +} + +func getTag(field reflect.StructField) string { + // If struct tag `yaml` exist, use that. If no `yaml` + // exists, but `json` does, use that and try the best to + // adhere to its rules + tag := field.Tag.Get(StructTagName) + if tag == "" { + tag = field.Tag.Get(`json`) + } + return tag +} + +func structField(field reflect.StructField) *StructField { + tag := getTag(field) + fieldName := strings.ToLower(field.Name) + options := strings.Split(tag, ",") + if len(options) > 0 { + if options[0] != "" { + fieldName = options[0] + } + } + structField := &StructField{ + FieldName: field.Name, + RenderName: fieldName, + } + if len(options) > 1 { + for _, opt := range options[1:] { + switch { + case opt == "omitempty": + structField.IsOmitEmpty = true + case opt == "flow": + structField.IsFlow = true + case opt == "inline": + structField.IsInline = true + case strings.HasPrefix(opt, "anchor"): + anchor := strings.Split(opt, "=") + if len(anchor) > 1 { + structField.AnchorName = anchor[1] + } else { + structField.IsAutoAnchor = true + } + case strings.HasPrefix(opt, "alias"): + alias := strings.Split(opt, "=") + if len(alias) > 1 { + structField.AliasName = alias[1] + } else { + structField.IsAutoAlias = true + } + default: + } + } + } + return structField +} + +func isIgnoredStructField(field reflect.StructField) bool { + if field.PkgPath != "" && !field.Anonymous { + // private field + return true + } + tag := getTag(field) + if tag == "-" { + return true + } + return false +} + +type StructFieldMap map[string]*StructField + +func (m StructFieldMap) isIncludedRenderName(name string) bool { + for _, v := range m { + if !v.IsInline && v.RenderName == name { + return true + } + } + return false +} + +func (m StructFieldMap) hasMergeProperty() bool { + for _, v := range m { + if v.IsOmitEmpty && v.IsInline && v.IsAutoAlias { + return true + } + } + return false +} + +func structFieldMap(structType reflect.Type) (StructFieldMap, error) { + structFieldMap := StructFieldMap{} + renderNameMap := map[string]struct{}{} + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + if isIgnoredStructField(field) { + continue + } + structField := structField(field) + if _, exists := renderNameMap[structField.RenderName]; exists { + return nil, xerrors.Errorf("duplicated struct field name %s", structField.RenderName) + } + structFieldMap[structField.FieldName] = structField + renderNameMap[structField.RenderName] = struct{}{} + } + return structFieldMap, nil +} diff --git a/vendor/github.com/goccy/go-yaml/token/token.go b/vendor/github.com/goccy/go-yaml/token/token.go new file mode 100644 index 000000000..182f4bea9 --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/token/token.go @@ -0,0 +1,1070 @@ +package token + +import ( + "fmt" + "strings" +) + +// Character type for character +type Character byte + +const ( + // SequenceEntryCharacter character for sequence entry + SequenceEntryCharacter Character = '-' + // MappingKeyCharacter character for mapping key + MappingKeyCharacter Character = '?' + // MappingValueCharacter character for mapping value + MappingValueCharacter Character = ':' + // CollectEntryCharacter character for collect entry + CollectEntryCharacter Character = ',' + // SequenceStartCharacter character for sequence start + SequenceStartCharacter Character = '[' + // SequenceEndCharacter character for sequence end + SequenceEndCharacter Character = ']' + // MappingStartCharacter character for mapping start + MappingStartCharacter Character = '{' + // MappingEndCharacter character for mapping end + MappingEndCharacter Character = '}' + // CommentCharacter character for comment + CommentCharacter Character = '#' + // AnchorCharacter character for anchor + AnchorCharacter Character = '&' + // AliasCharacter character for alias + AliasCharacter Character = '*' + // TagCharacter character for tag + TagCharacter Character = '!' + // LiteralCharacter character for literal + LiteralCharacter Character = '|' + // FoldedCharacter character for folded + FoldedCharacter Character = '>' + // SingleQuoteCharacter character for single quote + SingleQuoteCharacter Character = '\'' + // DoubleQuoteCharacter character for double quote + DoubleQuoteCharacter Character = '"' + // DirectiveCharacter character for directive + DirectiveCharacter Character = '%' + // SpaceCharacter character for space + SpaceCharacter Character = ' ' + // LineBreakCharacter character for line break + LineBreakCharacter Character = '\n' +) + +// Type type identifier for token +type Type int + +const ( + // UnknownType reserve for invalid type + UnknownType Type = iota + // DocumentHeaderType type for DocumentHeader token + DocumentHeaderType + // DocumentEndType type for DocumentEnd token + DocumentEndType + // SequenceEntryType type for SequenceEntry token + SequenceEntryType + // MappingKeyType type for MappingKey token + MappingKeyType + // MappingValueType type for MappingValue token + MappingValueType + // MergeKeyType type for MergeKey token + MergeKeyType + // CollectEntryType type for CollectEntry token + CollectEntryType + // SequenceStartType type for SequenceStart token + SequenceStartType + // SequenceEndType type for SequenceEnd token + SequenceEndType + // MappingStartType type for MappingStart token + MappingStartType + // MappingEndType type for MappingEnd token + MappingEndType + // CommentType type for Comment token + CommentType + // AnchorType type for Anchor token + AnchorType + // AliasType type for Alias token + AliasType + // TagType type for Tag token + TagType + // LiteralType type for Literal token + LiteralType + // FoldedType type for Folded token + FoldedType + // SingleQuoteType type for SingleQuote token + SingleQuoteType + // DoubleQuoteType type for DoubleQuote token + DoubleQuoteType + // DirectiveType type for Directive token + DirectiveType + // SpaceType type for Space token + SpaceType + // NullType type for Null token + NullType + // InfinityType type for Infinity token + InfinityType + // NanType type for Nan token + NanType + // IntegerType type for Integer token + IntegerType + // BinaryIntegerType type for BinaryInteger token + BinaryIntegerType + // OctetIntegerType type for OctetInteger token + OctetIntegerType + // HexIntegerType type for HexInteger token + HexIntegerType + // FloatType type for Float token + FloatType + // StringType type for String token + StringType + // BoolType type for Bool token + BoolType +) + +// String type identifier to text +func (t Type) String() string { + switch t { + case UnknownType: + return "Unknown" + case DocumentHeaderType: + return "DocumentHeader" + case DocumentEndType: + return "DocumentEnd" + case SequenceEntryType: + return "SequenceEntry" + case MappingKeyType: + return "MappingKey" + case MappingValueType: + return "MappingValue" + case MergeKeyType: + return "MergeKey" + case CollectEntryType: + return "CollectEntry" + case SequenceStartType: + return "SequenceStart" + case SequenceEndType: + return "SequenceEnd" + case MappingStartType: + return "MappingStart" + case MappingEndType: + return "MappingEnd" + case CommentType: + return "Comment" + case AnchorType: + return "Anchor" + case AliasType: + return "Alias" + case TagType: + return "Tag" + case LiteralType: + return "Literal" + case FoldedType: + return "Folded" + case SingleQuoteType: + return "SingleQuote" + case DoubleQuoteType: + return "DoubleQuote" + case DirectiveType: + return "Directive" + case SpaceType: + return "Space" + case StringType: + return "String" + case BoolType: + return "Bool" + case IntegerType: + return "Integer" + case BinaryIntegerType: + return "BinaryInteger" + case OctetIntegerType: + return "OctetInteger" + case HexIntegerType: + return "HexInteger" + case FloatType: + return "Float" + case NullType: + return "Null" + case InfinityType: + return "Infinity" + case NanType: + return "Nan" + } + return "" +} + +// CharacterType type for character category +type CharacterType int + +const ( + // CharacterTypeIndicator type of indicator character + CharacterTypeIndicator CharacterType = iota + // CharacterTypeWhiteSpace type of white space character + CharacterTypeWhiteSpace + // CharacterTypeMiscellaneous type of miscellaneous character + CharacterTypeMiscellaneous + // CharacterTypeEscaped type of escaped character + CharacterTypeEscaped +) + +// String character type identifier to text +func (c CharacterType) String() string { + switch c { + case CharacterTypeIndicator: + return "Indicator" + case CharacterTypeWhiteSpace: + return "WhiteSpcae" + case CharacterTypeMiscellaneous: + return "Miscellaneous" + case CharacterTypeEscaped: + return "Escaped" + } + return "" +} + +// Indicator type for indicator +type Indicator int + +const ( + // NotIndicator not indicator + NotIndicator Indicator = iota + // BlockStructureIndicator indicator for block structure ( '-', '?', ':' ) + BlockStructureIndicator + // FlowCollectionIndicator indicator for flow collection ( '[', ']', '{', '}', ',' ) + FlowCollectionIndicator + // CommentIndicator indicator for comment ( '#' ) + CommentIndicator + // NodePropertyIndicator indicator for node property ( '!', '&', '*' ) + NodePropertyIndicator + // BlockScalarIndicator indicator for block scalar ( '|', '>' ) + BlockScalarIndicator + // QuotedScalarIndicator indicator for quoted scalar ( ''', '"' ) + QuotedScalarIndicator + // DirectiveIndicator indicator for directive ( '%' ) + DirectiveIndicator + // InvalidUseOfReservedIndicator indicator for invalid use of reserved keyword ( '@', '`' ) + InvalidUseOfReservedIndicator +) + +// String indicator to text +func (i Indicator) String() string { + switch i { + case NotIndicator: + return "NotIndicator" + case BlockStructureIndicator: + return "BlockStructure" + case FlowCollectionIndicator: + return "FlowCollection" + case CommentIndicator: + return "Comment" + case NodePropertyIndicator: + return "NodeProperty" + case BlockScalarIndicator: + return "BlockScalar" + case QuotedScalarIndicator: + return "QuotedScalar" + case DirectiveIndicator: + return "Directive" + case InvalidUseOfReservedIndicator: + return "InvalidUseOfReserved" + } + return "" +} + +var ( + reservedNullKeywords = []string{ + "null", + "Null", + "NULL", + "~", + } + reservedBoolKeywords = []string{ + "true", + "True", + "TRUE", + "false", + "False", + "FALSE", + } + // For compatibility with other YAML 1.1 parsers + // Note that we use these solely for encoding the bool value with quotes. + // go-yaml should not treat these as reserved keywords at parsing time. + // as go-yaml is supposed to be compliant only with YAML 1.2. + reservedLegacyBoolKeywords = []string{ + "y", + "Y", + "yes", + "Yes", + "YES", + "n", + "N", + "no", + "No", + "NO", + "on", + "On", + "ON", + "off", + "Off", + "OFF", + } + reservedInfKeywords = []string{ + ".inf", + ".Inf", + ".INF", + "-.inf", + "-.Inf", + "-.INF", + } + reservedNanKeywords = []string{ + ".nan", + ".NaN", + ".NAN", + } + reservedKeywordMap = map[string]func(string, string, *Position) *Token{} + // reservedEncKeywordMap contains is the keyword map used at encoding time. + // This is supposed to be a superset of reservedKeywordMap, + // and used to quote legacy keywords present in YAML 1.1 or lesser for compatibility reasons, + // even though this library is supposed to be YAML 1.2-compliant. + reservedEncKeywordMap = map[string]func(string, string, *Position) *Token{} +) + +func reservedKeywordToken(typ Type, value, org string, pos *Position) *Token { + return &Token{ + Type: typ, + CharacterType: CharacterTypeMiscellaneous, + Indicator: NotIndicator, + Value: value, + Origin: org, + Position: pos, + } +} + +func init() { + for _, keyword := range reservedNullKeywords { + reservedKeywordMap[keyword] = func(value, org string, pos *Position) *Token { + return reservedKeywordToken(NullType, value, org, pos) + } + } + for _, keyword := range reservedBoolKeywords { + f := func(value, org string, pos *Position) *Token { + return reservedKeywordToken(BoolType, value, org, pos) + } + reservedKeywordMap[keyword] = f + reservedEncKeywordMap[keyword] = f + } + for _, keyword := range reservedLegacyBoolKeywords { + reservedEncKeywordMap[keyword] = func(value, org string, pos *Position) *Token { + return reservedKeywordToken(BoolType, value, org, pos) + } + } + for _, keyword := range reservedInfKeywords { + reservedKeywordMap[keyword] = func(value, org string, pos *Position) *Token { + return reservedKeywordToken(InfinityType, value, org, pos) + } + } + for _, keyword := range reservedNanKeywords { + reservedKeywordMap[keyword] = func(value, org string, pos *Position) *Token { + return reservedKeywordToken(NanType, value, org, pos) + } + } +} + +// ReservedTagKeyword type of reserved tag keyword +type ReservedTagKeyword string + +const ( + // IntegerTag `!!int` tag + IntegerTag ReservedTagKeyword = "!!int" + // FloatTag `!!float` tag + FloatTag ReservedTagKeyword = "!!float" + // NullTag `!!null` tag + NullTag ReservedTagKeyword = "!!null" + // SequenceTag `!!seq` tag + SequenceTag ReservedTagKeyword = "!!seq" + // MappingTag `!!map` tag + MappingTag ReservedTagKeyword = "!!map" + // StringTag `!!str` tag + StringTag ReservedTagKeyword = "!!str" + // BinaryTag `!!binary` tag + BinaryTag ReservedTagKeyword = "!!binary" + // OrderedMapTag `!!omap` tag + OrderedMapTag ReservedTagKeyword = "!!omap" + // SetTag `!!set` tag + SetTag ReservedTagKeyword = "!!set" + // TimestampTag `!!timestamp` tag + TimestampTag ReservedTagKeyword = "!!timestamp" +) + +var ( + // ReservedTagKeywordMap map for reserved tag keywords + ReservedTagKeywordMap = map[ReservedTagKeyword]func(string, string, *Position) *Token{ + IntegerTag: func(value, org string, pos *Position) *Token { + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } + }, + FloatTag: func(value, org string, pos *Position) *Token { + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } + }, + NullTag: func(value, org string, pos *Position) *Token { + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } + }, + SequenceTag: func(value, org string, pos *Position) *Token { + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } + }, + MappingTag: func(value, org string, pos *Position) *Token { + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } + }, + StringTag: func(value, org string, pos *Position) *Token { + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } + }, + BinaryTag: func(value, org string, pos *Position) *Token { + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } + }, + OrderedMapTag: func(value, org string, pos *Position) *Token { + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } + }, + SetTag: func(value, org string, pos *Position) *Token { + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } + }, + TimestampTag: func(value, org string, pos *Position) *Token { + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } + }, + } +) + +type numType int + +const ( + numTypeNone numType = iota + numTypeBinary + numTypeOctet + numTypeHex + numTypeFloat +) + +type numStat struct { + isNum bool + typ numType +} + +func getNumberStat(str string) *numStat { + stat := &numStat{} + if str == "" { + return stat + } + if str == "-" || str == "." || str == "+" || str == "_" { + return stat + } + if str[0] == '_' { + return stat + } + dotFound := false + isNegative := false + isExponent := false + if str[0] == '-' { + isNegative = true + } + for idx, c := range str { + switch c { + case 'x': + if (isNegative && idx == 2) || (!isNegative && idx == 1) { + continue + } + case 'o': + if (isNegative && idx == 2) || (!isNegative && idx == 1) { + continue + } + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + continue + case 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F': + if (len(str) > 2 && str[0] == '0' && str[1] == 'x') || + (len(str) > 3 && isNegative && str[1] == '0' && str[2] == 'x') { + // hex number + continue + } + if c == 'b' && ((isNegative && idx == 2) || (!isNegative && idx == 1)) { + // binary number + continue + } + if (c == 'e' || c == 'E') && dotFound { + // exponent + isExponent = true + continue + } + case '.': + if dotFound { + // multiple dot + return stat + } + dotFound = true + continue + case '-': + if idx == 0 || isExponent { + continue + } + case '+': + if idx == 0 || isExponent { + continue + } + case '_': + continue + } + return stat + } + stat.isNum = true + switch { + case dotFound: + stat.typ = numTypeFloat + case strings.HasPrefix(str, "0b") || strings.HasPrefix(str, "-0b"): + stat.typ = numTypeBinary + case strings.HasPrefix(str, "0x") || strings.HasPrefix(str, "-0x"): + stat.typ = numTypeHex + case strings.HasPrefix(str, "0o") || strings.HasPrefix(str, "-0o"): + stat.typ = numTypeOctet + case (len(str) > 1 && str[0] == '0') || (len(str) > 1 && str[0] == '-' && str[1] == '0'): + stat.typ = numTypeOctet + } + return stat +} + +func looksLikeTimeValue(value string) bool { + for i, c := range value { + switch c { + case ':', '1', '2', '3', '4', '5', '6', '7', '8', '9': + continue + case '0': + if i == 0 { + return false + } + continue + } + return false + } + return true +} + +// IsNeedQuoted whether need quote for passed string or not +func IsNeedQuoted(value string) bool { + if value == "" { + return true + } + if _, exists := reservedEncKeywordMap[value]; exists { + return true + } + if stat := getNumberStat(value); stat.isNum { + return true + } + first := value[0] + switch first { + case '*', '&', '[', '{', '}', ']', ',', '!', '|', '>', '%', '\'', '"', '@': + return true + } + last := value[len(value)-1] + switch last { + case ':': + return true + } + if looksLikeTimeValue(value) { + return true + } + for i, c := range value { + switch c { + case '#', '\\': + return true + case ':': + if i+1 < len(value) && value[i+1] == ' ' { + return true + } + } + } + return false +} + +// LiteralBlockHeader detect literal block scalar header +func LiteralBlockHeader(value string) string { + lbc := DetectLineBreakCharacter(value) + + switch { + case !strings.Contains(value, lbc): + return "" + case strings.HasSuffix(value, fmt.Sprintf("%s%s", lbc, lbc)): + return "|+" + case strings.HasSuffix(value, lbc): + return "|" + default: + return "|-" + } +} + +// New create reserved keyword token or number token and other string token +func New(value string, org string, pos *Position) *Token { + fn := reservedKeywordMap[value] + if fn != nil { + return fn(value, org, pos) + } + if stat := getNumberStat(value); stat.isNum { + tk := &Token{ + Type: IntegerType, + CharacterType: CharacterTypeMiscellaneous, + Indicator: NotIndicator, + Value: value, + Origin: org, + Position: pos, + } + switch stat.typ { + case numTypeFloat: + tk.Type = FloatType + case numTypeBinary: + tk.Type = BinaryIntegerType + case numTypeOctet: + tk.Type = OctetIntegerType + case numTypeHex: + tk.Type = HexIntegerType + } + return tk + } + return String(value, org, pos) +} + +// Position type for position in YAML document +type Position struct { + Line int + Column int + Offset int + IndentNum int + IndentLevel int +} + +// String position to text +func (p *Position) String() string { + return fmt.Sprintf("[level:%d,line:%d,column:%d,offset:%d]", p.IndentLevel, p.Line, p.Column, p.Offset) +} + +// Token type for token +type Token struct { + Type Type + CharacterType CharacterType + Indicator Indicator + Value string + Origin string + Position *Position + Next *Token + Prev *Token +} + +// PreviousType previous token type +func (t *Token) PreviousType() Type { + if t.Prev != nil { + return t.Prev.Type + } + return UnknownType +} + +// NextType next token type +func (t *Token) NextType() Type { + if t.Next != nil { + return t.Next.Type + } + return UnknownType +} + +// AddColumn append column number to current position of column +func (t *Token) AddColumn(col int) { + if t == nil { + return + } + t.Position.Column += col +} + +// Clone copy token ( preserve Prev/Next reference ) +func (t *Token) Clone() *Token { + if t == nil { + return nil + } + copied := *t + if t.Position != nil { + pos := *(t.Position) + copied.Position = &pos + } + return &copied +} + +// Tokens type of token collection +type Tokens []*Token + +func (t *Tokens) add(tk *Token) { + tokens := *t + if len(tokens) == 0 { + tokens = append(tokens, tk) + } else { + last := tokens[len(tokens)-1] + last.Next = tk + tk.Prev = last + tokens = append(tokens, tk) + } + *t = tokens +} + +// Add append new some tokens +func (t *Tokens) Add(tks ...*Token) { + for _, tk := range tks { + t.add(tk) + } +} + +// Dump dump all token structures for debugging +func (t Tokens) Dump() { + for _, tk := range t { + fmt.Printf("- %+v\n", tk) + } +} + +// String create token for String +func String(value string, org string, pos *Position) *Token { + return &Token{ + Type: StringType, + CharacterType: CharacterTypeMiscellaneous, + Indicator: NotIndicator, + Value: value, + Origin: org, + Position: pos, + } +} + +// SequenceEntry create token for SequenceEntry +func SequenceEntry(org string, pos *Position) *Token { + return &Token{ + Type: SequenceEntryType, + CharacterType: CharacterTypeIndicator, + Indicator: BlockStructureIndicator, + Value: string(SequenceEntryCharacter), + Origin: org, + Position: pos, + } +} + +// MappingKey create token for MappingKey +func MappingKey(pos *Position) *Token { + return &Token{ + Type: MappingKeyType, + CharacterType: CharacterTypeIndicator, + Indicator: BlockStructureIndicator, + Value: string(MappingKeyCharacter), + Origin: string(MappingKeyCharacter), + Position: pos, + } +} + +// MappingValue create token for MappingValue +func MappingValue(pos *Position) *Token { + return &Token{ + Type: MappingValueType, + CharacterType: CharacterTypeIndicator, + Indicator: BlockStructureIndicator, + Value: string(MappingValueCharacter), + Origin: string(MappingValueCharacter), + Position: pos, + } +} + +// CollectEntry create token for CollectEntry +func CollectEntry(org string, pos *Position) *Token { + return &Token{ + Type: CollectEntryType, + CharacterType: CharacterTypeIndicator, + Indicator: FlowCollectionIndicator, + Value: string(CollectEntryCharacter), + Origin: org, + Position: pos, + } +} + +// SequenceStart create token for SequenceStart +func SequenceStart(org string, pos *Position) *Token { + return &Token{ + Type: SequenceStartType, + CharacterType: CharacterTypeIndicator, + Indicator: FlowCollectionIndicator, + Value: string(SequenceStartCharacter), + Origin: org, + Position: pos, + } +} + +// SequenceEnd create token for SequenceEnd +func SequenceEnd(org string, pos *Position) *Token { + return &Token{ + Type: SequenceEndType, + CharacterType: CharacterTypeIndicator, + Indicator: FlowCollectionIndicator, + Value: string(SequenceEndCharacter), + Origin: org, + Position: pos, + } +} + +// MappingStart create token for MappingStart +func MappingStart(org string, pos *Position) *Token { + return &Token{ + Type: MappingStartType, + CharacterType: CharacterTypeIndicator, + Indicator: FlowCollectionIndicator, + Value: string(MappingStartCharacter), + Origin: org, + Position: pos, + } +} + +// MappingEnd create token for MappingEnd +func MappingEnd(org string, pos *Position) *Token { + return &Token{ + Type: MappingEndType, + CharacterType: CharacterTypeIndicator, + Indicator: FlowCollectionIndicator, + Value: string(MappingEndCharacter), + Origin: org, + Position: pos, + } +} + +// Comment create token for Comment +func Comment(value string, org string, pos *Position) *Token { + return &Token{ + Type: CommentType, + CharacterType: CharacterTypeIndicator, + Indicator: CommentIndicator, + Value: value, + Origin: org, + Position: pos, + } +} + +// Anchor create token for Anchor +func Anchor(org string, pos *Position) *Token { + return &Token{ + Type: AnchorType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: string(AnchorCharacter), + Origin: org, + Position: pos, + } +} + +// Alias create token for Alias +func Alias(org string, pos *Position) *Token { + return &Token{ + Type: AliasType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: string(AliasCharacter), + Origin: org, + Position: pos, + } +} + +// Tag create token for Tag +func Tag(value string, org string, pos *Position) *Token { + fn := ReservedTagKeywordMap[ReservedTagKeyword(value)] + if fn != nil { + return fn(value, org, pos) + } + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } +} + +// Literal create token for Literal +func Literal(value string, org string, pos *Position) *Token { + return &Token{ + Type: LiteralType, + CharacterType: CharacterTypeIndicator, + Indicator: BlockScalarIndicator, + Value: value, + Origin: org, + Position: pos, + } +} + +// Folded create token for Folded +func Folded(value string, org string, pos *Position) *Token { + return &Token{ + Type: FoldedType, + CharacterType: CharacterTypeIndicator, + Indicator: BlockScalarIndicator, + Value: value, + Origin: org, + Position: pos, + } +} + +// SingleQuote create token for SingleQuote +func SingleQuote(value string, org string, pos *Position) *Token { + return &Token{ + Type: SingleQuoteType, + CharacterType: CharacterTypeIndicator, + Indicator: QuotedScalarIndicator, + Value: value, + Origin: org, + Position: pos, + } +} + +// DoubleQuote create token for DoubleQuote +func DoubleQuote(value string, org string, pos *Position) *Token { + return &Token{ + Type: DoubleQuoteType, + CharacterType: CharacterTypeIndicator, + Indicator: QuotedScalarIndicator, + Value: value, + Origin: org, + Position: pos, + } +} + +// Directive create token for Directive +func Directive(org string, pos *Position) *Token { + return &Token{ + Type: DirectiveType, + CharacterType: CharacterTypeIndicator, + Indicator: DirectiveIndicator, + Value: string(DirectiveCharacter), + Origin: org, + Position: pos, + } +} + +// Space create token for Space +func Space(pos *Position) *Token { + return &Token{ + Type: SpaceType, + CharacterType: CharacterTypeWhiteSpace, + Indicator: NotIndicator, + Value: string(SpaceCharacter), + Origin: string(SpaceCharacter), + Position: pos, + } +} + +// MergeKey create token for MergeKey +func MergeKey(org string, pos *Position) *Token { + return &Token{ + Type: MergeKeyType, + CharacterType: CharacterTypeMiscellaneous, + Indicator: NotIndicator, + Value: "<<", + Origin: org, + Position: pos, + } +} + +// DocumentHeader create token for DocumentHeader +func DocumentHeader(org string, pos *Position) *Token { + return &Token{ + Type: DocumentHeaderType, + CharacterType: CharacterTypeMiscellaneous, + Indicator: NotIndicator, + Value: "---", + Origin: org, + Position: pos, + } +} + +// DocumentEnd create token for DocumentEnd +func DocumentEnd(org string, pos *Position) *Token { + return &Token{ + Type: DocumentEndType, + CharacterType: CharacterTypeMiscellaneous, + Indicator: NotIndicator, + Value: "...", + Origin: org, + Position: pos, + } +} + +// DetectLineBreakCharacter detect line break character in only one inside scalar content scope. +func DetectLineBreakCharacter(src string) string { + nc := strings.Count(src, "\n") + rc := strings.Count(src, "\r") + rnc := strings.Count(src, "\r\n") + switch { + case nc == rnc && rc == rnc: + return "\r\n" + case rc > nc: + return "\r" + default: + return "\n" + } +} diff --git a/vendor/github.com/goccy/go-yaml/validate.go b/vendor/github.com/goccy/go-yaml/validate.go new file mode 100644 index 000000000..20a2d6d91 --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/validate.go @@ -0,0 +1,13 @@ +package yaml + +// StructValidator need to implement Struct method only +// ( see https://pkg.go.dev/github.com/go-playground/validator/v10#Validate.Struct ) +type StructValidator interface { + Struct(interface{}) error +} + +// FieldError need to implement StructField method only +// ( see https://pkg.go.dev/github.com/go-playground/validator/v10#FieldError ) +type FieldError interface { + StructField() string +} diff --git a/vendor/github.com/goccy/go-yaml/yaml.go b/vendor/github.com/goccy/go-yaml/yaml.go new file mode 100644 index 000000000..2e541d855 --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/yaml.go @@ -0,0 +1,290 @@ +package yaml + +import ( + "bytes" + "context" + "io" + "reflect" + "sync" + + "github.com/goccy/go-yaml/ast" + "github.com/goccy/go-yaml/internal/errors" + "golang.org/x/xerrors" +) + +// BytesMarshaler interface may be implemented by types to customize their +// behavior when being marshaled into a YAML document. The returned value +// is marshaled in place of the original value implementing Marshaler. +// +// If an error is returned by MarshalYAML, the marshaling procedure stops +// and returns with the provided error. +type BytesMarshaler interface { + MarshalYAML() ([]byte, error) +} + +// BytesMarshalerContext interface use BytesMarshaler with context.Context. +type BytesMarshalerContext interface { + MarshalYAML(context.Context) ([]byte, error) +} + +// InterfaceMarshaler interface has MarshalYAML compatible with github.com/go-yaml/yaml package. +type InterfaceMarshaler interface { + MarshalYAML() (interface{}, error) +} + +// InterfaceMarshalerContext interface use InterfaceMarshaler with context.Context. +type InterfaceMarshalerContext interface { + MarshalYAML(context.Context) (interface{}, error) +} + +// BytesUnmarshaler interface may be implemented by types to customize their +// behavior when being unmarshaled from a YAML document. +type BytesUnmarshaler interface { + UnmarshalYAML([]byte) error +} + +// BytesUnmarshalerContext interface use BytesUnmarshaler with context.Context. +type BytesUnmarshalerContext interface { + UnmarshalYAML(context.Context, []byte) error +} + +// InterfaceUnmarshaler interface has UnmarshalYAML compatible with github.com/go-yaml/yaml package. +type InterfaceUnmarshaler interface { + UnmarshalYAML(func(interface{}) error) error +} + +// InterfaceUnmarshalerContext interface use InterfaceUnmarshaler with context.Context. +type InterfaceUnmarshalerContext interface { + UnmarshalYAML(context.Context, func(interface{}) error) error +} + +// MapItem is an item in a MapSlice. +type MapItem struct { + Key, Value interface{} +} + +// MapSlice encodes and decodes as a YAML map. +// The order of keys is preserved when encoding and decoding. +type MapSlice []MapItem + +// ToMap convert to map[interface{}]interface{}. +func (s MapSlice) ToMap() map[interface{}]interface{} { + v := map[interface{}]interface{}{} + for _, item := range s { + v[item.Key] = item.Value + } + return v +} + +// Marshal serializes the value provided into a YAML document. The structure +// of the generated document will reflect the structure of the value itself. +// Maps and pointers (to struct, string, int, etc) are accepted as the in value. +// +// Struct fields are only marshalled if they are exported (have an upper case +// first letter), and are marshalled using the field name lowercased as the +// default key. Custom keys may be defined via the "yaml" name in the field +// tag: the content preceding the first comma is used as the key, and the +// following comma-separated options are used to tweak the marshalling process. +// Conflicting names result in a runtime error. +// +// The field tag format accepted is: +// +// `(...) yaml:"[][,[,]]" (...)` +// +// The following flags are currently supported: +// +// omitempty Only include the field if it's not set to the zero +// value for the type or to empty slices or maps. +// Zero valued structs will be omitted if all their public +// fields are zero, unless they implement an IsZero +// method (see the IsZeroer interface type), in which +// case the field will be included if that method returns true. +// +// flow Marshal using a flow style (useful for structs, +// sequences and maps). +// +// inline Inline the field, which must be a struct or a map, +// causing all of its fields or keys to be processed as if +// they were part of the outer struct. For maps, keys must +// not conflict with the yaml keys of other struct fields. +// +// anchor Marshal with anchor. If want to define anchor name explicitly, use anchor=name style. +// Otherwise, if used 'anchor' name only, used the field name lowercased as the anchor name +// +// alias Marshal with alias. If want to define alias name explicitly, use alias=name style. +// Otherwise, If omitted alias name and the field type is pointer type, +// assigned anchor name automatically from same pointer address. +// +// In addition, if the key is "-", the field is ignored. +// +// For example: +// +// type T struct { +// F int `yaml:"a,omitempty"` +// B int +// } +// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n" +// yaml.Marshal(&T{F: 1}) // Returns "a: 1\nb: 0\n" +// +func Marshal(v interface{}) ([]byte, error) { + return MarshalWithOptions(v) +} + +// MarshalWithOptions serializes the value provided into a YAML document with EncodeOptions. +func MarshalWithOptions(v interface{}, opts ...EncodeOption) ([]byte, error) { + return MarshalContext(context.Background(), v, opts...) +} + +// MarshalContext serializes the value provided into a YAML document with context.Context and EncodeOptions. +func MarshalContext(ctx context.Context, v interface{}, opts ...EncodeOption) ([]byte, error) { + var buf bytes.Buffer + if err := NewEncoder(&buf, opts...).EncodeContext(ctx, v); err != nil { + return nil, errors.Wrapf(err, "failed to marshal") + } + return buf.Bytes(), nil +} + +// ValueToNode convert from value to ast.Node. +func ValueToNode(v interface{}, opts ...EncodeOption) (ast.Node, error) { + var buf bytes.Buffer + node, err := NewEncoder(&buf, opts...).EncodeToNode(v) + if err != nil { + return nil, errors.Wrapf(err, "failed to convert value to node") + } + return node, nil +} + +// Unmarshal decodes the first document found within the in byte slice +// and assigns decoded values into the out value. +// +// Struct fields are only unmarshalled if they are exported (have an +// upper case first letter), and are unmarshalled using the field name +// lowercased as the default key. Custom keys may be defined via the +// "yaml" name in the field tag: the content preceding the first comma +// is used as the key, and the following comma-separated options are +// used to tweak the marshalling process (see Marshal). +// Conflicting names result in a runtime error. +// +// For example: +// +// type T struct { +// F int `yaml:"a,omitempty"` +// B int +// } +// var t T +// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t) +// +// See the documentation of Marshal for the format of tags and a list of +// supported tag options. +// +func Unmarshal(data []byte, v interface{}) error { + return UnmarshalWithOptions(data, v) +} + +// UnmarshalWithOptions decodes with DecodeOptions the first document found within the in byte slice +// and assigns decoded values into the out value. +func UnmarshalWithOptions(data []byte, v interface{}, opts ...DecodeOption) error { + return UnmarshalContext(context.Background(), data, v, opts...) +} + +// UnmarshalContext decodes with context.Context and DecodeOptions. +func UnmarshalContext(ctx context.Context, data []byte, v interface{}, opts ...DecodeOption) error { + dec := NewDecoder(bytes.NewBuffer(data), opts...) + if err := dec.DecodeContext(ctx, v); err != nil { + if err == io.EOF { + return nil + } + return errors.Wrapf(err, "failed to unmarshal") + } + return nil +} + +// NodeToValue converts node to the value pointed to by v. +func NodeToValue(node ast.Node, v interface{}, opts ...DecodeOption) error { + var buf bytes.Buffer + if err := NewDecoder(&buf, opts...).DecodeFromNode(node, v); err != nil { + return errors.Wrapf(err, "failed to convert node to value") + } + return nil +} + +// FormatError is a utility function that takes advantage of the metadata +// stored in the errors returned by this package's parser. +// +// If the second argument `colored` is true, the error message is colorized. +// If the third argument `inclSource` is true, the error message will +// contain snippets of the YAML source that was used. +func FormatError(e error, colored, inclSource bool) string { + var pp errors.PrettyPrinter + if xerrors.As(e, &pp) { + var buf bytes.Buffer + pp.PrettyPrint(&errors.Sink{&buf}, colored, inclSource) + return buf.String() + } + + return e.Error() +} + +// YAMLToJSON convert YAML bytes to JSON. +func YAMLToJSON(bytes []byte) ([]byte, error) { + var v interface{} + if err := UnmarshalWithOptions(bytes, &v, UseOrderedMap()); err != nil { + return nil, errors.Wrapf(err, "failed to unmarshal") + } + out, err := MarshalWithOptions(v, JSON()) + if err != nil { + return nil, errors.Wrapf(err, "failed to marshal with json option") + } + return out, nil +} + +// JSONToYAML convert JSON bytes to YAML. +func JSONToYAML(bytes []byte) ([]byte, error) { + var v interface{} + if err := UnmarshalWithOptions(bytes, &v, UseOrderedMap()); err != nil { + return nil, errors.Wrapf(err, "failed to unmarshal from json bytes") + } + out, err := Marshal(v) + if err != nil { + return nil, errors.Wrapf(err, "failed to marshal") + } + return out, nil +} + +var ( + globalCustomMarshalerMu sync.Mutex + globalCustomUnmarshalerMu sync.Mutex + globalCustomMarshalerMap = map[reflect.Type]func(interface{}) ([]byte, error){} + globalCustomUnmarshalerMap = map[reflect.Type]func(interface{}, []byte) error{} +) + +// RegisterCustomMarshaler overrides any encoding process for the type specified in generics. +// If you want to switch the behavior for each encoder, use `CustomMarshaler` defined as EncodeOption. +// +// NOTE: If type T implements MarshalYAML for pointer receiver, the type specified in RegisterCustomMarshaler must be *T. +// If RegisterCustomMarshaler and CustomMarshaler of EncodeOption are specified for the same type, +// the CustomMarshaler specified in EncodeOption takes precedence. +func RegisterCustomMarshaler[T any](marshaler func(T) ([]byte, error)) { + globalCustomMarshalerMu.Lock() + defer globalCustomMarshalerMu.Unlock() + + var typ T + globalCustomMarshalerMap[reflect.TypeOf(typ)] = func(v interface{}) ([]byte, error) { + return marshaler(v.(T)) + } +} + +// RegisterCustomUnmarshaler overrides any decoding process for the type specified in generics. +// If you want to switch the behavior for each decoder, use `CustomUnmarshaler` defined as DecodeOption. +// +// NOTE: If RegisterCustomUnmarshaler and CustomUnmarshaler of DecodeOption are specified for the same type, +// the CustomUnmarshaler specified in DecodeOption takes precedence. +func RegisterCustomUnmarshaler[T any](unmarshaler func(*T, []byte) error) { + globalCustomUnmarshalerMu.Lock() + defer globalCustomUnmarshalerMu.Unlock() + + var typ *T + globalCustomUnmarshalerMap[reflect.TypeOf(typ)] = func(v interface{}, b []byte) error { + return unmarshaler(v.(*T), b) + } +} diff --git a/vendor/github.com/gookit/color/README.md b/vendor/github.com/gookit/color/README.md index 718b11b58..77d50ca3b 100644 --- a/vendor/github.com/gookit/color/README.md +++ b/vendor/github.com/gookit/color/README.md @@ -570,6 +570,7 @@ Check out these projects, which use https://github.com/gookit/color : - [xo/terminfo](https://github.com/xo/terminfo) - [beego/bee](https://github.com/beego/bee) - [issue9/term](https://github.com/issue9/term) + - [muesli/termenv](https://github.com/muesli/termenv) - [ANSI escape code](https://en.wikipedia.org/wiki/ANSI_escape_code) - [Standard ANSI color map](https://conemu.github.io/en/AnsiEscapeCodes.html#Standard_ANSI_color_map) - [Terminal Colors](https://gist.github.com/XVilka/8346728) diff --git a/vendor/github.com/gookit/color/README.zh-CN.md b/vendor/github.com/gookit/color/README.zh-CN.md index 1b144058e..192a89c5a 100644 --- a/vendor/github.com/gookit/color/README.zh-CN.md +++ b/vendor/github.com/gookit/color/README.zh-CN.md @@ -578,6 +578,7 @@ const ( ## 参考项目 - [inhere/console](https://github.com/inhere/php-console) + - [muesli/termenv](https://github.com/muesli/termenv) - [xo/terminfo](https://github.com/xo/terminfo) - [beego/bee](https://github.com/beego/bee) - [issue9/term](https://github.com/issue9/term) diff --git a/vendor/github.com/gookit/color/any.go b/vendor/github.com/gookit/color/any.go new file mode 100644 index 000000000..8bf31c181 --- /dev/null +++ b/vendor/github.com/gookit/color/any.go @@ -0,0 +1,6 @@ +//go:build !go1.18 +// +build !go1.18 + +package color + +type any = interface{} diff --git a/vendor/github.com/gookit/color/color.go b/vendor/github.com/gookit/color/color.go index 59e0b0b67..22de1b045 100644 --- a/vendor/github.com/gookit/color/color.go +++ b/vendor/github.com/gookit/color/color.go @@ -183,7 +183,7 @@ func InnerErrs() []error { // Usage: // // msg := RenderCode("3;32;45", "some", "message") -func RenderCode(code string, args ...interface{}) string { +func RenderCode(code string, args ...any) string { var message string if ln := len(args); ln == 0 { return "" @@ -205,7 +205,7 @@ func RenderCode(code string, args ...interface{}) string { // 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 { +func RenderWithSpaces(code string, args ...any) string { msg := formatArgsForPrintln(args) if len(code) == 0 { return msg diff --git a/vendor/github.com/gookit/color/color_16.go b/vendor/github.com/gookit/color/color_16.go index 3551521c0..0b70efe44 100644 --- a/vendor/github.com/gookit/color/color_16.go +++ b/vendor/github.com/gookit/color/color_16.go @@ -188,57 +188,65 @@ func (c Color) Text(message string) string { return RenderString(c.String(), mes // 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...) } +// +// green := color.FgGreen.Render +// fmt.Println(green("message")) +func (c Color) Render(a ...any) 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...) } +// +// green := color.FgGreen.Renderln +// fmt.Println(green("message")) +func (c Color) Renderln(a ...any) 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 ...any) string { return RenderCode(c.String(), a...) } // Sprintf format and render message. // // Usage: -// green := color.Green.Sprintf -// colored := green("message") -func (c Color) Sprintf(format string, args ...interface{}) string { +// +// green := color.Green.Sprintf +// colored := green("message") +func (c Color) Sprintf(format string, args ...any) string { return RenderString(c.String(), fmt.Sprintf(format, args...)) } // Print messages. // // Usage: -// color.Green.Print("message") +// +// color.Green.Print("message") +// // OR: -// green := color.FgGreen.Print -// green("message") -func (c Color) Print(args ...interface{}) { +// +// green := color.FgGreen.Print +// green("message") +func (c Color) Print(args ...any) { doPrintV2(c.Code(), fmt.Sprint(args...)) } // Printf format and print messages. // // Usage: -// color.Cyan.Printf("string %s", "arg0") -func (c Color) Printf(format string, a ...interface{}) { +// +// color.Cyan.Printf("string %s", "arg0") +func (c Color) Printf(format string, a ...any) { doPrintV2(c.Code(), fmt.Sprintf(format, a...)) } // Println messages with new line -func (c Color) Println(a ...interface{}) { doPrintlnV2(c.String(), a) } +func (c Color) Println(a ...any) { doPrintlnV2(c.String(), a) } // Light current color. eg: 36(FgCyan) -> 96(FgLightCyan). // // Usage: -// lightCyan := Cyan.Light() -// lightCyan.Print("message") +// +// lightCyan := Cyan.Light() +// lightCyan.Print("message") func (c Color) Light() Color { val := int(c) if val >= 30 && val <= 47 { @@ -252,8 +260,9 @@ func (c Color) Light() Color { // Darken current color. eg. 96(FgLightCyan) -> 36(FgCyan) // // Usage: -// cyan := LightCyan.Darken() -// cyan.Print("message") +// +// cyan := LightCyan.Darken() +// cyan.Print("message") func (c Color) Darken() Color { val := int(c) if val >= 90 && val <= 107 { @@ -461,9 +470,7 @@ func Fg2Bg(val uint8) uint8 { } // Basic2nameMap data -func Basic2nameMap() map[uint8]string { - return basic2nameMap -} +func Basic2nameMap() map[uint8]string { return basic2nameMap } // func initName2basicMap() map[string]uint8 { // n2b := make(map[string]uint8, len(basic2nameMap)) diff --git a/vendor/github.com/gookit/color/color_256.go b/vendor/github.com/gookit/color/color_256.go index c95c0f7b4..991e604c0 100644 --- a/vendor/github.com/gookit/color/color_256.go +++ b/vendor/github.com/gookit/color/color_256.go @@ -19,16 +19,19 @@ from wikipedia, 256 color: // tpl for 8 bit 256 color(`2^8`) // // format: -// ESC[ … 38;5; … m // 选择前景色 -// ESC[ … 48;5; … m // 选择背景色 +// +// ESC[ … 38;5; … m // 选择前景色 +// ESC[ … 48;5; … m // 选择背景色 // // example: -// fg "\x1b[38;5;242m" -// bg "\x1b[48;5;208m" -// both "\x1b[38;5;242;48;5;208m" +// +// fg "\x1b[38;5;242m" +// bg "\x1b[48;5;208m" +// both "\x1b[38;5;242;48;5;208m" // // links: -// https://zh.wikipedia.org/wiki/ANSI%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97#8位 +// +// https://zh.wikipedia.org/wiki/ANSI%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97#8位 const ( TplFg256 = "38;5;%d" TplBg256 = "48;5;%d" @@ -45,12 +48,14 @@ const ( // 颜色值使用10进制和16进制都可 0x98 = 152 // // The color consists of two uint8: -// 0: color value -// 1: color type; Fg=0, Bg=1, >1: unset value +// +// 0: color value +// 1: color type; Fg=0, Bg=1, >1: unset value // // example: -// fg color: [152, 0] -// bg color: [152, 1] +// +// fg color: [152, 0] +// bg color: [152, 1] // // NOTICE: now support 256 color on windows CMD, PowerShell // lint warn - Name starts with package name @@ -87,27 +92,27 @@ func (c Color256) Reset() error { } // Print print message -func (c Color256) Print(a ...interface{}) { +func (c Color256) Print(a ...any) { doPrintV2(c.String(), fmt.Sprint(a...)) } // Printf format and print message -func (c Color256) Printf(format string, a ...interface{}) { +func (c Color256) Printf(format string, a ...any) { doPrintV2(c.String(), fmt.Sprintf(format, a...)) } // Println print message with newline -func (c Color256) Println(a ...interface{}) { +func (c Color256) Println(a ...any) { doPrintlnV2(c.String(), a) } // Sprint returns rendered message -func (c Color256) Sprint(a ...interface{}) string { +func (c Color256) Sprint(a ...any) string { return RenderCode(c.String(), a...) } // Sprintf returns format and rendered message -func (c Color256) Sprintf(format string, a ...interface{}) string { +func (c Color256) Sprintf(format string, a ...any) string { return RenderString(c.String(), fmt.Sprintf(format, a...)) } @@ -206,9 +211,10 @@ type Style256 struct { // S256 create a color256 style // // Usage: -// s := color.S256() -// s := color.S256(132) // fg -// s := color.S256(132, 203) // fg and bg +// +// s := color.S256() +// s := color.S256(132) // fg +// s := color.S256(132, 203) // fg and bg func S256(fgAndBg ...uint8) *Style256 { s := &Style256{} vl := len(fgAndBg) @@ -256,27 +262,27 @@ func (s *Style256) AddOpts(opts ...Color) *Style256 { } // Print message -func (s *Style256) Print(a ...interface{}) { +func (s *Style256) Print(a ...any) { doPrintV2(s.String(), fmt.Sprint(a...)) } // Printf format and print message -func (s *Style256) Printf(format string, a ...interface{}) { +func (s *Style256) Printf(format string, a ...any) { doPrintV2(s.String(), fmt.Sprintf(format, a...)) } // Println print message with newline -func (s *Style256) Println(a ...interface{}) { +func (s *Style256) Println(a ...any) { doPrintlnV2(s.String(), a) } // Sprint returns rendered message -func (s *Style256) Sprint(a ...interface{}) string { +func (s *Style256) Sprint(a ...any) string { return RenderCode(s.Code(), a...) } // Sprintf returns format and rendered message -func (s *Style256) Sprintf(format string, a ...interface{}) string { +func (s *Style256) Sprintf(format string, a ...any) string { return RenderString(s.Code(), fmt.Sprintf(format, a...)) } diff --git a/vendor/github.com/gookit/color/color_rgb.go b/vendor/github.com/gookit/color/color_rgb.go index ff3c1bb08..724cf6659 100644 --- a/vendor/github.com/gookit/color/color_rgb.go +++ b/vendor/github.com/gookit/color/color_rgb.go @@ -8,20 +8,24 @@ import ( // 24 bit RGB color // RGB: -// R 0-255 G 0-255 B 0-255 -// R 00-FF G 00-FF B 00-FF (16进制) +// +// R 0-255 G 0-255 B 0-255 +// R 00-FF G 00-FF B 00-FF (16进制) // // Format: -// ESC[ … 38;2;;; … m // Select RGB foreground color -// ESC[ … 48;2;;; … m // Choose RGB background color +// +// ESC[ … 38;2;;; … m // Select RGB foreground color +// ESC[ … 48;2;;; … m // Choose RGB background color // // links: -// https://zh.wikipedia.org/wiki/ANSI%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97#24位 +// +// https://zh.wikipedia.org/wiki/ANSI%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97#24位 // // example: -// fg: \x1b[38;2;30;144;255mMESSAGE\x1b[0m -// bg: \x1b[48;2;30;144;255mMESSAGE\x1b[0m -// both: \x1b[38;2;233;90;203;48;2;30;144;255mMESSAGE\x1b[0m +// +// fg: \x1b[38;2;30;144;255mMESSAGE\x1b[0m +// bg: \x1b[48;2;30;144;255mMESSAGE\x1b[0m +// both: \x1b[38;2;233;90;203;48;2;30;144;255mMESSAGE\x1b[0m const ( TplFgRGB = "38;2;%d;%d;%d" TplBgRGB = "48;2;%d;%d;%d" @@ -45,10 +49,11 @@ const ( // The last digit represents the foreground(0), background(1), >1 is unset value // // Usage: -// // 0, 1, 2 is R,G,B. -// // 3rd: Fg=0, Bg=1, >1: unset value -// RGBColor{30,144,255, 0} -// RGBColor{30,144,255, 1} +// +// // 0, 1, 2 is R,G,B. +// // 3rd: Fg=0, Bg=1, >1: unset value +// RGBColor{30,144,255, 0} +// RGBColor{30,144,255, 1} // // NOTICE: now support RGB color on Windows CMD, PowerShell type RGBColor [4]uint8 @@ -59,9 +64,10 @@ var emptyRGBColor = RGBColor{3: 99} // RGB color create. // // Usage: -// c := RGB(30,144,255) -// c := RGB(30,144,255, true) -// c.Print("message") +// +// c := RGB(30,144,255) +// c := RGB(30,144,255, true) +// c.Print("message") func RGB(r, g, b uint8, isBg ...bool) RGBColor { rgb := RGBColor{r, g, b} if len(isBg) > 0 && isBg[0] { @@ -90,11 +96,12 @@ func RgbFromInts(rgb []int, isBg ...bool) RGBColor { // HEX create RGB color from a HEX color string. // // Usage: -// c := HEX("ccc") // rgb: [204 204 204] -// c := HEX("aabbcc") // rgb: [170 187 204] -// c := HEX("#aabbcc") -// c := HEX("0xaabbcc") -// c.Print("message") +// +// c := HEX("ccc") // rgb: [204 204 204] +// c := HEX("aabbcc") // rgb: [170 187 204] +// c := HEX("#aabbcc") +// c := HEX("0xaabbcc") +// c.Print("message") func HEX(hex string, isBg ...bool) RGBColor { if rgb := HexToRgb(hex); len(rgb) > 0 { return RGB(uint8(rgb[0]), uint8(rgb[1]), uint8(rgb[2]), isBg...) @@ -139,11 +146,12 @@ func RGBFromSlice(rgb []uint8, isBg ...bool) RGBColor { // Support use color name in the {namedRgbMap} // // Usage: -// c := RGBFromString("170,187,204") -// c.Print("message") // -// c := RGBFromString("brown") -// c.Print("message with color brown") +// c := RGBFromString("170,187,204") +// c.Print("message") +// +// c := RGBFromString("brown") +// c.Print("message with color brown") func RGBFromString(rgb string, isBg ...bool) RGBColor { // use color name in the {namedRgbMap} if rgbVal, ok := namedRgbMap[rgb]; ok { @@ -180,27 +188,27 @@ func (c RGBColor) Reset() error { } // Print print message -func (c RGBColor) Print(a ...interface{}) { +func (c RGBColor) Print(a ...any) { doPrintV2(c.String(), fmt.Sprint(a...)) } // Printf format and print message -func (c RGBColor) Printf(format string, a ...interface{}) { +func (c RGBColor) Printf(format string, a ...any) { doPrintV2(c.String(), fmt.Sprintf(format, a...)) } // Println print message with newline -func (c RGBColor) Println(a ...interface{}) { +func (c RGBColor) Println(a ...any) { doPrintlnV2(c.String(), a) } // Sprint returns rendered message -func (c RGBColor) Sprint(a ...interface{}) string { +func (c RGBColor) Sprint(a ...any) string { return RenderCode(c.String(), a...) } // Sprintf returns format and rendered message -func (c RGBColor) Sprintf(format string, a ...interface{}) string { +func (c RGBColor) Sprintf(format string, a ...any) string { return RenderString(c.String(), fmt.Sprintf(format, a...)) } @@ -279,8 +287,8 @@ func (c RGBColor) C16() Color { return c.Basic() } // 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 @@ -303,8 +311,9 @@ func NewRGBStyle(fg RGBColor, bg ...RGBColor) *RGBStyle { // HEXStyle create a RGBStyle from HEX color string. // // Usage: -// s := HEXStyle("aabbcc", "eee") -// s.Print("message") +// +// s := HEXStyle("aabbcc", "eee") +// s.Print("message") func HEXStyle(fg string, bg ...string) *RGBStyle { s := &RGBStyle{} if len(bg) > 0 { @@ -320,8 +329,9 @@ func HEXStyle(fg string, bg ...string) *RGBStyle { // RGBStyleFromString create a RGBStyle from color value string. // // Usage: -// s := RGBStyleFromString("170,187,204", "70,87,4") -// s.Print("message") +// +// s := RGBStyleFromString("170,187,204", "70,87,4") +// s.Print("message") func RGBStyleFromString(fg string, bg ...string) *RGBStyle { s := &RGBStyle{} if len(bg) > 0 { @@ -363,27 +373,27 @@ func (s *RGBStyle) AddOpts(opts ...Color) *RGBStyle { } // Print print message -func (s *RGBStyle) Print(a ...interface{}) { +func (s *RGBStyle) Print(a ...any) { doPrintV2(s.String(), fmt.Sprint(a...)) } // Printf format and print message -func (s *RGBStyle) Printf(format string, a ...interface{}) { +func (s *RGBStyle) Printf(format string, a ...any) { doPrintV2(s.String(), fmt.Sprintf(format, a...)) } // Println print message with newline -func (s *RGBStyle) Println(a ...interface{}) { +func (s *RGBStyle) Println(a ...any) { doPrintlnV2(s.String(), a) } // Sprint returns rendered message -func (s *RGBStyle) Sprint(a ...interface{}) string { +func (s *RGBStyle) Sprint(a ...any) string { return RenderCode(s.String(), a...) } // Sprintf returns format and rendered message -func (s *RGBStyle) Sprintf(format string, a ...interface{}) string { +func (s *RGBStyle) Sprintf(format string, a ...any) string { return RenderString(s.String(), fmt.Sprintf(format, a...)) } diff --git a/vendor/github.com/gookit/color/color_tag.go b/vendor/github.com/gookit/color/color_tag.go index 4f6fed938..1d2b9d3fe 100644 --- a/vendor/github.com/gookit/color/color_tag.go +++ b/vendor/github.com/gookit/color/color_tag.go @@ -41,7 +41,8 @@ var ( // There are internal defined fg color tags // // Usage: -// content text +// +// content text // // @notice 加 0 在前面是为了防止之前的影响到现在的设置 var colorTags = map[string]string{ @@ -324,15 +325,17 @@ func (tp *TagParser) ParseByEnv(str string) string { return tp.Parse(str) } -// Parse parse given string, replace color tag and return rendered string +// Parse given string, replace color tag and return rendered string // // Use built in tags: -// CONTENT -// // e.g: `message` +// +// CONTENT +// // e.g: `message` // // Custom tag attributes: -// `CONTENT` -// // e.g: `wel` +// +// `CONTENT` +// // e.g: `wel` func (tp *TagParser) Parse(str string) string { // not contains color tag if !strings.Contains(str, "") { @@ -376,26 +379,30 @@ func ReplaceTag(str string) string { // ParseCodeFromAttr parse color attributes. // // attr format: -// // VALUE please see var: FgColors, BgColors, AllOptions -// "fg=VALUE;bg=VALUE;op=VALUE" +// +// // 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" +// +// "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 +// +// // hex // "fg=fc1cac" // "fg=fc1cac;bg=c2c3c4" -// // r,g,b +// // r,g,b // "fg=23,45,214" // "fg=23,45,214;bg=109,99,88" func ParseCodeFromAttr(attr string) (code string) { @@ -476,12 +483,10 @@ func ClearTag(s string) string { *************************************************************/ // GetTagCode get color code by tag name -func GetTagCode(name string) string { - return colorTags[name] -} +func GetTagCode(name string) string { return colorTags[name] } // ApplyTag for messages -func ApplyTag(tag string, a ...interface{}) string { +func ApplyTag(tag string, a ...any) string { return RenderCode(GetTagCode(tag), a...) } @@ -510,11 +515,12 @@ func IsDefinedTag(name string) bool { // Tag value is a defined style name // Usage: -// Tag("info").Println("message") +// +// Tag("info").Println("message") type Tag string // Print messages -func (tg Tag) Print(a ...interface{}) { +func (tg Tag) Print(a ...any) { name := string(tg) str := fmt.Sprint(a...) @@ -526,7 +532,7 @@ func (tg Tag) Print(a ...interface{}) { } // Printf format and print messages -func (tg Tag) Printf(format string, a ...interface{}) { +func (tg Tag) Printf(format string, a ...any) { name := string(tg) str := fmt.Sprintf(format, a...) @@ -538,7 +544,7 @@ func (tg Tag) Printf(format string, a ...interface{}) { } // Println messages line -func (tg Tag) Println(a ...interface{}) { +func (tg Tag) Println(a ...any) { name := string(tg) if stl := GetStyle(name); !stl.IsEmpty() { stl.Println(a...) @@ -548,12 +554,12 @@ func (tg Tag) Println(a ...interface{}) { } // Sprint render messages -func (tg Tag) Sprint(a ...interface{}) string { +func (tg Tag) Sprint(a ...any) string { return RenderCode(GetTagCode(string(tg)), a...) } // Sprintf format and render messages -func (tg Tag) Sprintf(format string, a ...interface{}) string { +func (tg Tag) Sprintf(format string, a ...any) string { tag := string(tg) str := fmt.Sprintf(format, a...) diff --git a/vendor/github.com/gookit/color/printer.go b/vendor/github.com/gookit/color/printer.go index 326aabc0b..985a0b624 100644 --- a/vendor/github.com/gookit/color/printer.go +++ b/vendor/github.com/gookit/color/printer.go @@ -9,18 +9,19 @@ import "fmt" // PrinterFace interface type PrinterFace interface { fmt.Stringer - Sprint(a ...interface{}) string - Sprintf(format string, a ...interface{}) string - Print(a ...interface{}) - Printf(format string, a ...interface{}) - Println(a ...interface{}) + Sprint(a ...any) string + Sprintf(format string, a ...any) string + Print(a ...any) + Printf(format string, a ...any) + Println(a ...any) } // Printer a generic color message printer. // // Usage: -// p := &Printer{Code: "32;45;3"} -// p.Print("message") +// +// p := &Printer{Code: "32;45;3"} +// p.Print("message") type Printer struct { // NoColor disable color. NoColor bool @@ -40,27 +41,27 @@ func (p *Printer) String() string { } // Sprint returns rendering colored messages -func (p *Printer) Sprint(a ...interface{}) string { +func (p *Printer) Sprint(a ...any) string { return RenderCode(p.String(), a...) } // Sprintf returns format and rendering colored messages -func (p *Printer) Sprintf(format string, a ...interface{}) string { +func (p *Printer) Sprintf(format string, a ...any) string { return RenderString(p.String(), fmt.Sprintf(format, a...)) } // Print rendering colored messages -func (p *Printer) Print(a ...interface{}) { +func (p *Printer) Print(a ...any) { doPrintV2(p.String(), fmt.Sprint(a...)) } // Printf format and rendering colored messages -func (p *Printer) Printf(format string, a ...interface{}) { +func (p *Printer) Printf(format string, a ...any) { doPrintV2(p.String(), fmt.Sprintf(format, a...)) } // Println rendering colored messages with newline -func (p *Printer) Println(a ...interface{}) { +func (p *Printer) Println(a ...any) { doPrintlnV2(p.Code, a) } @@ -77,46 +78,56 @@ func (p *Printer) IsEmpty() bool { type SimplePrinter struct{} // Print message -func (s *SimplePrinter) Print(v ...interface{}) { +func (s *SimplePrinter) Print(v ...any) { Print(v...) } // Printf message -func (s *SimplePrinter) Printf(format string, v ...interface{}) { +func (s *SimplePrinter) Printf(format string, v ...any) { Printf(format, v...) } // Println message -func (s *SimplePrinter) Println(v ...interface{}) { +func (s *SimplePrinter) Println(v ...any) { Println(v...) } +// Successf message +func (s *SimplePrinter) Successf(format string, a ...any) { + Success.Printf(format, a...) +} + +// Successln message +func (s *SimplePrinter) Successln(a ...any) { + Success.Println(a...) +} + // Infof message -func (s *SimplePrinter) Infof(format string, a ...interface{}) { +func (s *SimplePrinter) Infof(format string, a ...any) { Info.Printf(format, a...) } // Infoln message -func (s *SimplePrinter) Infoln(a ...interface{}) { +func (s *SimplePrinter) Infoln(a ...any) { Info.Println(a...) } // Warnf message -func (s *SimplePrinter) Warnf(format string, a ...interface{}) { +func (s *SimplePrinter) Warnf(format string, a ...any) { Warn.Printf(format, a...) } // Warnln message -func (s *SimplePrinter) Warnln(a ...interface{}) { +func (s *SimplePrinter) Warnln(a ...any) { Warn.Println(a...) } // Errorf message -func (s *SimplePrinter) Errorf(format string, a ...interface{}) { +func (s *SimplePrinter) Errorf(format string, a ...any) { Error.Printf(format, a...) } // Errorln message -func (s *SimplePrinter) Errorln(a ...interface{}) { +func (s *SimplePrinter) Errorln(a ...any) { Error.Println(a...) } diff --git a/vendor/github.com/gookit/color/quickstart.go b/vendor/github.com/gookit/color/quickstart.go index 4dbd1a431..b368b8a14 100644 --- a/vendor/github.com/gookit/color/quickstart.go +++ b/vendor/github.com/gookit/color/quickstart.go @@ -5,104 +5,104 @@ package color *************************************************************/ // Redp print message with Red color -func Redp(a ...interface{}) { Red.Print(a...) } +func Redp(a ...any) { Red.Print(a...) } // Redf print message with Red color -func Redf(format string, a ...interface{}) { Red.Printf(format, a...) } +func Redf(format string, a ...any) { Red.Printf(format, a...) } // Redln print message line with Red color -func Redln(a ...interface{}) { Red.Println(a...) } +func Redln(a ...any) { Red.Println(a...) } // Bluep print message with Blue color -func Bluep(a ...interface{}) { Blue.Print(a...) } +func Bluep(a ...any) { Blue.Print(a...) } // Bluef print message with Blue color -func Bluef(format string, a ...interface{}) { Blue.Printf(format, a...) } +func Bluef(format string, a ...any) { Blue.Printf(format, a...) } // Blueln print message line with Blue color -func Blueln(a ...interface{}) { Blue.Println(a...) } +func Blueln(a ...any) { Blue.Println(a...) } // Cyanp print message with Cyan color -func Cyanp(a ...interface{}) { Cyan.Print(a...) } +func Cyanp(a ...any) { Cyan.Print(a...) } // Cyanf print message with Cyan color -func Cyanf(format string, a ...interface{}) { Cyan.Printf(format, a...) } +func Cyanf(format string, a ...any) { Cyan.Printf(format, a...) } // Cyanln print message line with Cyan color -func Cyanln(a ...interface{}) { Cyan.Println(a...) } +func Cyanln(a ...any) { Cyan.Println(a...) } // Grayp print message with Gray color -func Grayp(a ...interface{}) { Gray.Print(a...) } +func Grayp(a ...any) { Gray.Print(a...) } // Grayf print message with Gray color -func Grayf(format string, a ...interface{}) { Gray.Printf(format, a...) } +func Grayf(format string, a ...any) { Gray.Printf(format, a...) } // Grayln print message line with Gray color -func Grayln(a ...interface{}) { Gray.Println(a...) } +func Grayln(a ...any) { Gray.Println(a...) } // Greenp print message with Green color -func Greenp(a ...interface{}) { Green.Print(a...) } +func Greenp(a ...any) { Green.Print(a...) } // Greenf print message with Green color -func Greenf(format string, a ...interface{}) { Green.Printf(format, a...) } +func Greenf(format string, a ...any) { Green.Printf(format, a...) } // Greenln print message line with Green color -func Greenln(a ...interface{}) { Green.Println(a...) } +func Greenln(a ...any) { Green.Println(a...) } // Yellowp print message with Yellow color -func Yellowp(a ...interface{}) { Yellow.Print(a...) } +func Yellowp(a ...any) { Yellow.Print(a...) } // Yellowf print message with Yellow color -func Yellowf(format string, a ...interface{}) { Yellow.Printf(format, a...) } +func Yellowf(format string, a ...any) { Yellow.Printf(format, a...) } // Yellowln print message line with Yellow color -func Yellowln(a ...interface{}) { Yellow.Println(a...) } +func Yellowln(a ...any) { Yellow.Println(a...) } // Magentap print message with Magenta color -func Magentap(a ...interface{}) { Magenta.Print(a...) } +func Magentap(a ...any) { Magenta.Print(a...) } // Magentaf print message with Magenta color -func Magentaf(format string, a ...interface{}) { Magenta.Printf(format, a...) } +func Magentaf(format string, a ...any) { Magenta.Printf(format, a...) } // Magentaln print message line with Magenta color -func Magentaln(a ...interface{}) { Magenta.Println(a...) } +func Magentaln(a ...any) { Magenta.Println(a...) } /************************************************************* * quick use style print message *************************************************************/ // Infop print message with Info color -func Infop(a ...interface{}) { Info.Print(a...) } +func Infop(a ...any) { Info.Print(a...) } // Infof print message with Info style -func Infof(format string, a ...interface{}) { Info.Printf(format, a...) } +func Infof(format string, a ...any) { Info.Printf(format, a...) } // Infoln print message with Info style -func Infoln(a ...interface{}) { Info.Println(a...) } +func Infoln(a ...any) { Info.Println(a...) } // Successp print message with success color -func Successp(a ...interface{}) { Success.Print(a...) } +func Successp(a ...any) { Success.Print(a...) } // Successf print message with success style -func Successf(format string, a ...interface{}) { Success.Printf(format, a...) } +func Successf(format string, a ...any) { Success.Printf(format, a...) } // Successln print message with success style -func Successln(a ...interface{}) { Success.Println(a...) } +func Successln(a ...any) { Success.Println(a...) } // Errorp print message with Error color -func Errorp(a ...interface{}) { Error.Print(a...) } +func Errorp(a ...any) { Error.Print(a...) } // Errorf print message with Error style -func Errorf(format string, a ...interface{}) { Error.Printf(format, a...) } +func Errorf(format string, a ...any) { Error.Printf(format, a...) } // Errorln print message with Error style -func Errorln(a ...interface{}) { Error.Println(a...) } +func Errorln(a ...any) { Error.Println(a...) } // Warnp print message with Warn color -func Warnp(a ...interface{}) { Warn.Print(a...) } +func Warnp(a ...any) { Warn.Print(a...) } // Warnf print message with Warn style -func Warnf(format string, a ...interface{}) { Warn.Printf(format, a...) } +func Warnf(format string, a ...any) { Warn.Printf(format, a...) } // Warnln print message with Warn style -func Warnln(a ...interface{}) { Warn.Println(a...) } +func Warnln(a ...any) { Warn.Println(a...) } diff --git a/vendor/github.com/gookit/color/style.go b/vendor/github.com/gookit/color/style.go index fad76fb33..a009d1d6e 100644 --- a/vendor/github.com/gookit/color/style.go +++ b/vendor/github.com/gookit/color/style.go @@ -12,12 +12,14 @@ import ( // Style a 16 color style. can add: fg color, bg color, color options // // Example: -// color.Style{color.FgGreen}.Print("message") +// +// color.Style{color.FgGreen}.Print("message") type Style []Color // New create a custom style // // Usage: +// // color.New(color.FgGreen).Print("message") // equals to: // color.Style{color.FgGreen}.Print("message") @@ -37,43 +39,45 @@ func (s *Style) Add(cs ...Color) { // Render render text // Usage: -// color.New(color.FgGreen).Render("text") -// color.New(color.FgGreen, color.BgBlack, color.OpBold).Render("text") -func (s Style) Render(a ...interface{}) string { +// +// color.New(color.FgGreen).Render("text") +// color.New(color.FgGreen, color.BgBlack, color.OpBold).Render("text") +func (s Style) Render(a ...any) string { return RenderCode(s.String(), a...) } // Renderln render text line. // like Println, will add spaces for each argument // Usage: -// color.New(color.FgGreen).Renderln("text", "more") -// color.New(color.FgGreen, color.BgBlack, color.OpBold).Render("text", "more") -func (s Style) Renderln(a ...interface{}) string { +// +// color.New(color.FgGreen).Renderln("text", "more") +// color.New(color.FgGreen, color.BgBlack, color.OpBold).Render("text", "more") +func (s Style) Renderln(a ...any) string { return RenderWithSpaces(s.String(), a...) } // Sprint is alias of the 'Render' -func (s Style) Sprint(a ...interface{}) string { +func (s Style) Sprint(a ...any) string { return RenderCode(s.String(), a...) } // Sprintf format and render message. -func (s Style) Sprintf(format string, a ...interface{}) string { +func (s Style) Sprintf(format string, a ...any) string { return RenderString(s.String(), fmt.Sprintf(format, a...)) } // Print render and Print text -func (s Style) Print(a ...interface{}) { +func (s Style) Print(a ...any) { doPrintV2(s.String(), fmt.Sprint(a...)) } // Printf render and print text -func (s Style) Printf(format string, a ...interface{}) { +func (s Style) Printf(format string, a ...any) { doPrintV2(s.Code(), fmt.Sprintf(format, a...)) } // Println render and print text line -func (s Style) Println(a ...interface{}) { +func (s Style) Println(a ...any) { doPrintlnV2(s.String(), a) } @@ -115,20 +119,20 @@ func (t *Theme) Save() { } // Tips use name as title, only apply style for name -func (t *Theme) Tips(format string, a ...interface{}) { +func (t *Theme) Tips(format string, a ...any) { // only apply style for name t.Print(strings.ToUpper(t.Name) + ": ") Printf(format+"\n", a...) } // Prompt use name as title, and apply style for message -func (t *Theme) Prompt(format string, a ...interface{}) { +func (t *Theme) Prompt(format string, a ...any) { title := strings.ToUpper(t.Name) + ":" t.Println(title, fmt.Sprintf(format, a...)) } // Block like Prompt, but will wrap a empty line -func (t *Theme) Block(format string, a ...interface{}) { +func (t *Theme) Block(format string, a ...any) { title := strings.ToUpper(t.Name) + ":\n" t.Println(title, fmt.Sprintf(format, a...)) @@ -140,10 +144,11 @@ func (t *Theme) Block(format string, a ...interface{}) { // internal themes(like bootstrap style) // Usage: -// color.Info.Print("message") -// color.Info.Printf("a %s message", "test") -// color.Warn.Println("message") -// color.Error.Println("message") +// +// color.Info.Print("message") +// color.Info.Printf("a %s message", "test") +// color.Warn.Println("message") +// color.Error.Println("message") var ( // Info color style Info = &Theme{"info", Style{OpReset, FgGreen}} @@ -175,7 +180,8 @@ var ( // Themes internal defined themes. // Usage: -// color.Themes["info"].Println("message") +// +// color.Themes["info"].Println("message") var Themes = map[string]*Theme{ "info": Info, "note": Note, @@ -211,7 +217,8 @@ func GetTheme(name string) *Theme { // Styles internal defined styles, like bootstrap styles. // Usage: -// color.Styles["info"].Println("message") +// +// color.Styles["info"].Println("message") var Styles = map[string]Style{ "info": {OpReset, FgGreen}, "note": {OpBold, FgLightCyan}, @@ -285,31 +292,31 @@ func (s *Scheme) Style(name string) Style { } // Infof message print -func (s *Scheme) Infof(format string, a ...interface{}) { +func (s *Scheme) Infof(format string, a ...any) { s.Styles["info"].Printf(format, a...) } // Infoln message print -func (s *Scheme) Infoln(v ...interface{}) { +func (s *Scheme) Infoln(v ...any) { s.Styles["info"].Println(v...) } // Warnf message print -func (s *Scheme) Warnf(format string, a ...interface{}) { +func (s *Scheme) Warnf(format string, a ...any) { s.Styles["warn"].Printf(format, a...) } // Warnln message print -func (s *Scheme) Warnln(v ...interface{}) { +func (s *Scheme) Warnln(v ...any) { s.Styles["warn"].Println(v...) } // Errorf message print -func (s *Scheme) Errorf(format string, a ...interface{}) { +func (s *Scheme) Errorf(format string, a ...any) { s.Styles["error"].Printf(format, a...) } // Errorln message print -func (s *Scheme) Errorln(v ...interface{}) { +func (s *Scheme) Errorln(v ...any) { s.Styles["error"].Println(v...) } diff --git a/vendor/github.com/gookit/color/utils.go b/vendor/github.com/gookit/color/utils.go index 4554b27ee..b6920f6dc 100644 --- a/vendor/github.com/gookit/color/utils.go +++ b/vendor/github.com/gookit/color/utils.go @@ -32,39 +32,31 @@ func ResetTerminal() error { *************************************************************/ // Print render color tag and print messages -func Print(a ...interface{}) { +func Print(a ...any) { Fprint(output, a...) } // Printf format and print messages -func Printf(format string, a ...interface{}) { +func Printf(format string, a ...any) { Fprintf(output, format, a...) } // Println messages with new line -func Println(a ...interface{}) { +func Println(a ...any) { Fprintln(output, a...) } // Fprint print rendered messages to writer // // Notice: will ignore print error -func Fprint(w io.Writer, a ...interface{}) { +func Fprint(w io.Writer, a ...any) { _, err := fmt.Fprint(w, Render(a...)) saveInternalError(err) - - // if isLikeInCmd { - // renderColorCodeOnCmd(func() { - // _, _ = fmt.Fprint(w, Render(a...)) - // }) - // } else { - // _, _ = fmt.Fprint(w, Render(a...)) - // } } // Fprintf print format and rendered messages to writer. // Notice: will ignore print error -func Fprintf(w io.Writer, format string, a ...interface{}) { +func Fprintf(w io.Writer, format string, a ...any) { str := fmt.Sprintf(format, a...) _, err := fmt.Fprint(w, ReplaceTag(str)) saveInternalError(err) @@ -72,7 +64,7 @@ func Fprintf(w io.Writer, format string, a ...interface{}) { // Fprintln print rendered messages line to writer // Notice: will ignore print error -func Fprintln(w io.Writer, a ...interface{}) { +func Fprintln(w io.Writer, a ...any) { str := formatArgsForPrintln(a) _, err := fmt.Fprintln(w, ReplaceTag(str)) saveInternalError(err) @@ -80,7 +72,7 @@ func Fprintln(w io.Writer, a ...interface{}) { // Lprint passes colored messages to a log.Logger for printing. // Notice: should be goroutine safe -func Lprint(l *log.Logger, a ...interface{}) { +func Lprint(l *log.Logger, a ...any) { l.Print(Render(a...)) } @@ -90,7 +82,7 @@ func Lprint(l *log.Logger, a ...interface{}) { // // text := Render("hello world!") // fmt.Println(text) -func Render(a ...interface{}) string { +func Render(a ...any) string { if len(a) == 0 { return "" } @@ -98,28 +90,23 @@ func Render(a ...interface{}) string { } // Sprint parse color tags, return rendered string -func Sprint(a ...interface{}) string { +func Sprint(a ...any) string { if len(a) == 0 { return "" } - return ReplaceTag(fmt.Sprint(a...)) } // Sprintf format and return rendered string -func Sprintf(format string, a ...interface{}) string { +func Sprintf(format string, a ...any) string { return ReplaceTag(fmt.Sprintf(format, a...)) } // String alias of the ReplaceTag -func String(s string) string { - return ReplaceTag(s) -} +func String(s string) string { return ReplaceTag(s) } // Text alias of the ReplaceTag -func Text(s string) string { - return ReplaceTag(s) -} +func Text(s string) string { return ReplaceTag(s) } // Uint8sToInts convert []uint8 to []int // func Uint8sToInts(u8s []uint8 ) []int { @@ -138,25 +125,17 @@ func Text(s string) string { func doPrintV2(code, str string) { _, err := fmt.Fprint(output, RenderString(code, str)) saveInternalError(err) - - // if isLikeInCmd { - // renderColorCodeOnCmd(func() { - // _, _ = fmt.Fprint(output, RenderString(code, str)) - // }) - // } else { - // _, _ = fmt.Fprint(output, RenderString(code, str)) - // } } // new implementation, support render full color code on pwsh.exe, cmd.exe -func doPrintlnV2(code string, args []interface{}) { +func doPrintlnV2(code string, args []any) { str := formatArgsForPrintln(args) _, err := fmt.Fprintln(output, RenderString(code, str)) saveInternalError(err) } // use Println, will add spaces for each arg -func formatArgsForPrintln(args []interface{}) (message string) { +func formatArgsForPrintln(args []any) (message string) { if ln := len(args); ln == 0 { message = "" } else if ln == 1 { @@ -178,7 +157,7 @@ func formatArgsForPrintln(args []interface{}) (message string) { // return debugMode == "on" // } -func debugf(f string, v ...interface{}) { +func debugf(f string, v ...any) { if debugMode { fmt.Print("COLOR_DEBUG: ") fmt.Printf(f, v...) diff --git a/vendor/github.com/gookit/config/v2/README.md b/vendor/github.com/gookit/config/v2/README.md index e011b3356..908385d62 100644 --- a/vendor/github.com/gookit/config/v2/README.md +++ b/vendor/github.com/gookit/config/v2/README.md @@ -84,7 +84,7 @@ package main import ( "github.com/gookit/config/v2" - "github.com/gookit/config/v2/yamlv3" + "github.com/gookit/config/v2/yaml" ) // go run ./examples/yaml.go @@ -92,7 +92,7 @@ func main() { config.WithOptions(config.ParseEnv) // add driver for support yaml content - config.AddDriver(yamlv3.Driver) + config.AddDriver(yaml.Driver) err := config.LoadFiles("testdata/yml_base.yml") if err != nil { @@ -389,7 +389,7 @@ Support parse default value by struct tag `default` c := config.New("test").WithOptions(config.ParseDefault) // only set name -c.SetData(map[string]interface{}{ +c.SetData(map[string]any{ "name": "inhere", }) @@ -421,7 +421,7 @@ dump.Println(user) ### Load Config - `LoadOSEnvs(nameToKeyMap map[string]string)` Load data from os ENV -- `LoadData(dataSource ...interface{}) (err error)` Load from struts or maps +- `LoadData(dataSource ...any) (err error)` Load from struts or maps - `LoadFlags(keys []string) (err error)` Load from CLI flags - `LoadExists(sourceFiles ...string) (err error)` - `LoadFiles(sourceFiles ...string) (err error)` @@ -445,7 +445,7 @@ dump.Println(user) - `Strings(key string) (arr []string)` - `SubDataMap(key string) maputi.Data` - `StringMap(key string) (mp map[string]string)` -- `Get(key string, findByPath ...bool) (value interface{})` +- `Get(key string, findByPath ...bool) (value any)` **Mapping data to struct:** @@ -455,14 +455,14 @@ dump.Println(user) ### Setting Values -- `Set(key string, val interface{}, setByPath ...bool) (err error)` +- `Set(key string, val any, setByPath ...bool) (err error)` ### Useful Methods - `Getenv(name string, defVal ...string) (val string)` - `AddDriver(driver Driver)` -- `Data() map[string]interface{}` -- `SetData(data map[string]interface{})` set data to override the Config.Data +- `Data() map[string]any` +- `SetData(data map[string]any)` set data to override the Config.Data - `Exists(key string, findByPath ...bool) bool` - `DumpTo(out io.Writer, format string) (n int64, err error)` @@ -497,11 +497,18 @@ Check out these projects, which use https://github.com/gookit/config : ## See also -- Ini parse [gookit/ini/parser](https://github.com/gookit/ini/tree/master/parser) -- Properties parse [gookit/properties](https://github.com/gookit/properties) -- Json5 parse [json5](https://github.com/yosuke-furukawa/json5) -- Yaml parse [go-yaml](https://github.com/go-yaml/yaml) -- Toml parse [go toml](https://github.com/BurntSushi/toml) +- Ini parser [gookit/ini/parser](https://github.com/gookit/ini/tree/master/parser) +- Properties parser [gookit/properties](https://github.com/gookit/properties) +- Json5 parser + - [yosuke-furukawa/json5](https://github.com/yosuke-furukawa/json5) + - [titanous/json5](https://github.com/titanous/json5) +- Json parser + - [goccy/go-json](https://github.com/goccy/go-json) + - [json-iterator/go](https://github.com/json-iterator/go) +- Yaml parser + - [goccy/go-yaml](https://github.com/goccy/go-yaml) + - [go-yaml/yaml](https://github.com/go-yaml/yaml) +- Toml parser [go toml](https://github.com/BurntSushi/toml) - Data merge [mergo](https://github.com/imdario/mergo) - Map structure [mapstructure](https://github.com/mitchellh/mapstructure) diff --git a/vendor/github.com/gookit/config/v2/README.zh-CN.md b/vendor/github.com/gookit/config/v2/README.zh-CN.md index 17b8a6aac..077e8046d 100644 --- a/vendor/github.com/gookit/config/v2/README.zh-CN.md +++ b/vendor/github.com/gookit/config/v2/README.zh-CN.md @@ -83,7 +83,7 @@ package main import ( "github.com/gookit/config/v2" - "github.com/gookit/config/v2/yamlv3" + "github.com/gookit/config/v2/yaml" ) // go run ./examples/yaml.go @@ -92,7 +92,7 @@ func main() { config.WithOptions(config.ParseEnv) // 添加驱动程序以支持yaml内容解析(除了JSON是默认支持,其他的则是按需使用) - config.AddDriver(yamlv3.Driver) + config.AddDriver(yaml.Driver) // 加载配置,可以同时传入多个文件 err := config.LoadFiles("testdata/yml_base.yml") @@ -371,7 +371,7 @@ NEW: 支持通过结构标签 `default` 解析并设置默认值 c := config.New("test").WithOptions(config.ParseDefault) // only set name - c.SetData(map[string]interface{}{ + c.SetData(map[string]any{ "name": "inhere", }) @@ -402,7 +402,7 @@ NEW: 支持通过结构标签 `default` 解析并设置默认值 ### 载入配置 -- `LoadData(dataSource ...interface{}) (err error)` 从struct或map加载数据 +- `LoadData(dataSource ...any) (err error)` 从struct或map加载数据 - `LoadFlags(keys []string) (err error)` 从命令行参数载入数据 - `LoadOSEnvs(nameToKeyMap map[string]string)` 从ENV载入数据 - `LoadExists(sourceFiles ...string) (err error)` 从存在的配置文件里加载数据,会忽略不存在的文件 @@ -427,25 +427,25 @@ NEW: 支持通过结构标签 `default` 解析并设置默认值 - `Strings(key string) (arr []string)` - `StringMap(key string) (mp map[string]string)` - `SubDataMap(key string) maputi.Data` -- `Get(key string, findByPath ...bool) (value interface{})` +- `Get(key string, findByPath ...bool) (value any)` **将数据映射到结构体:** -- `BindStruct(key string, dst interface{}) error` -- `MapOnExists(key string, dst interface{}) error` +- `BindStruct(key string, dst any) error` +- `MapOnExists(key string, dst any) error` ### 设置值 -- `Set(key string, val interface{}, setByPath ...bool) (err error)` +- `Set(key string, val any, setByPath ...bool) (err error)` ### 有用的方法 - `Getenv(name string, defVal ...string) (val string)` - `AddDriver(driver Driver)` -- `Data() map[string]interface{}` +- `Data() map[string]any` - `Exists(key string, findByPath ...bool) bool` - `DumpTo(out io.Writer, format string) (n int64, err error)` -- `SetData(data map[string]interface{})` 设置数据以覆盖 `Config.Data` +- `SetData(data map[string]any)` 设置数据以覆盖 `Config.Data` ## 单元测试 @@ -484,6 +484,9 @@ go test -cover ./... - Toml 解析 [go toml](https://github.com/BurntSushi/toml) - 数据合并 [mergo](https://github.com/imdario/mergo) - 映射数据到结构体 [mapstructure](https://github.com/mitchellh/mapstructure) +- JSON5 解析 + - [yosuke-furukawa/json5](https://github.com/yosuke-furukawa/json5) + - [titanous/json5](https://github.com/titanous/json5) ## License diff --git a/vendor/github.com/gookit/config/v2/any.go b/vendor/github.com/gookit/config/v2/any.go deleted file mode 100644 index 37237165c..000000000 --- a/vendor/github.com/gookit/config/v2/any.go +++ /dev/null @@ -1,6 +0,0 @@ -package config - -// alias of interface{} -// -// TIP: cannot add `go:build !go1.18` in file head, that require the go.mod set `go 1.18` -type any = interface{} diff --git a/vendor/github.com/gookit/config/v2/config.go b/vendor/github.com/gookit/config/v2/config.go index ea7544857..4c708c93a 100644 --- a/vendor/github.com/gookit/config/v2/config.go +++ b/vendor/github.com/gookit/config/v2/config.go @@ -90,13 +90,15 @@ type Config struct { loadedUrls []string loadedFiles []string driverNames []string - reloading bool + // driver alias to name map. + aliasMap map[string]string + reloading bool // TODO Deprecated decoder and encoder, use driver instead // drivers map[string]Driver - // decoders["toml"] = func(blob []byte, v interface{}) (err error){} - // decoders["yaml"] = func(blob []byte, v interface{}) (err error){} + // decoders["toml"] = func(blob []byte, v any) (err error){} + // decoders["yaml"] = func(blob []byte, v any) (err error){} decoders map[string]Decoder encoders map[string]Encoder @@ -109,17 +111,9 @@ type Config struct { sMapCache map[string]strMap } -// New config instance -func New(name string) *Config { - return &Config{ - name: name, - opts: newDefaultOption(), - data: make(map[string]any), - - // default add JSON driver - encoders: map[string]Encoder{JSON: JSONEncoder}, - decoders: map[string]Decoder{JSON: JSONDecoder}, - } +// New config instance, default add JSON driver +func New(name string, opts ...OptionFn) *Config { + return NewEmpty(name).WithDriver(JSONDriver).WithOptions(opts...) } // NewEmpty config instance @@ -132,6 +126,7 @@ func NewEmpty(name string) *Config { // don't add any drivers encoders: map[string]Encoder{}, decoders: map[string]Decoder{}, + aliasMap: make(map[string]string), } } @@ -140,15 +135,13 @@ func NewWith(name string, fn func(c *Config)) *Config { return New(name).With(fn) } -// NewWithOptions config instance -func NewWithOptions(name string, opts ...func(opts *Options)) *Config { +// NewWithOptions config instance. alias of New() +func NewWithOptions(name string, opts ...OptionFn) *Config { return New(name).WithOptions(opts...) } // Default get the default instance -func Default() *Config { - return dc -} +func Default() *Config { return dc } /************************************************************* * config drivers @@ -171,6 +164,11 @@ func AddDriver(driver Driver) { dc.AddDriver(driver) } // AddDriver set a decoder and encoder driver for a format. func (c *Config) AddDriver(driver Driver) { format := driver.Name() + if len(driver.Aliases()) > 0 { + for _, alias := range driver.Aliases() { + c.aliasMap[alias] = format + } + } c.driverNames = append(c.driverNames, format) c.decoders[format] = driver.GetDecoder() @@ -179,21 +177,21 @@ func (c *Config) AddDriver(driver Driver) { // HasDecoder has decoder func (c *Config) HasDecoder(format string) bool { - format = fixFormat(format) + format = c.resolveFormat(format) _, ok := c.decoders[format] return ok } // HasEncoder has encoder func (c *Config) HasEncoder(format string) bool { - format = fixFormat(format) + format = c.resolveFormat(format) _, ok := c.encoders[format] return ok } // DelDriver delete driver of the format func (c *Config) DelDriver(format string) { - format = fixFormat(format) + format = c.resolveFormat(format) delete(c.decoders, format) delete(c.encoders, format) } @@ -205,6 +203,21 @@ func (c *Config) DelDriver(format string) { // Name get config name func (c *Config) Name() string { return c.name } +// AddAlias add alias for a format(driver name) +func AddAlias(format, alias string) { dc.AddAlias(format, alias) } + +// AddAlias add alias for a format(driver name) +// +// Example: +// +// config.AddAlias("ini", "conf") +func (c *Config) AddAlias(format, alias string) { + c.aliasMap[alias] = format +} + +// AliasMap get alias map +func (c *Config) AliasMap() map[string]string { return c.aliasMap } + // Error get last error, will clear after read. func (c *Config) Error() error { err := c.err @@ -237,8 +250,8 @@ func (c *Config) ClearAll() { c.ClearData() c.ClearCaches() - c.loadedUrls = []string{} - c.loadedFiles = []string{} + c.aliasMap = make(map[string]string) + // options c.opts.Readonly = false } @@ -246,7 +259,7 @@ func (c *Config) ClearAll() { func (c *Config) ClearData() { c.fireHook(OnCleanData) - c.data = make(map[string]interface{}) + c.data = make(map[string]any) c.loadedUrls = []string{} c.loadedFiles = []string{} } diff --git a/vendor/github.com/gookit/config/v2/driver.go b/vendor/github.com/gookit/config/v2/driver.go index 502b7520c..2776b569c 100644 --- a/vendor/github.com/gookit/config/v2/driver.go +++ b/vendor/github.com/gookit/config/v2/driver.go @@ -1,6 +1,5 @@ package config -// default json driver(encoder/decoder) import ( "encoding/json" @@ -11,10 +10,19 @@ import ( // TODO refactor: rename GetDecoder() to Decode(), rename GetEncoder() to Encode() type Driver interface { Name() string + Aliases() []string // alias format names, use for resolve format name GetDecoder() Decoder GetEncoder() Encoder } +// DriverV2 interface. +type DriverV2 interface { + Name() string // driver name, also is format name. + Aliases() []string // alias format names, use for resolve format name + Decode(blob []byte, v any) (err error) + Encode(v any) (out []byte, err error) +} + // Decoder for decode yml,json,toml format content type Decoder func(blob []byte, v any) (err error) @@ -24,6 +32,7 @@ type Encoder func(v any) (out []byte, err error) // StdDriver struct type StdDriver struct { name string + aliases []string decoder Decoder encoder Encoder } @@ -33,9 +42,24 @@ func NewDriver(name string, dec Decoder, enc Encoder) *StdDriver { return &StdDriver{name: name, decoder: dec, encoder: enc} } +// WithAliases set aliases for driver +func (d *StdDriver) WithAliases(aliases ...string) *StdDriver { + d.aliases = aliases + return d +} + +// WithAlias add alias for driver +func (d *StdDriver) WithAlias(alias string) *StdDriver { + d.aliases = append(d.aliases, alias) + return d +} + // Name of driver -func (d *StdDriver) Name() string { - return d.name +func (d *StdDriver) Name() string { return d.name } + +// Aliases format name of driver +func (d *StdDriver) Aliases() []string { + return d.aliases } // Decode of driver @@ -59,13 +83,11 @@ func (d *StdDriver) GetEncoder() Encoder { } /************************************************************* - * json driver + * JSON driver *************************************************************/ var ( // JSONAllowComments support write comments on json file. - // - // Deprecated: please use JSONDriver.ClearComments = true JSONAllowComments = true // JSONMarshalIndent if not empty, will use json.MarshalIndent for encode data. @@ -107,6 +129,11 @@ func (d *jsonDriver) Name() string { return d.driverName } +// Aliases of the driver +func (d *jsonDriver) Aliases() []string { + return nil +} + // Decode for the driver func (d *jsonDriver) Decode(data []byte, v any) error { if d.ClearComments { diff --git a/vendor/github.com/gookit/config/v2/export.go b/vendor/github.com/gookit/config/v2/export.go index 85187dcba..3371d127a 100644 --- a/vendor/github.com/gookit/config/v2/export.go +++ b/vendor/github.com/gookit/config/v2/export.go @@ -55,6 +55,9 @@ func MapOnExists(key string, dst any) error { } // MapOnExists mapping data to the dst structure only on key exists. +// +// - Support ParseEnv on mapping +// - Support ParseDefault on mapping func (c *Config) MapOnExists(key string, dst any) error { err := c.Structure(key, dst) if err != nil && err == ErrNotFound { @@ -66,12 +69,15 @@ func (c *Config) MapOnExists(key string, dst any) error { // Structure get config data and binding to the dst structure. // +// - Support ParseEnv on mapping +// - Support ParseDefault on mapping +// // Usage: // // dbInfo := Db{} // config.Structure("db", &dbInfo) func (c *Config) Structure(key string, dst any) error { - var data interface{} + var data any // binding all data if key == "" { data = c.data @@ -133,7 +139,7 @@ func (c *Config) DumpTo(out io.Writer, format string) (n int64, err error) { var ok bool var encoder Encoder - format = fixFormat(format) + format = c.resolveFormat(format) if encoder, ok = c.encoders[format]; !ok { err = errors.New("not exists/register encoder for the format: " + format) return diff --git a/vendor/github.com/gookit/config/v2/load.go b/vendor/github.com/gookit/config/v2/load.go index 36ef5adc5..38becdf8c 100644 --- a/vendor/github.com/gookit/config/v2/load.go +++ b/vendor/github.com/gookit/config/v2/load.go @@ -4,13 +4,15 @@ import ( "errors" "flag" "fmt" - "io/ioutil" + "io" + "io/fs" "net/http" "os" "path/filepath" "strings" "time" + "github.com/gookit/goutil/errorx" "github.com/gookit/goutil/fsutil" "github.com/imdario/mergo" ) @@ -42,6 +44,10 @@ func LoadExists(sourceFiles ...string) error { return dc.LoadExists(sourceFiles. // LoadExists load and parse config files, but will ignore not exists file. func (c *Config) LoadExists(sourceFiles ...string) (err error) { for _, file := range sourceFiles { + if file == "" { + continue + } + if err = c.loadFile(file, true, ""); err != nil { return } @@ -72,7 +78,7 @@ func (c *Config) LoadRemote(format, url string) (err error) { } // read response content - bts, err := ioutil.ReadAll(resp.Body) + bts, err := io.ReadAll(resp.Body) if err == nil { if err = c.parseSourceCode(format, bts); err != nil { return @@ -191,15 +197,21 @@ func LoadData(dataSource ...any) error { return dc.LoadData(dataSource...) } // // The dataSources can be: // - map[string]any +// - map[string]string func (c *Config) LoadData(dataSources ...any) (err error) { if c.opts.Delimiter == 0 { c.opts.Delimiter = defaultDelimiter } for _, ds := range dataSources { + if smp, ok := ds.(map[string]string); ok { + c.LoadSMap(smp) + continue + } + err = mergo.Merge(&c.data, ds, mergo.WithOverride) if err != nil { - return + return errorx.WithStack(err) } } @@ -207,6 +219,14 @@ func (c *Config) LoadData(dataSources ...any) (err error) { return } +// LoadSMap to config +func (c *Config) LoadSMap(smp map[string]string) { + for k, v := range smp { + c.data[k] = v + } + c.fireHook(OnLoadData) +} + // LoadSources load one or multi byte data func LoadSources(format string, src []byte, more ...[]byte) error { return dc.LoadSources(format, src, more...) @@ -314,9 +334,8 @@ func (c *Config) LoadFromDir(dirPath, format string) (err error) { extName := "." + format extLen := len(extName) - return fsutil.FindInDir(dirPath, func(fPath string, fi os.FileInfo) error { - baseName := fi.Name() - + return fsutil.FindInDir(dirPath, func(fPath string, ent fs.DirEntry) error { + baseName := ent.Name() if strings.HasSuffix(baseName, extName) { data, err := c.parseSourceToMap(format, fsutil.MustReadFile(fPath)) if err != nil { @@ -386,7 +405,7 @@ func (c *Config) loadFile(file string, loadExist bool, format string) (err error defer fd.Close() // read file content - bts, err := ioutil.ReadAll(fd) + bts, err := io.ReadAll(fd) if err == nil { // get format for file ext if format == "" { @@ -431,8 +450,8 @@ func (c *Config) loadDataMap(data map[string]any) (err error) { } // parse config source code to Config. -func (c *Config) parseSourceToMap(format string, blob []byte) (map[string]interface{}, error) { - format = fixFormat(format) +func (c *Config) parseSourceToMap(format string, blob []byte) (map[string]any, error) { + format = c.resolveFormat(format) decode := c.decoders[format] if decode == nil { return nil, errors.New("not register decoder for the format: " + format) diff --git a/vendor/github.com/gookit/config/v2/options.go b/vendor/github.com/gookit/config/v2/options.go index 49916625e..595468f85 100644 --- a/vendor/github.com/gookit/config/v2/options.go +++ b/vendor/github.com/gookit/config/v2/options.go @@ -50,6 +50,9 @@ type Options struct { // WatchChange bool } +// OptionFn option func +type OptionFn func(*Options) + func newDefaultOption() *Options { return &Options{ ParseKey: true, @@ -159,10 +162,10 @@ func WithHookFunc(fn HookFunc) func(*Options) { func EnableCache(opts *Options) { opts.EnableCache = true } // WithOptions with options -func WithOptions(opts ...func(*Options)) { dc.WithOptions(opts...) } +func WithOptions(opts ...OptionFn) { dc.WithOptions(opts...) } // WithOptions apply some options -func (c *Config) WithOptions(opts ...func(opts *Options)) *Config { +func (c *Config) WithOptions(opts ...OptionFn) *Config { if !c.IsEmpty() { panic("config: Cannot set options after data has been loaded") } diff --git a/vendor/github.com/gookit/config/v2/read.go b/vendor/github.com/gookit/config/v2/read.go index 6bb79da97..246a2943c 100644 --- a/vendor/github.com/gookit/config/v2/read.go +++ b/vendor/github.com/gookit/config/v2/read.go @@ -90,32 +90,46 @@ func (c *Config) Exists(key string, findByPath ...bool) (ok bool) { *************************************************************/ // Data return all config data -func Data() map[string]interface{} { return dc.Data() } +func Data() map[string]any { return dc.Data() } -// Data get all config data -func (c *Config) Data() map[string]interface{} { +// Data get all config data. +// +// Note: will don't apply any options, like ParseEnv +func (c *Config) Data() map[string]any { return c.data } +// Keys return all config data +func Keys() []string { return dc.Keys() } + +// Keys get all config data +func (c *Config) Keys() []string { + keys := make([]string, 0, len(c.data)) + for key := range c.data { + keys = append(keys, key) + } + return keys +} + // Get config value by key string, support get sub-value by key path(eg. 'map.key'), // // - ok is true, find value from config // - ok is false, not found or error -func Get(key string, findByPath ...bool) interface{} { return dc.Get(key, findByPath...) } +func Get(key string, findByPath ...bool) any { return dc.Get(key, findByPath...) } // Get config value by key -func (c *Config) Get(key string, findByPath ...bool) interface{} { +func (c *Config) Get(key string, findByPath ...bool) any { val, _ := c.GetValue(key, findByPath...) return val } // GetValue get value by given key string. -func GetValue(key string, findByPath ...bool) (interface{}, bool) { +func GetValue(key string, findByPath ...bool) (any, bool) { return dc.GetValue(key, findByPath...) } // GetValue get value by given key string. -func (c *Config) GetValue(key string, findByPath ...bool) (value interface{}, ok bool) { +func (c *Config) GetValue(key string, findByPath ...bool) (value any, ok bool) { sep := c.opts.Delimiter if key = formatKey(key, string(sep)); key == "" { c.addError(ErrKeyIsEmpty) @@ -235,6 +249,18 @@ func (c *Config) String(key string, defVal ...string) string { return value } +// MustString get a string by key, will panic on empty or not exists +func MustString(key string) string { return dc.MustString(key) } + +// MustString get a string by key, will panic on empty or not exists +func (c *Config) MustString(key string) string { + value, ok := c.getString(key) + if !ok { + panic("config: string value not found, key: " + key) + } + return value +} + func (c *Config) getString(key string) (value string, ok bool) { // find from cache if c.opts.EnableCache && len(c.strCache) > 0 { @@ -257,8 +283,11 @@ func (c *Config) getString(key string) (value string, ok bool) { value = envutil.ParseEnvValue(value) } default: - // value = fmt.Sprintf("%v", val) - value, _ = strutil.AnyToString(val, false) + var err error + value, err = strutil.AnyToString(val, false) + if err != nil { + return "", false + } } // add cache @@ -520,6 +549,17 @@ func (c *Config) Strings(key string) (arr []string) { return } +// StringsBySplit get []string by split a string value. +func StringsBySplit(key, sep string) []string { return dc.StringsBySplit(key, sep) } + +// StringsBySplit get []string by split a string value. +func (c *Config) StringsBySplit(key, sep string) (ss []string) { + if str, ok := c.getString(key); ok { + ss = strutil.Split(str, sep) + } + return +} + // StringMap get config data as a map[string]string func StringMap(key string) map[string]string { return dc.StringMap(key) } diff --git a/vendor/github.com/gookit/config/v2/util.go b/vendor/github.com/gookit/config/v2/util.go index f42789314..bfb597d39 100644 --- a/vendor/github.com/gookit/config/v2/util.go +++ b/vendor/github.com/gookit/config/v2/util.go @@ -13,12 +13,15 @@ import ( // ValDecodeHookFunc returns a mapstructure.DecodeHookFunc // that parse ENV var, and more custom parse func ValDecodeHookFunc(parseEnv, parseTime bool) mapstructure.DecodeHookFunc { - return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + return func(f reflect.Type, t reflect.Type, data any) (any, error) { if f.Kind() != reflect.String { return data, nil } str := data.(string) + if parseEnv { + str = envutil.ParseEnvValue(str) + } if len(str) < 2 { return str, nil } @@ -32,14 +35,19 @@ func ValDecodeHookFunc(parseEnv, parseTime bool) mapstructure.DecodeHookFunc { return dur, nil } } - } else if parseEnv { // parse ENV value - str = envutil.ParseEnvValue(str) } - return str, nil } } +// resolve format, check is alias +func (c *Config) resolveFormat(f string) string { + if name, ok := c.aliasMap[f]; ok { + return name + } + return f +} + /************************************************************* * Deprecated methods *************************************************************/ @@ -55,7 +63,7 @@ func SetDecoder(format string, decoder Decoder) { // // Deprecated: please use driver instead func (c *Config) SetDecoder(format string, decoder Decoder) { - format = fixFormat(format) + format = c.resolveFormat(format) c.decoders[format] = decoder } @@ -79,7 +87,7 @@ func SetEncoder(format string, encoder Encoder) { // // Deprecated: please use driver instead func (c *Config) SetEncoder(format string, encoder Encoder) { - format = fixFormat(format) + format = c.resolveFormat(format) c.encoders[format] = encoder } @@ -141,20 +149,3 @@ func parseVarNameAndType(key string) (string, string) { func formatKey(key, sep string) string { return strings.Trim(strings.TrimSpace(key), sep) } - -// resolve fix inc/conf/yaml format -func fixFormat(f string) string { - if f == Yml { - f = Yaml - } - - if f == "inc" { - f = Ini - } - - // eg nginx config file. - if f == "conf" { - f = Hcl - } - return f -} diff --git a/vendor/github.com/gookit/config/v2/yaml/yaml.go b/vendor/github.com/gookit/config/v2/yaml/yaml.go index 8d630a88c..f479d6acf 100644 --- a/vendor/github.com/gookit/config/v2/yaml/yaml.go +++ b/vendor/github.com/gookit/config/v2/yaml/yaml.go @@ -5,10 +5,9 @@ Usage please see example: */ package yaml -// see https://pkg.go.dev/gopkg.in/yaml.v2 import ( + "github.com/goccy/go-yaml" "github.com/gookit/config/v2" - "gopkg.in/yaml.v2" ) // Decoder the yaml content decoder @@ -18,5 +17,4 @@ var Decoder config.Decoder = yaml.Unmarshal var Encoder config.Encoder = yaml.Marshal // Driver for yaml -// TIP: recommended use the yamlv3.Driver -var Driver = config.NewDriver(config.Yaml, Decoder, Encoder) +var Driver = config.NewDriver(config.Yaml, Decoder, Encoder).WithAliases(config.Yml) diff --git a/vendor/github.com/gookit/goutil/Makefile b/vendor/github.com/gookit/goutil/Makefile index f902a5f18..19b591e34 100644 --- a/vendor/github.com/gookit/goutil/Makefile +++ b/vendor/github.com/gookit/goutil/Makefile @@ -29,7 +29,7 @@ readme: 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" + git commit -m ":memo: doc: update and re-generate README docs" csfix: ## Fix code style for all files by go fmt csfix: diff --git a/vendor/github.com/gookit/goutil/README.md b/vendor/github.com/gookit/goutil/README.md index 565f799ef..3fcc50276 100644 --- a/vendor/github.com/gookit/goutil/README.md +++ b/vendor/github.com/gookit/goutil/README.md @@ -7,7 +7,7 @@ [![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 package for the Go: int, string, array/slice, map, error, time, format, CLI, ENV, filesystem, system, testing and more. +💪 Useful utils(**600+**) package for the Go: int, string, array/slice, map, error, time, format, CLI, ENV, filesystem, system, testing and more. > **[中文说明](README.zh-CN.md)** @@ -27,7 +27,6 @@ - [`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:** @@ -43,6 +42,7 @@ - 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 + - [process](./sysutil/process) Provide some process handle util functions. - [`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 @@ -82,54 +82,75 @@ u64Val = goutil.Uint("2") // 2 ## Packages -### Array/Slice +### Array and Slice > Package `github.com/gookit/goutil/arrutil` ```go // source at arrutil/arrutil.go -func Reverse(ss []string) -func StringsRemove(ss []string, s string) []string -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 +func Reverse(ss []string) +func StringsRemove(ss []string, s string) []string +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 +func GetRandomOne[T any](arr []T) T +func RandomOne[T any](arr []T) T +func Unique[T ~string | comdef.XintOrFloat](list []T) []T +func IndexOf[T ~string | comdef.XintOrFloat](val T, list []T) int // 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 any) bool -func Contains(arr, val any) bool -func NotContains(arr, val any) bool +func IntsHas(ints []int, val int) bool +func Int64sHas(ints []int64, val int64) bool +func InStrings(elem string, ss []string) bool +func StringsHas(ss []string, val string) bool +func NotIn[T comdef.ScalarType](value T, list []T) bool +func In[T comdef.ScalarType](value T, list []T) bool +func ContainsAll[T comdef.ScalarType](list, values []T) bool +func IsSubList[T comdef.ScalarType](values, list []T) bool +func IsParent[T comdef.ScalarType](values, list []T) 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 +func TwowaySearch(data any, item any, fn Comparer) (int, error) +func MakeEmptySlice(itemType reflect.Type) any +func CloneSlice(data any) any +func Differences[T any](first, second []T, fn Comparer) []T +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/collection_gte118.go +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 // 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 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 +func JoinStrings(sep string, ss ...string) string +func StringsJoin(sep string, ss ...string) string +func JoinSlice(sep string, arr ...any) string +func ToInt64s(arr any) (ret []int64, err error) +func MustToInt64s(arr any) []int64 +func SliceToInt64s(arr []any) []int64 +func StringsAsInts(ss []string) []int +func StringsToInts(ss []string) (ints []int, err error) +func StringsTryInts(ss []string) (ints []int, err error) +func AnyToSlice(sl any) (ls []any, err error) +func AnyToStrings(arr any) []string +func MustToStrings(arr any) []string +func StringsToSlice(ss []string) []any +func ToStrings(arr any) (ret []string, err error) +func SliceToStrings(arr []any) []string +func QuietStrings(arr []any) []string +func ConvType[T any, R any](arr []T, newElemTyp R) ([]R, error) +func AnyToString(arr any) string +func SliceToString(arr ...any) string +func ToString(arr []any) string +func CombineToMap[K comdef.SortedType, V any](keys []K, values []V) map[K]V +func CombineToSMap(keys, values []string) map[string]string // source at arrutil/format.go -func NewFormatter(arr any) *ArrFormatter -func FormatIndent(arr any, indent string) string +func NewFormatter(arr any) *ArrFormatter +func FormatIndent(arr any, indent string) string ``` #### ArrUtil Usage @@ -153,32 +174,83 @@ ss, err := arrutil.ToStrings([]int{1, 2}) // ss: []string{"1", "2"} ``` +### Basefn + +> Package `github.com/gookit/goutil/basefn` + +```go +// source at basefn/basefunc.go +func Panicf(format string, v ...any) +func MustOK(err error) +func Must[T any](v T, err error) T +func ErrOnFail(cond bool, err error) error +func OrError(cond bool, err error) error +func FirstOr[T any](sl []T, elseVal T) T +func OrValue[T any](cond bool, okVal, elVal T) T +func OrReturn[T any](cond bool, okFn, elseFn func() T) T +func CallOn(cond bool, fn ErrFunc) error +func CallOrElse(cond bool, okFn, elseFn ErrFunc) error +// source at basefn/extfunc.go +func DataSize(size uint64) string +func HowLongAgo(sec int64) string +``` + +### Bytes Utils + +> Package `github.com/gookit/goutil/byteutil` + +```go +// source at byteutil/buffer.go +func NewBuffer() *Buffer +// source at byteutil/byteutil.go +func Random(length int) ([]byte, error) +func FirstLine(bs []byte) []byte +func StrOrErr(bs []byte, err error) (string, error) +func SafeString(bs []byte, err error) string +func String(b []byte) string +func ToString(b []byte) string +func AppendAny(dst []byte, v any) []byte +func Cut(bs []byte, sep byte) (before, after []byte, found bool) +// source at byteutil/bytex.go +func Md5(src any) []byte +// source at byteutil/check.go +func IsNumChar(c byte) bool +// source at byteutil/encoder.go +func NewStdEncoder(encFn func(src []byte) []byte, decFn func(src []byte) ([]byte, error)) *StdEncoder +// source at byteutil/pool.go +func NewChanPool(maxSize int, width int, capWidth int) *ChanPool +``` + ### 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 +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) +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/ext.go +func NewEnumString(enum ...string) EnumString +func NewKVString() KVString // source at cflag/optarg.go -func NewArg(name, desc string, required bool) *FlagArg +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 +func IsGoodName(name string) bool +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 @@ -191,71 +263,72 @@ func ReplaceShorts(args []string, shortsMap map[string]string) []string ```go // source at cliutil/cliutil.go -func LineBuild(binFile string, args []string) string -func BuildLine(binFile string, args []string) string -func String2OSArgs(line string) []string -func StringToOSArgs(line string) []string -func ParseLine(line string) []string -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 ExecCommand(binName string, args []string, workDir ...string) (string, error) -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 +func LineBuild(binFile string, args []string) string +func BuildLine(binFile string, args []string) string +func String2OSArgs(line string) []string +func StringToOSArgs(line string) []string +func ParseLine(line string) []string +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 ExecCommand(binName string, args []string, workDir ...string) (string, error) +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 // 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...) } +func Redp(a ...any) +func Redf(format string, a ...any) +func Redln(a ...any) +func Bluep(a ...any) +func Bluef(format string, a ...any) +func Blueln(a ...any) +func Cyanp(a ...any) +func Cyanf(format string, a ...any) +func Cyanln(a ...any) +func Grayp(a ...any) +func Grayf(format string, a ...any) +func Grayln(a ...any) +func Greenp(a ...any) +func Greenf(format string, a ...any) +func Greenln(a ...any) +func Yellowp(a ...any) +func Yellowf(format string, a ...any) +func Yellowln(a ...any) +func Magentap(a ...any) +func Magentaf(format string, a ...any) +func Magentaln(a ...any) +func Infop(a ...any) +func Infof(format string, a ...any) +func Infoln(a ...any) +func Successp(a ...any) +func Successf(format string, a ...any) +func Successln(a ...any) +func Errorp(a ...any) +func Errorf(format string, a ...any) +func Errorln(a ...any) +func Warnp(a ...any) +func Warnf(format string, a ...any) +func Warnln(a ...any) // 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) +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) -func ReadPassword(question ...string) string -func InputIsYes(ans string) bool -func ByteIsYes(ans byte) bool +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) +func ReadAsBool(tip string, defVal bool) bool +func ReadPassword(question ...string) string +func Confirm(tip string, defVal ...bool) bool +func InputIsYes(ans string) bool +func ByteIsYes(ans byte) bool ``` #### CLI Util Usage @@ -319,19 +392,30 @@ Build line: ./myapp -a val0 -m "this is message" arg0 ```go // source at dump/dump.go -func Std() *Dumper -func Reset() -func Config(fn func(opts *Options)) -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) +func Std() *Dumper +func Reset() +func Config(fns ...OptionFunc) +func Print(vs ...any) +func Println(vs ...any) +func Fprint(w io.Writer, vs ...any) +func Std2() *Dumper +func Reset2() +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 -func NewDefaultOptions(out io.Writer, skip int) *Options +func NewDumper(out io.Writer, skip int) *Dumper +func NewWithOptions(fns ...OptionFunc) *Dumper +// source at dump/options.go +func NewDefaultOptions(out io.Writer, skip int) *Options +func SkipNilField() OptionFunc +func SkipPrivate() OptionFunc +func BytesAsString() OptionFunc +func WithCallerSkip(skip int) OptionFunc +func WithoutPosition() OptionFunc +func WithoutOutput(out io.Writer) OptionFunc +func WithoutColor() OptionFunc +func WithoutType() OptionFunc ``` #### Examples @@ -383,31 +467,38 @@ Preview: ```go // source at envutil/envutil.go -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) +func VarReplace(s string) string +func VarParse(val string) string +func ParseEnvValue(val string) string +func ParseValue(val string) (newVal string) +func SetEnvMap(mp map[string]string) +func SetEnvs(kvPairs ...string) +func UnsetEnvs(keys ...string) // source at envutil/get.go -func Getenv(name string, def ...string) string -func GetInt(name string, def ...int) int -func GetBool(name string, def ...bool) bool -func Environ() map[string]string +func Getenv(name string, def ...string) string +func GetInt(name string, def ...int) int +func GetBool(name string, def ...bool) bool +func GetMulti(names ...string) map[string]string +func EnvPaths() []string +func EnvMap() map[string]string +func Environ() map[string]string +func SearchEnvKeys(keywords string) map[string]string +func SearchEnv(keywords string, matchValue bool) map[string]string // source at envutil/info.go -func IsWin() bool -func IsWindows() bool -func IsMac() bool -func IsLinux() bool -func IsMSys() bool -func IsWSL() bool -func IsTerminal(fd uintptr) bool -func StdIsTerminal() bool -func IsConsole(out io.Writer) bool -func HasShellEnv(shell string) bool -func IsSupportColor() bool -func IsSupport256Color() bool -func IsSupportTrueColor() bool -func IsGithubActions() bool +func IsWin() bool +func IsWindows() bool +func IsMac() bool +func IsLinux() bool +func IsMSys() bool +func IsWSL() bool +func IsTerminal(fd uintptr) bool +func StdIsTerminal() bool +func IsConsole(out io.Writer) bool +func HasShellEnv(shell string) bool +func IsSupportColor() bool +func IsSupport256Color() bool +func IsSupportTrueColor() bool +func IsGithubActions() bool ``` #### ENV Util Usage @@ -436,41 +527,50 @@ Package errorx provide a enhanced error implements, allow with call stack and wr ```go +// source at errorx/assert.go +func IsTrue(result bool, fmtAndArgs ...any) error +func IsFalse(result bool, fmtAndArgs ...any) error +func IsIn[T comdef.ScalarType](value T, list []T, fmtAndArgs ...any) error +func NotIn[T comdef.ScalarType](value T, list []T, fmtAndArgs ...any) error // source at errorx/errors.go -func NewR(code int, msg string) ErrorR -func Fail(code int, msg string) ErrorR -func Suc(msg string) ErrorR +func NewR(code int, msg string) ErrorR +func Fail(code int, msg string) ErrorR +func Failf(code int, tpl string, v ...any) ErrorR +func Suc(msg string) ErrorR // source at errorx/errorx.go -func New(msg string) 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 ...any) error -func WithPrev(err error, msg string) 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 ...any) error +func New(msg string) 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 ...any) error +func WithPrev(err error, msg string) 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 ...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) +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 ...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 any) bool -func As(err error, target any) bool +func Err(msg string) error +func Raw(msg string) error +func Ef(tpl string, vars ...any) error +func Errf(tpl string, vars ...any) error +func Rawf(tpl string, vars ...any) error +func Cause(err error) error +func Unwrap(err error) error +func Previous(err error) error +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 any) bool +func As(err error, target any) bool ``` #### Errorx Usage @@ -549,21 +649,23 @@ runtime.goexit() ``` -### Formatting +### Format Utils > Package `github.com/gookit/goutil/fmtutil` ```go +// source at fmtutil/fmtutil.go +func StringOrJSON(v any) ([]byte, error) // source at fmtutil/format.go -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 []any) (message string) +func DataSize(size uint64) string +func SizeToString(size uint64) string +func StringToByte(sizeStr string) uint64 +func ParseByte(sizeStr string) uint64 +func PrettyJSON(v any) (string, error) +func StringsToInts(ss []string) (ints []int, err error) +func ArgsWithSpaces(vs []any) (message string) // source at fmtutil/time.go -func HowLongAgo(sec int64) string +func HowLongAgo(sec int64) string ``` ### File System @@ -572,57 +674,93 @@ func HowLongAgo(sec int64) string ```go // source at fsutil/check.go -func PathExists(path string) bool -func IsDir(path string) bool -func FileExists(path string) bool -func IsFile(path string) bool -func IsAbsPath(aPath string) bool -func IsImageFile(path string) bool -func IsZipFile(filepath string) bool +func PathExists(path string) bool +func IsDir(path string) bool +func FileExists(path string) bool +func IsFile(path string) bool +func IsAbsPath(aPath string) bool +func IsImageFile(path string) bool +func IsZipFile(filepath string) bool +func PathMatch(pattern, s string) bool +// source at fsutil/find.go +func SearchNameUp(dirPath, name string) string +func SearchNameUpx(dirPath, name string) (string, bool) +func WalkDir(dir string, fn fs.WalkDirFunc) error +func GlobWithFunc(pattern string, fn func(filePath string) error) (err error) +func OnlyFindDir(_ string, ent fs.DirEntry) bool +func OnlyFindFile(_ string, ent fs.DirEntry) bool +func IncludeSuffix(ss ...string) FilterFunc +func ExcludeDotFile(_ string, ent fs.DirEntry) bool +func ExcludeSuffix(ss ...string) FilterFunc +func ApplyFilters(fPath string, ent fs.DirEntry, filters []FilterFunc) bool +func FindInDir(dir string, handleFn HandleFunc, filters ...FilterFunc) (e error) // source at fsutil/fsutil.go -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 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 JoinPaths(elem ...string) string +func JoinSubPaths(basePath string, elem ...string) string +func SlashPath(path string) string +func UnixPath(path string) string +func ToAbsPath(p string) string // 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 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) +func Dir(fpath string) string +func PathName(fpath string) string +func Name(fpath string) string +func FileExt(fpath string) string +func Extname(fpath string) string +func Suffix(fpath string) string +func Expand(pathStr string) string +func ExpandPath(pathStr string) string +func ResolvePath(pathStr string) string +func SplitPath(pathStr string) (dir, name string) +// source at fsutil/info_nonwin.go +func Realpath(pathStr string) string // 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, 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 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) +func Mkdir(dirPath string, perm os.FileMode) error +func MkDirs(perm os.FileMode, dirPaths ...string) error +func MkSubDirs(perm os.FileMode, parentDir string, subDirs ...string) error +func MkParentDir(fpath string) error +func OpenFile(filepath string, flag int, perm os.FileMode) (*os.File, error) +func MustOpenFile(filepath string, flag int, perm os.FileMode) *os.File +func QuickOpenFile(filepath string, fileFlag ...int) (*os.File, error) +func OpenAppendFile(filepath string, filePerm ...os.FileMode) (*os.File, error) +func OpenTruncFile(filepath string, filePerm ...os.FileMode) (*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 Remove(fPath string) error +func MustRemove(fPath string) +func QuietRemove(fPath string) +func RmIfExist(fPath string) error +func DeleteIfExist(fPath string) error +func RmFileIfExist(fPath string) error +func DeleteIfFileExist(fPath string) error +func Unzip(archive, targetDir string) (err error) +// source at fsutil/opread.go +func NewIOReader(in any) (r io.Reader, err error) +func DiscardReader(src io.Reader) +func ReadFile(filePath string) []byte +func MustReadFile(filePath string) []byte +func ReadReader(r io.Reader) []byte +func MustReadReader(r io.Reader) []byte +func ReadString(in any) string +func ReadStringOrErr(in any) (string, error) +func ReadAll(in any) []byte +func GetContents(in any) []byte +func ReadOrErr(in any) ([]byte, error) +func ReadExistFile(filePath string) []byte +func TextScanner(in any) *scanner.Scanner +func LineScanner(in any) *bufio.Scanner +// source at fsutil/opwrite.go +func PutContents(filePath string, data any, fileFlag ...int) (int, error) +func WriteFile(filePath string, data any, perm os.FileMode, fileFlag ...int) error +func WriteOSFile(f *os.File, data any) (n int, err error) +func CopyFile(srcPath, dstPath string) error +func MustCopyFile(srcPath, dstPath string) ``` #### FsUtil Usage @@ -634,30 +772,23 @@ package main import ( "fmt" - "os" + "io/fs" - "github.com/gookit/goutil/fsutil/finder" + "github.com/gookit/goutil/fsutil" ) func main() { - f := finder.EmptyFinder() + // find all files in dir + fsutil.FindInDir("./", func(filePath string, de fs.DirEntry) error { + fmt.Println(filePath) + return nil + }) - f. - AddDir("./testdata"). - AddFile("finder.go"). - NoDotFile(). - // NoDotDir(). - Find(). - Each(func(filePath string) { - fmt.Println(filePath) - }) - - finder.NewFinder([]string{"./testdata"}). - AddFile("finder.go"). - NoDotDir(). - EachStat(func(fi os.FileInfo, filePath string) { - fmt.Println(filePath, "=>", fi.ModTime()) - }) + // find files with filters + fsutil.FindInDir("./", func(filePath string, de fs.DirEntry) error { + fmt.Println(filePath) + return nil + }, fsutil.ExcludeDotFile) } ``` @@ -668,18 +799,21 @@ func main() { ```go // source at jsonutil/jsonutil.go -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 +func WriteFile(filePath string, data any) error +func WritePretty(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 IsJSON(s string) bool +func IsJSONFast(s string) bool +func StripComments(src string) string ``` ### Map @@ -688,33 +822,41 @@ func StripComments(src string) string ```go // source at maputil/check.go -func HasKey(mp, key any) (ok bool) -func HasAllKeys(mp any, keys ...any) (ok bool, noKey any) +func HasKey(mp, key any) (ok bool) +func HasOneKey(mp any, keys ...any) (ok bool, key any) +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]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) +func KeyToLower(src map[string]string) map[string]string +func ToStringMap(src map[string]any) map[string]string +func CombineToSMap(keys, values []string) SMap +func CombineToMap[K comdef.SortedType, V any](keys []K, values []V) map[K]V +func ToAnyMap(mp any) map[string]any +func TryAnyMap(mp any) (map[string]any, error) +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 +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) +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) +func EachAnyMap(mp any, fn func(key string, val 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 MakeByPath(path string, val any) (mp map[string]any) -func MakeByKeys(keys []string, val any) (mp map[string]any) +func SimpleMerge(src, dst map[string]any) map[string]any +func DeepMerge(src, dst map[string]any, deep int) map[string]any +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 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) +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 @@ -723,57 +865,68 @@ func SetByKeys(mp *map[string]any, keys []string, val any) (err error) ```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) +func Compare(srcVal, dstVal any, op string) (ok bool) +func CompInt[T comdef.Xint](srcVal, dstVal T, op string) (ok bool) +func CompInt64(srcVal, dstVal int64, op string) bool +func CompFloat[T comdef.Float](srcVal, dstVal T, op string) (ok bool) +func CompValue[T comdef.XintOrFloat](srcVal, dstVal T, op string) (ok bool) +func InRange[T comdef.IntOrFloat](val, min, max T) bool +func OutRange[T comdef.IntOrFloat](val, min, max T) bool +func InUintRange[T comdef.Uint](val, min, max T) bool // source at mathutil/convert.go -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) +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 SafeInt64(in any) int64 +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) +func Min[T comdef.XintOrFloat](x, y T) T +func Max[T comdef.XintOrFloat](x, y T) T +func SwapMin[T comdef.XintOrFloat](x, y T) (T, T) +func SwapMax[T comdef.XintOrFloat](x, y T) (T, T) +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) +func MaxFloat(x, y float64) float64 +func OrElse[T comdef.XintOrFloat](in, nv T) T // source at mathutil/number.go -func IsNumeric(c byte) bool -func Percent(val, total int) float64 -func ElapsedTime(startTime time.Time) string -func DataSize(size uint64) string -func HowLongAgo(sec int64) string +func IsNumeric(c byte) bool +func Percent(val, total int) float64 +func ElapsedTime(startTime time.Time) string +func DataSize(size uint64) string +func HowLongAgo(sec int64) string // source at mathutil/random.go -func RandomInt(min, max int) int -func RandInt(min, max int) int { return RandomInt(min, max) } -func RandIntWithSeed(min, max int, seed int64) int -func RandomIntWithSeed(min, max int, seed int64) int +func RandomInt(min, max int) int +func RandInt(min, max int) int +func RandIntWithSeed(min, max int, seed int64) int +func RandomIntWithSeed(min, max int, seed int64) int ``` ### Reflects @@ -782,33 +935,45 @@ func RandomIntWithSeed(min, max int, seed int64) int ```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 +func HasChild(v reflect.Value) bool +func IsArrayOrSlice(k reflect.Kind) bool +func IsSimpleKind(k reflect.Kind) bool +func IsAnyInt(k reflect.Kind) bool +func IsIntx(k reflect.Kind) bool +func IsUintX(k reflect.Kind) 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) +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 ConvSlice(oldSlRv reflect.Value, newElemTyp reflect.Type) (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 +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) +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 SliceElemKind(typ reflect.Type) reflect.Kind +func UnexportedValue(rv reflect.Value) any +func SetUnexportedValue(rv reflect.Value, value any) +func SetValue(rv reflect.Value, val any) error +func SetRValue(rv, val reflect.Value) +func EachMap(mp reflect.Value, fn func(key, val reflect.Value)) +func EachStrAnyMap(mp reflect.Value, fn func(key string, val any)) +func FlatMap(rv reflect.Value, fn FlatFunc) // source at reflects/value.go -func Wrap(rv reflect.Value) Value -func ValueOf(v any) Value +func Wrap(rv reflect.Value) Value +func ValueOf(v any) Value ``` ### Stdio @@ -817,14 +982,23 @@ func ValueOf(v any) Value ```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 +func QuietFprint(w io.Writer, a ...any) +func QuietFprintf(w io.Writer, tpl string, vs ...any) +func QuietFprintln(w io.Writer, a ...any) +func QuietWriteString(w io.Writer, ss ...string) +// source at stdio/stdio.go +func DiscardReader(src io.Reader) +func ReadString(r io.Reader) string +func MustReadReader(r io.Reader) []byte +func NewIOReader(in any) io.Reader +func NewScanner(in any) *bufio.Scanner +func WriteByte(b byte) +func WriteBytes(bs []byte) +func WritelnBytes(bs []byte) +func WriteString(s string) +func Writeln(s string) // source at stdio/writer.go -func NewWriteWrapper(w io.Writer) *WriteWrapper +func NewWriteWrapper(w io.Writer) *WriteWrapper ``` ### Standard @@ -833,40 +1007,40 @@ func NewWriteWrapper(w io.Writer) *WriteWrapper ```go // 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)) +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 +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/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) +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 any) string -func CutFuncName(fullFcName string) (pkgPath, shortFnName string) -func PkgName(fullFcName string) string +func FuncName(fn any) string +func CutFuncName(fullFcName string) (pkgPath, shortFnName string) +func PkgName(fullFcName string) string // source at stdutil/stack.go -func GetCallStacks(all bool) []byte -func GetCallerInfo(skip int) string -func SimpleCallersInfo(skip, num int) []string -func GetCallersInfo(skip, max int) []string +func GetCallStacks(all bool) []byte +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 ...any) -func GoVersion() string +func DiscardE(_ error) +func PanicIfErr(err error) +func PanicIf(err error) +func Panicf(format string, v ...any) +func GoVersion() string ``` ### Structs @@ -875,28 +1049,49 @@ func GoVersion() string ```go // source at structs/alias.go -func NewAliases(checker func(alias string)) *Aliases +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) +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 ToSMap(st any, optFns ...MapOptFunc) map[string]string +func TryToSMap(st any, optFns ...MapOptFunc) (map[string]string, error) +func MustToSMap(st any, optFns ...MapOptFunc) map[string]string +func ToString(st any, optFns ...MapOptFunc) string +func WithMapTagName(tagName string) MapOptFunc +func MergeAnonymous(opt *MapOptions) +func ExportPrivate(opt *MapOptions) +func StructToMap(st any, optFns ...MapOptFunc) (map[string]any, error) +// source at structs/copy.go +func MapStruct(srcSt, dstSt any) // source at structs/data.go -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 +func NewData() *Data +func NewOrderedMap(len int) *OrderedMap +// source at structs/init.go +func InitStructSlice(opt *InitOptions) +func Init(ptr any, optFns ...InitOptFunc) error +func InitDefaults(ptr any, optFns ...InitOptFunc) error // source at structs/structs.go -func MapStruct(srcSt, dstSt any) +func IsExported(name string) bool +func IsUnexported(name string) bool // source at structs/tags.go -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) +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 ParseTagValueQuick(tagVal string, defines []string) maputil.SMap +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 +func NewValue(val any) *Value +// source at structs/wrapper.go +func Wrap(src any) *Wrapper +func NewWrapper(src any) *Wrapper +func WrapValue(rv reflect.Value) *Wrapper +// source at structs/writer.go +func NewWriter(ptr any) *Wrapper +func WithParseDefault(opt *SetOptions) +func SetValues(ptr any, data map[string]any, optFns ...SetOptFunc) error ``` ### Strings @@ -905,270 +1100,319 @@ func NewValue(val any) *Value ```go // source at strutil/bytes.go -func NewBuffer() *Buffer -func NewByteChanPool(maxSize int, width int, capWidth int) *ByteChanPool +func NewBuffer() *Buffer +func NewByteChanPool(maxSize, width, capWidth int) *ByteChanPool // source at strutil/check.go -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 HasOneSub(s string, subs []string) bool -func HasAllSubs(s string, subs []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 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 +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 IEqual(s1, s2 string) bool +func NoCaseEq(s, t string) bool +func IContains(s, sub string) bool +func ContainsByte(s string, c byte) bool +func ContainsOne(s string, subs []string) bool +func HasOneSub(s string, subs []string) bool +func ContainsAll(s string, subs []string) bool +func HasAllSubs(s string, subs []string) bool +func IsStartsOf(s string, prefixes []string) bool +func HasOnePrefix(s string, prefixes []string) bool +func HasPrefix(s string, prefix string) bool +func IsStartOf(s, prefix string) bool +func HasSuffix(s string, suffix string) bool +func IsEndOf(s, suffix string) bool +func HasOneSuffix(s string, suffixes []string) bool +func IsValidUtf8(s string) bool +func IsSpace(c byte) bool +func IsEmpty(s string) bool +func IsBlank(s string) bool +func IsNotBlank(s string) bool +func IsBlankBytes(bs []byte) bool +func IsSymbol(r rune) bool +func HasEmpty(ss ...string) bool +func IsAllEmpty(ss ...string) bool +func IsVersion(s string) bool +func Compare(s1, s2, op string) bool +func VersionCompare(v1, v2, op string) bool +func SimpleMatch(s string, keywords []string) bool +func QuickMatch(pattern, s string) bool +func PathMatch(pattern, s string) bool +func GlobMatch(pattern, s string) bool +func LikeMatch(pattern, s string) bool +func MatchNodePath(pattern, s string, sep string) bool // source at strutil/convert.go -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) { return ToIntSlice(s, sep...) } -func ToIntSlice(s string, sep ...string) (ints []int, err error) -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) +func Quote(s string) string +func Unquote(s string) string +func Join(sep string, ss ...string) string +func JoinList(sep string, ss []string) string +func JoinAny(sep string, parts ...any) string +func Implode(sep string, ss ...string) string +func String(val any) (string, error) +func ToString(val any) (string, error) +func QuietString(in any) string +func SafeString(in any) string +func MustString(in any) string +func StringOrErr(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 Int2(s string) int +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 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 ToSlice(s string, sep ...string) []string +func ToOSArgs(s string) []string +func ToDuration(s string) (time.Duration, error) +// source at strutil/crypto.go +func Md5(src any) string +func MD5(src any) string +func GenMd5(src any) string +func Md5Bytes(src any) []byte +func HashPasswd(pwd, key string) string +func VerifyPasswd(pwdMAC, pwd, key string) bool // source at strutil/encode.go -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 +func EscapeJS(s string) string +func EscapeHTML(s string) string +func AddSlashes(s string) string +func StripSlashes(s string) string +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/ext.go +func NewComparator(src, dst string) *SimilarComparator +func Similarity(s, t string, rate float32) (float32, bool) // source at strutil/filter.go -func Trim(s string, cutSet ...string) string -func Ltrim(s string, cutSet ...string) string { return TrimLeft(s, cutSet...) } -func LTrim(s string, cutSet ...string) string { return TrimLeft(s, cutSet...) } -func TrimLeft(s string, cutSet ...string) string -func Rtrim(s string, cutSet ...string) string { return TrimRight(s, cutSet...) } -func RTrim(s string, cutSet ...string) string { return TrimRight(s, cutSet...) } -func TrimRight(s string, cutSet ...string) string -func FilterEmail(s string) string +func Trim(s string, cutSet ...string) string +func Ltrim(s string, cutSet ...string) string +func LTrim(s string, cutSet ...string) string +func TrimLeft(s string, cutSet ...string) string +func Rtrim(s string, cutSet ...string) string +func RTrim(s string, cutSet ...string) string +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) } -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 SnakeCase(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 +func Title(s string) string +func Lower(s string) string +func Lowercase(s string) string +func Upper(s string) string +func Uppercase(s string) string +func UpperWord(s string) string +func LowerFirst(s string) string +func UpperFirst(s string) string +func SnakeCase(s string, sep ...string) string +func Camel(s string, sep ...string) string +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 +func MicroTimeID() string +func MicroTimeHexID() string +func DatetimeNo(prefix string) 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 +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 PadChars[T byte | rune](cs []T, pad T, length int, pos PosFlag) []T +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 +func RepeatChars[T byte | rune](char T, times int) []T +// source at strutil/parse.go +func MustToTime(s string, layouts ...string) time.Time +func ToTime(s string, layouts ...string) (t time.Time, err error) +func ParseSizeRange(expr string, opt *ParseSizeOpt) (min, max uint64, err error) +func SafeByteSize(sizeStr string) uint64 +func ToByteSize(sizeStr string) (uint64, error) // source at strutil/random.go -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) +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) +func RandWithTpl(n int, letters string) string // 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) +func RuneIsWord(c rune) bool +func RuneIsLower(c rune) bool +func RuneIsUpper(c rune) bool +func RunePos(s string, ru rune) int +func IsSpaceRune(r rune) bool +func Utf8Len(s string) int +func Utf8len(s string) int +func RuneCount(s string) int +func RuneWidth(r rune) int +func TextWidth(s string) int +func Utf8Width(s string) int +func RunesWidth(rs []rune) (w int) +func Truncate(s string, w int, tail string) string +func TextTruncate(s string, w int, tail string) string +func Utf8Truncate(s string, w int, tail string) string +func TextSplit(s string, w int) []string +func Utf8Split(s string, w int) []string +func TextWrap(s string, w int) string +func WidthWrap(s string, w int) string +func WordWrap(s string, w int) string // 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) } -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) +func Cut(s, sep string) (before string, after string, found bool) +func QuietCut(s, sep string) (before string, after string) +func MustCut(s, sep string) (before string, after string) +func TrimCut(s, sep string) (string, string) +func SplitKV(s, sep string) (string, string) +func SplitValid(s, sep string) (ss []string) +func Split(s, sep string) (ss []string) +func SplitNValid(s, sep string, n int) (ss []string) +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, strict ...bool) (string, string) +func FirstLine(output string) string // source at strutil/strutil.go -func Replaces(str string, pairs map[string]string) 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 +func OrCond(cond bool, s1, s2 string) string +func OrElse(s, orVal string) string +func OrHandle(s string, fn func(s string) string) string +func Valid(ss ...string) string +func Replaces(str string, pairs map[string]string) string +func NewReplacer(pairs map[string]string) *strings.Replacer +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 +func SubstrCount(s string, substr string, params ...uint64) (int, error) ``` -### System +### System Utils > Package `github.com/gookit/goutil/sysutil` ```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 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) // source at sysutil/stack.go -func CallersInfos(skip, num int, filters ...func(file string, fc *runtime.Func) bool) []*CallerInfo +func CallersInfos(skip, num int, filters ...func(file string, fc *runtime.Func) bool) []*CallerInfo // source at sysutil/sysenv.go -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 +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 FindExecutable(binName string) (string, error) +func Executable(binName string) (string, error) +func HasExecutable(binName string) bool +func Getenv(name string, def ...string) string +func Environ() map[string]string +func EnvMapWith(newEnv map[string]string) map[string]string +func EnvPaths() []string +func SearchPath(keywords string, limit int) []string // source at sysutil/sysgo.go -func GoVersion() string -func ParseGoVersion(line string) (*GoInfo, error) -func OsGoInfo() (*GoInfo, error) +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 +func Workdir() string +func BinDir() string +func BinName() string +func BinFile() string +func Open(fileOrUrl string) error +func OpenBrowser(fileOrUrl string) error +func OpenFile(path string) error // 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 +func Kill(pid int, signal syscall.Signal) error +func ProcessExists(pid int) bool +// source at sysutil/sysutil_unix.go +func IsWin() bool +func IsWindows() bool +func IsMac() bool +func IsDarwin() bool +func IsLinux() bool +func OpenURL(URL string) error // source at sysutil/user.go -func MustFindUser(uname string) *user.User -func LoginUser() *user.User -func CurrentUser() *user.User -func UHomeDir() string -func UserHomeDir() string -func HomeDir() string -func UserDir(subPath string) string -func UserCacheDir(subPath string) string -func UserConfigDir(subPath string) string -func ExpandPath(path string) string +func MustFindUser(uname string) *user.User +func LoginUser() *user.User +func CurrentUser() *user.User +func UHomeDir() string +func UserHomeDir() string +func HomeDir() string +func UserDir(subPath string) string +func UserCacheDir(subPath string) string +func UserConfigDir(subPath string) string +func ExpandPath(path string) string +func ExpandHome(path string) string // source at sysutil/user_nonwin.go -func ChangeUserByName(newUname string) (err error) -func ChangeUserUidGid(newUid int, newGid int) (err error) +func ChangeUserByName(newUname string) (err error) +func ChangeUserUidGid(newUid int, newGid int) (err error) ``` -### Testing +### Testing Utils > Package `github.com/gookit/goutil/testutil` ```go // source at testutil/buffer.go -func NewBuffer() *Buffer +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()) +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() +func RevertOSEnv() +func MockCleanOsEnv(mp map[string]string, fn func()) +// source at testutil/fsmock.go +func NewDirEnt(fpath string, isDir ...bool) *DirEnt // 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 +func NewHttpRequest(method, path string, data *MD) *http.Request +func MockRequest(h http.Handler, method, path string, data *MD) *httptest.ResponseRecorder // source at testutil/testutil.go -func DiscardStdout() error -func ReadOutput() (s string) -func RewriteStdout() -func RestoreStdout(printData ...bool) (s string) -func RewriteStderr() -func RestoreStderr(printData ...bool) (s string) +func DiscardStdout() error +func ReadOutput() (s string) +func RewriteStdout() +func RestoreStdout(printData ...bool) (s string) +func RewriteStderr() +func RestoreStderr(printData ...bool) (s string) // source at testutil/writer.go -func NewTestWriter() *TestWriter +func NewTestWriter() *TestWriter ``` ### Timex @@ -1177,47 +1421,57 @@ func NewTestWriter() *TestWriter Provides an enhanced time.Time implementation, and add more commonly used functional methods. ```go +// source at timex/gotime.go +func SetLocalByName(tzName string) error +func NowAddDay(day int) time.Time +func NowAddHour(hour int) time.Time +func NowAddMinutes(minutes int) time.Time +func NowAddSec(seconds 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 +func AddSeconds(t time.Time, seconds int) time.Time +func AddSec(t time.Time, seconds int) time.Time +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 TodayStart() time.Time +func TodayEnd() time.Time // source at timex/template.go -func ToLayout(template string) string +func ToLayout(template string) string // source at timex/timex.go -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 +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 -func DateFormat(t time.Time, template string) string -func FormatByTpl(t time.Time, template string) string -func FormatUnix(sec int64) string -func FormatUnixBy(sec int64, layout string) string -func FormatUnixByTpl(sec int64, template string) string -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 -func AddSeconds(t time.Time, seconds int) time.Time -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 TodayStart() time.Time -func TodayEnd() time.Time -func HowLongAgo(sec int64) string -func ToDuration(s string) (time.Duration, error) +func NowUnix() int64 +func Format(t time.Time) string +func FormatBy(t time.Time, layout string) string +func Date(t time.Time, template ...string) string +func Datetime(t time.Time, template ...string) string +func DateFormat(t time.Time, template string) string +func FormatByTpl(t time.Time, template string) string +func FormatUnix(sec int64, layout ...string) string +func FormatUnixBy(sec int64, layout string) string +func FormatUnixByTpl(sec int64, template ...string) string +func HowLongAgo(sec int64) string +func ToTime(s string, layouts ...string) (time.Time, error) +func ToDur(s string) (time.Duration, error) +func ToDuration(s string) (time.Duration, error) +func IsDuration(s string) bool +func TryToTime(s string, bt time.Time) (time.Time, error) +func InRange(dst, start, end time.Time) bool +func ParseRange(expr string, opt *ParseRangeOpt) (start, end time.Time, err error) ``` #### Timex Usage @@ -1383,4 +1637,3 @@ root@xx:/go/work# go test ./... ## 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 829ffe39c..68af10a4a 100644 --- a/vendor/github.com/gookit/goutil/README.zh-CN.md +++ b/vendor/github.com/gookit/goutil/README.zh-CN.md @@ -7,7 +7,7 @@ [![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一些常用的string、number、slice、map、struct、env、system等工具函数实现、增强、收集和整理。 +`goutil` Go 常用功能的扩展工具库(**600+**)。包含:数字,byte, 字符串,slice/数组,Map,结构体,反射,文本,文件,错误,时间日期,测试,特殊处理,格式化,常用信息获取等等。 > **[EN README](README.md)** @@ -27,7 +27,6 @@ Go一些常用的string、number、slice、map、struct、env、system等工具 - [`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) 提供一些进程操作相关的实用功能。 **扩展工具包** @@ -41,9 +40,11 @@ Go一些常用的string、number、slice、map、struct、env、system等工具 - sysutil: - [clipboard](sysutil/clipboard) 提供简单的剪贴板读写操作工具库 - [cmdr](sysutil/cmdr) 提供快速构建和运行一个cmd,批量运行多个cmd任务 + - [process](./sysutil/process) 提供一些进程操作相关的实用功能。 - [`testutil`](testutil) test help 相关操作的函数工具包. eg: http test, mock ENV value - [assert](testutil/assert) 用于帮助测试的断言函数工具包 - [`timex`](timex) 提供增强的 time.Time 实现。添加更多常用的功能方法 + - 提供类似 `Y-m-d H:i:s` 的日期时间格式解析处理 - 例如: DayStart(), DayAfter(), DayAgo(), DateFormat() 等等 ## GoDoc @@ -80,54 +81,75 @@ u64Val = goutil.Uint("2") // 2 ## Packages -### Array/Slice +### Array and Slice > Package `github.com/gookit/goutil/arrutil` ```go // source at arrutil/arrutil.go -func Reverse(ss []string) -func StringsRemove(ss []string, s string) []string -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 +func Reverse(ss []string) +func StringsRemove(ss []string, s string) []string +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 +func GetRandomOne[T any](arr []T) T +func RandomOne[T any](arr []T) T +func Unique[T ~string | comdef.XintOrFloat](list []T) []T +func IndexOf[T ~string | comdef.XintOrFloat](val T, list []T) int // 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 any) bool -func Contains(arr, val any) bool -func NotContains(arr, val any) bool +func IntsHas(ints []int, val int) bool +func Int64sHas(ints []int64, val int64) bool +func InStrings(elem string, ss []string) bool +func StringsHas(ss []string, val string) bool +func NotIn[T comdef.ScalarType](value T, list []T) bool +func In[T comdef.ScalarType](value T, list []T) bool +func ContainsAll[T comdef.ScalarType](list, values []T) bool +func IsSubList[T comdef.ScalarType](values, list []T) bool +func IsParent[T comdef.ScalarType](values, list []T) 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 +func TwowaySearch(data any, item any, fn Comparer) (int, error) +func MakeEmptySlice(itemType reflect.Type) any +func CloneSlice(data any) any +func Differences[T any](first, second []T, fn Comparer) []T +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/collection_gte118.go +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 // 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 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 +func JoinStrings(sep string, ss ...string) string +func StringsJoin(sep string, ss ...string) string +func JoinSlice(sep string, arr ...any) string +func ToInt64s(arr any) (ret []int64, err error) +func MustToInt64s(arr any) []int64 +func SliceToInt64s(arr []any) []int64 +func StringsAsInts(ss []string) []int +func StringsToInts(ss []string) (ints []int, err error) +func StringsTryInts(ss []string) (ints []int, err error) +func AnyToSlice(sl any) (ls []any, err error) +func AnyToStrings(arr any) []string +func MustToStrings(arr any) []string +func StringsToSlice(ss []string) []any +func ToStrings(arr any) (ret []string, err error) +func SliceToStrings(arr []any) []string +func QuietStrings(arr []any) []string +func ConvType[T any, R any](arr []T, newElemTyp R) ([]R, error) +func AnyToString(arr any) string +func SliceToString(arr ...any) string +func ToString(arr []any) string +func CombineToMap[K comdef.SortedType, V any](keys []K, values []V) map[K]V +func CombineToSMap(keys, values []string) map[string]string // source at arrutil/format.go -func NewFormatter(arr any) *ArrFormatter -func FormatIndent(arr any, indent string) string +func NewFormatter(arr any) *ArrFormatter +func FormatIndent(arr any, indent string) string ``` #### ArrUtil Usage @@ -151,32 +173,83 @@ ss, err := arrutil.ToStrings([]int{1, 2}) // ss: []string{"1", "2"} ``` +### Basefn + +> Package `github.com/gookit/goutil/basefn` + +```go +// source at basefn/basefunc.go +func Panicf(format string, v ...any) +func MustOK(err error) +func Must[T any](v T, err error) T +func ErrOnFail(cond bool, err error) error +func OrError(cond bool, err error) error +func FirstOr[T any](sl []T, elseVal T) T +func OrValue[T any](cond bool, okVal, elVal T) T +func OrReturn[T any](cond bool, okFn, elseFn func() T) T +func CallOn(cond bool, fn ErrFunc) error +func CallOrElse(cond bool, okFn, elseFn ErrFunc) error +// source at basefn/extfunc.go +func DataSize(size uint64) string +func HowLongAgo(sec int64) string +``` + +### Bytes Utils + +> Package `github.com/gookit/goutil/byteutil` + +```go +// source at byteutil/buffer.go +func NewBuffer() *Buffer +// source at byteutil/byteutil.go +func Random(length int) ([]byte, error) +func FirstLine(bs []byte) []byte +func StrOrErr(bs []byte, err error) (string, error) +func SafeString(bs []byte, err error) string +func String(b []byte) string +func ToString(b []byte) string +func AppendAny(dst []byte, v any) []byte +func Cut(bs []byte, sep byte) (before, after []byte, found bool) +// source at byteutil/bytex.go +func Md5(src any) []byte +// source at byteutil/check.go +func IsNumChar(c byte) bool +// source at byteutil/encoder.go +func NewStdEncoder(encFn func(src []byte) []byte, decFn func(src []byte) ([]byte, error)) *StdEncoder +// source at byteutil/pool.go +func NewChanPool(maxSize int, width int, capWidth int) *ChanPool +``` + ### 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 +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) +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/ext.go +func NewEnumString(enum ...string) EnumString +func NewKVString() KVString // source at cflag/optarg.go -func NewArg(name, desc string, required bool) *FlagArg +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 +func IsGoodName(name string) bool +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 @@ -189,71 +262,72 @@ func ReplaceShorts(args []string, shortsMap map[string]string) []string ```go // source at cliutil/cliutil.go -func LineBuild(binFile string, args []string) string -func BuildLine(binFile string, args []string) string -func String2OSArgs(line string) []string -func StringToOSArgs(line string) []string -func ParseLine(line string) []string -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 ExecCommand(binName string, args []string, workDir ...string) (string, error) -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 +func LineBuild(binFile string, args []string) string +func BuildLine(binFile string, args []string) string +func String2OSArgs(line string) []string +func StringToOSArgs(line string) []string +func ParseLine(line string) []string +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 ExecCommand(binName string, args []string, workDir ...string) (string, error) +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 // 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...) } +func Redp(a ...any) +func Redf(format string, a ...any) +func Redln(a ...any) +func Bluep(a ...any) +func Bluef(format string, a ...any) +func Blueln(a ...any) +func Cyanp(a ...any) +func Cyanf(format string, a ...any) +func Cyanln(a ...any) +func Grayp(a ...any) +func Grayf(format string, a ...any) +func Grayln(a ...any) +func Greenp(a ...any) +func Greenf(format string, a ...any) +func Greenln(a ...any) +func Yellowp(a ...any) +func Yellowf(format string, a ...any) +func Yellowln(a ...any) +func Magentap(a ...any) +func Magentaf(format string, a ...any) +func Magentaln(a ...any) +func Infop(a ...any) +func Infof(format string, a ...any) +func Infoln(a ...any) +func Successp(a ...any) +func Successf(format string, a ...any) +func Successln(a ...any) +func Errorp(a ...any) +func Errorf(format string, a ...any) +func Errorln(a ...any) +func Warnp(a ...any) +func Warnf(format string, a ...any) +func Warnln(a ...any) // 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) +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) -func ReadPassword(question ...string) string -func InputIsYes(ans string) bool -func ByteIsYes(ans byte) bool +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) +func ReadAsBool(tip string, defVal bool) bool +func ReadPassword(question ...string) string +func Confirm(tip string, defVal ...bool) bool +func InputIsYes(ans string) bool +func ByteIsYes(ans byte) bool ``` #### CLI Util Usage @@ -317,19 +391,30 @@ Build line: ./myapp -a val0 -m "this is message" arg0 ```go // source at dump/dump.go -func Std() *Dumper -func Reset() -func Config(fn func(opts *Options)) -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) +func Std() *Dumper +func Reset() +func Config(fns ...OptionFunc) +func Print(vs ...any) +func Println(vs ...any) +func Fprint(w io.Writer, vs ...any) +func Std2() *Dumper +func Reset2() +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 -func NewDefaultOptions(out io.Writer, skip int) *Options +func NewDumper(out io.Writer, skip int) *Dumper +func NewWithOptions(fns ...OptionFunc) *Dumper +// source at dump/options.go +func NewDefaultOptions(out io.Writer, skip int) *Options +func SkipNilField() OptionFunc +func SkipPrivate() OptionFunc +func BytesAsString() OptionFunc +func WithCallerSkip(skip int) OptionFunc +func WithoutPosition() OptionFunc +func WithoutOutput(out io.Writer) OptionFunc +func WithoutColor() OptionFunc +func WithoutType() OptionFunc ``` #### Examples @@ -381,31 +466,38 @@ Preview: ```go // source at envutil/envutil.go -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) +func VarReplace(s string) string +func VarParse(val string) string +func ParseEnvValue(val string) string +func ParseValue(val string) (newVal string) +func SetEnvMap(mp map[string]string) +func SetEnvs(kvPairs ...string) +func UnsetEnvs(keys ...string) // source at envutil/get.go -func Getenv(name string, def ...string) string -func GetInt(name string, def ...int) int -func GetBool(name string, def ...bool) bool -func Environ() map[string]string +func Getenv(name string, def ...string) string +func GetInt(name string, def ...int) int +func GetBool(name string, def ...bool) bool +func GetMulti(names ...string) map[string]string +func EnvPaths() []string +func EnvMap() map[string]string +func Environ() map[string]string +func SearchEnvKeys(keywords string) map[string]string +func SearchEnv(keywords string, matchValue bool) map[string]string // source at envutil/info.go -func IsWin() bool -func IsWindows() bool -func IsMac() bool -func IsLinux() bool -func IsMSys() bool -func IsWSL() bool -func IsTerminal(fd uintptr) bool -func StdIsTerminal() bool -func IsConsole(out io.Writer) bool -func HasShellEnv(shell string) bool -func IsSupportColor() bool -func IsSupport256Color() bool -func IsSupportTrueColor() bool -func IsGithubActions() bool +func IsWin() bool +func IsWindows() bool +func IsMac() bool +func IsLinux() bool +func IsMSys() bool +func IsWSL() bool +func IsTerminal(fd uintptr) bool +func StdIsTerminal() bool +func IsConsole(out io.Writer) bool +func HasShellEnv(shell string) bool +func IsSupportColor() bool +func IsSupport256Color() bool +func IsSupportTrueColor() bool +func IsGithubActions() bool ``` #### ENV Util Usage @@ -436,41 +528,50 @@ envutil.ParseValue("${ENV_NAME | defValue}") ```go +// source at errorx/assert.go +func IsTrue(result bool, fmtAndArgs ...any) error +func IsFalse(result bool, fmtAndArgs ...any) error +func IsIn[T comdef.ScalarType](value T, list []T, fmtAndArgs ...any) error +func NotIn[T comdef.ScalarType](value T, list []T, fmtAndArgs ...any) error // source at errorx/errors.go -func NewR(code int, msg string) ErrorR -func Fail(code int, msg string) ErrorR -func Suc(msg string) ErrorR +func NewR(code int, msg string) ErrorR +func Fail(code int, msg string) ErrorR +func Failf(code int, tpl string, v ...any) ErrorR +func Suc(msg string) ErrorR // source at errorx/errorx.go -func New(msg string) 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 ...any) error -func WithPrev(err error, msg string) 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 ...any) error +func New(msg string) 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 ...any) error +func WithPrev(err error, msg string) 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 ...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) +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 ...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 any) bool -func As(err error, target any) bool +func Err(msg string) error +func Raw(msg string) error +func Ef(tpl string, vars ...any) error +func Errf(tpl string, vars ...any) error +func Rawf(tpl string, vars ...any) error +func Cause(err error) error +func Unwrap(err error) error +func Previous(err error) error +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 any) bool +func As(err error, target any) bool ``` #### Errorx 使用示例 @@ -549,21 +650,23 @@ runtime.goexit() ``` -### Formatting +### Format Utils > Package `github.com/gookit/goutil/fmtutil` ```go +// source at fmtutil/fmtutil.go +func StringOrJSON(v any) ([]byte, error) // source at fmtutil/format.go -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 []any) (message string) +func DataSize(size uint64) string +func SizeToString(size uint64) string +func StringToByte(sizeStr string) uint64 +func ParseByte(sizeStr string) uint64 +func PrettyJSON(v any) (string, error) +func StringsToInts(ss []string) (ints []int, err error) +func ArgsWithSpaces(vs []any) (message string) // source at fmtutil/time.go -func HowLongAgo(sec int64) string +func HowLongAgo(sec int64) string ``` ### File System @@ -572,57 +675,93 @@ func HowLongAgo(sec int64) string ```go // source at fsutil/check.go -func PathExists(path string) bool -func IsDir(path string) bool -func FileExists(path string) bool -func IsFile(path string) bool -func IsAbsPath(aPath string) bool -func IsImageFile(path string) bool -func IsZipFile(filepath string) bool +func PathExists(path string) bool +func IsDir(path string) bool +func FileExists(path string) bool +func IsFile(path string) bool +func IsAbsPath(aPath string) bool +func IsImageFile(path string) bool +func IsZipFile(filepath string) bool +func PathMatch(pattern, s string) bool +// source at fsutil/find.go +func SearchNameUp(dirPath, name string) string +func SearchNameUpx(dirPath, name string) (string, bool) +func WalkDir(dir string, fn fs.WalkDirFunc) error +func GlobWithFunc(pattern string, fn func(filePath string) error) (err error) +func OnlyFindDir(_ string, ent fs.DirEntry) bool +func OnlyFindFile(_ string, ent fs.DirEntry) bool +func IncludeSuffix(ss ...string) FilterFunc +func ExcludeDotFile(_ string, ent fs.DirEntry) bool +func ExcludeSuffix(ss ...string) FilterFunc +func ApplyFilters(fPath string, ent fs.DirEntry, filters []FilterFunc) bool +func FindInDir(dir string, handleFn HandleFunc, filters ...FilterFunc) (e error) // source at fsutil/fsutil.go -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 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 JoinPaths(elem ...string) string +func JoinSubPaths(basePath string, elem ...string) string +func SlashPath(path string) string +func UnixPath(path string) string +func ToAbsPath(p string) string // 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 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) +func Dir(fpath string) string +func PathName(fpath string) string +func Name(fpath string) string +func FileExt(fpath string) string +func Extname(fpath string) string +func Suffix(fpath string) string +func Expand(pathStr string) string +func ExpandPath(pathStr string) string +func ResolvePath(pathStr string) string +func SplitPath(pathStr string) (dir, name string) +// source at fsutil/info_nonwin.go +func Realpath(pathStr string) string // 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, 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 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) +func Mkdir(dirPath string, perm os.FileMode) error +func MkDirs(perm os.FileMode, dirPaths ...string) error +func MkSubDirs(perm os.FileMode, parentDir string, subDirs ...string) error +func MkParentDir(fpath string) error +func OpenFile(filepath string, flag int, perm os.FileMode) (*os.File, error) +func MustOpenFile(filepath string, flag int, perm os.FileMode) *os.File +func QuickOpenFile(filepath string, fileFlag ...int) (*os.File, error) +func OpenAppendFile(filepath string, filePerm ...os.FileMode) (*os.File, error) +func OpenTruncFile(filepath string, filePerm ...os.FileMode) (*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 Remove(fPath string) error +func MustRemove(fPath string) +func QuietRemove(fPath string) +func RmIfExist(fPath string) error +func DeleteIfExist(fPath string) error +func RmFileIfExist(fPath string) error +func DeleteIfFileExist(fPath string) error +func Unzip(archive, targetDir string) (err error) +// source at fsutil/opread.go +func NewIOReader(in any) (r io.Reader, err error) +func DiscardReader(src io.Reader) +func ReadFile(filePath string) []byte +func MustReadFile(filePath string) []byte +func ReadReader(r io.Reader) []byte +func MustReadReader(r io.Reader) []byte +func ReadString(in any) string +func ReadStringOrErr(in any) (string, error) +func ReadAll(in any) []byte +func GetContents(in any) []byte +func ReadOrErr(in any) ([]byte, error) +func ReadExistFile(filePath string) []byte +func TextScanner(in any) *scanner.Scanner +func LineScanner(in any) *bufio.Scanner +// source at fsutil/opwrite.go +func PutContents(filePath string, data any, fileFlag ...int) (int, error) +func WriteFile(filePath string, data any, perm os.FileMode, fileFlag ...int) error +func WriteOSFile(f *os.File, data any) (n int, err error) +func CopyFile(srcPath, dstPath string) error +func MustCopyFile(srcPath, dstPath string) ``` #### FsUtil Usage @@ -634,30 +773,23 @@ package main import ( "fmt" - "os" + "io/fs" - "github.com/gookit/goutil/fsutil/finder" + "github.com/gookit/goutil/fsutil" ) func main() { - f := finder.EmptyFinder() + // find all files in dir + fsutil.FindInDir("./", func(filePath string, de fs.DirEntry) error { + fmt.Println(filePath) + return nil + }) - f. - AddDir("./testdata"). - AddFile("finder.go"). - NoDotFile(). - // NoDotDir(). - Find(). - Each(func(filePath string) { - fmt.Println(filePath) - }) - - finder.NewFinder([]string{"./testdata"}). - AddFile("finder.go"). - NoDotDir(). - EachStat(func(fi os.FileInfo, filePath string) { - fmt.Println(filePath, "=>", fi.ModTime()) - }) + // find files with filters + fsutil.FindInDir("./", func(filePath string, de fs.DirEntry) error { + fmt.Println(filePath) + return nil + }, fsutil.ExcludeDotFile) } ``` @@ -668,18 +800,21 @@ func main() { ```go // source at jsonutil/jsonutil.go -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 +func WriteFile(filePath string, data any) error +func WritePretty(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 IsJSON(s string) bool +func IsJSONFast(s string) bool +func StripComments(src string) string ``` ### Map @@ -688,33 +823,41 @@ func StripComments(src string) string ```go // source at maputil/check.go -func HasKey(mp, key any) (ok bool) -func HasAllKeys(mp any, keys ...any) (ok bool, noKey any) +func HasKey(mp, key any) (ok bool) +func HasOneKey(mp any, keys ...any) (ok bool, key any) +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]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) +func KeyToLower(src map[string]string) map[string]string +func ToStringMap(src map[string]any) map[string]string +func CombineToSMap(keys, values []string) SMap +func CombineToMap[K comdef.SortedType, V any](keys []K, values []V) map[K]V +func ToAnyMap(mp any) map[string]any +func TryAnyMap(mp any) (map[string]any, error) +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 +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) +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) +func EachAnyMap(mp any, fn func(key string, val 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 MakeByPath(path string, val any) (mp map[string]any) -func MakeByKeys(keys []string, val any) (mp map[string]any) +func SimpleMerge(src, dst map[string]any) map[string]any +func DeepMerge(src, dst map[string]any, deep int) map[string]any +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 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) +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 @@ -723,57 +866,68 @@ func SetByKeys(mp *map[string]any, keys []string, val any) (err error) ```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) +func Compare(srcVal, dstVal any, op string) (ok bool) +func CompInt[T comdef.Xint](srcVal, dstVal T, op string) (ok bool) +func CompInt64(srcVal, dstVal int64, op string) bool +func CompFloat[T comdef.Float](srcVal, dstVal T, op string) (ok bool) +func CompValue[T comdef.XintOrFloat](srcVal, dstVal T, op string) (ok bool) +func InRange[T comdef.IntOrFloat](val, min, max T) bool +func OutRange[T comdef.IntOrFloat](val, min, max T) bool +func InUintRange[T comdef.Uint](val, min, max T) bool // source at mathutil/convert.go -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) +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 SafeInt64(in any) int64 +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) +func Min[T comdef.XintOrFloat](x, y T) T +func Max[T comdef.XintOrFloat](x, y T) T +func SwapMin[T comdef.XintOrFloat](x, y T) (T, T) +func SwapMax[T comdef.XintOrFloat](x, y T) (T, T) +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) +func MaxFloat(x, y float64) float64 +func OrElse[T comdef.XintOrFloat](in, nv T) T // source at mathutil/number.go -func IsNumeric(c byte) bool -func Percent(val, total int) float64 -func ElapsedTime(startTime time.Time) string -func DataSize(size uint64) string -func HowLongAgo(sec int64) string +func IsNumeric(c byte) bool +func Percent(val, total int) float64 +func ElapsedTime(startTime time.Time) string +func DataSize(size uint64) string +func HowLongAgo(sec int64) string // source at mathutil/random.go -func RandomInt(min, max int) int -func RandInt(min, max int) int { return RandomInt(min, max) } -func RandIntWithSeed(min, max int, seed int64) int -func RandomIntWithSeed(min, max int, seed int64) int +func RandomInt(min, max int) int +func RandInt(min, max int) int +func RandIntWithSeed(min, max int, seed int64) int +func RandomIntWithSeed(min, max int, seed int64) int ``` ### Reflects @@ -782,33 +936,45 @@ func RandomIntWithSeed(min, max int, seed int64) int ```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 +func HasChild(v reflect.Value) bool +func IsArrayOrSlice(k reflect.Kind) bool +func IsSimpleKind(k reflect.Kind) bool +func IsAnyInt(k reflect.Kind) bool +func IsIntx(k reflect.Kind) bool +func IsUintX(k reflect.Kind) 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) +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 ConvSlice(oldSlRv reflect.Value, newElemTyp reflect.Type) (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 +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) +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 SliceElemKind(typ reflect.Type) reflect.Kind +func UnexportedValue(rv reflect.Value) any +func SetUnexportedValue(rv reflect.Value, value any) +func SetValue(rv reflect.Value, val any) error +func SetRValue(rv, val reflect.Value) +func EachMap(mp reflect.Value, fn func(key, val reflect.Value)) +func EachStrAnyMap(mp reflect.Value, fn func(key string, val any)) +func FlatMap(rv reflect.Value, fn FlatFunc) // source at reflects/value.go -func Wrap(rv reflect.Value) Value -func ValueOf(v any) Value +func Wrap(rv reflect.Value) Value +func ValueOf(v any) Value ``` ### Stdio @@ -817,14 +983,23 @@ func ValueOf(v any) Value ```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 +func QuietFprint(w io.Writer, a ...any) +func QuietFprintf(w io.Writer, tpl string, vs ...any) +func QuietFprintln(w io.Writer, a ...any) +func QuietWriteString(w io.Writer, ss ...string) +// source at stdio/stdio.go +func DiscardReader(src io.Reader) +func ReadString(r io.Reader) string +func MustReadReader(r io.Reader) []byte +func NewIOReader(in any) io.Reader +func NewScanner(in any) *bufio.Scanner +func WriteByte(b byte) +func WriteBytes(bs []byte) +func WritelnBytes(bs []byte) +func WriteString(s string) +func Writeln(s string) // source at stdio/writer.go -func NewWriteWrapper(w io.Writer) *WriteWrapper +func NewWriteWrapper(w io.Writer) *WriteWrapper ``` ### Standard @@ -833,40 +1008,40 @@ func NewWriteWrapper(w io.Writer) *WriteWrapper ```go // 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)) +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 +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/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) +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 any) string -func CutFuncName(fullFcName string) (pkgPath, shortFnName string) -func PkgName(fullFcName string) string +func FuncName(fn any) string +func CutFuncName(fullFcName string) (pkgPath, shortFnName string) +func PkgName(fullFcName string) string // source at stdutil/stack.go -func GetCallStacks(all bool) []byte -func GetCallerInfo(skip int) string -func SimpleCallersInfo(skip, num int) []string -func GetCallersInfo(skip, max int) []string +func GetCallStacks(all bool) []byte +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 ...any) -func GoVersion() string +func DiscardE(_ error) +func PanicIfErr(err error) +func PanicIf(err error) +func Panicf(format string, v ...any) +func GoVersion() string ``` ### Structs @@ -875,28 +1050,49 @@ func GoVersion() string ```go // source at structs/alias.go -func NewAliases(checker func(alias string)) *Aliases +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) +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 ToSMap(st any, optFns ...MapOptFunc) map[string]string +func TryToSMap(st any, optFns ...MapOptFunc) (map[string]string, error) +func MustToSMap(st any, optFns ...MapOptFunc) map[string]string +func ToString(st any, optFns ...MapOptFunc) string +func WithMapTagName(tagName string) MapOptFunc +func MergeAnonymous(opt *MapOptions) +func ExportPrivate(opt *MapOptions) +func StructToMap(st any, optFns ...MapOptFunc) (map[string]any, error) +// source at structs/copy.go +func MapStruct(srcSt, dstSt any) // source at structs/data.go -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 +func NewData() *Data +func NewOrderedMap(len int) *OrderedMap +// source at structs/init.go +func InitStructSlice(opt *InitOptions) +func Init(ptr any, optFns ...InitOptFunc) error +func InitDefaults(ptr any, optFns ...InitOptFunc) error // source at structs/structs.go -func MapStruct(srcSt, dstSt any) +func IsExported(name string) bool +func IsUnexported(name string) bool // source at structs/tags.go -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) +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 ParseTagValueQuick(tagVal string, defines []string) maputil.SMap +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 +func NewValue(val any) *Value +// source at structs/wrapper.go +func Wrap(src any) *Wrapper +func NewWrapper(src any) *Wrapper +func WrapValue(rv reflect.Value) *Wrapper +// source at structs/writer.go +func NewWriter(ptr any) *Wrapper +func WithParseDefault(opt *SetOptions) +func SetValues(ptr any, data map[string]any, optFns ...SetOptFunc) error ``` ### Strings @@ -905,270 +1101,319 @@ func NewValue(val any) *Value ```go // source at strutil/bytes.go -func NewBuffer() *Buffer -func NewByteChanPool(maxSize int, width int, capWidth int) *ByteChanPool +func NewBuffer() *Buffer +func NewByteChanPool(maxSize, width, capWidth int) *ByteChanPool // source at strutil/check.go -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 HasOneSub(s string, subs []string) bool -func HasAllSubs(s string, subs []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 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 +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 IEqual(s1, s2 string) bool +func NoCaseEq(s, t string) bool +func IContains(s, sub string) bool +func ContainsByte(s string, c byte) bool +func ContainsOne(s string, subs []string) bool +func HasOneSub(s string, subs []string) bool +func ContainsAll(s string, subs []string) bool +func HasAllSubs(s string, subs []string) bool +func IsStartsOf(s string, prefixes []string) bool +func HasOnePrefix(s string, prefixes []string) bool +func HasPrefix(s string, prefix string) bool +func IsStartOf(s, prefix string) bool +func HasSuffix(s string, suffix string) bool +func IsEndOf(s, suffix string) bool +func HasOneSuffix(s string, suffixes []string) bool +func IsValidUtf8(s string) bool +func IsSpace(c byte) bool +func IsEmpty(s string) bool +func IsBlank(s string) bool +func IsNotBlank(s string) bool +func IsBlankBytes(bs []byte) bool +func IsSymbol(r rune) bool +func HasEmpty(ss ...string) bool +func IsAllEmpty(ss ...string) bool +func IsVersion(s string) bool +func Compare(s1, s2, op string) bool +func VersionCompare(v1, v2, op string) bool +func SimpleMatch(s string, keywords []string) bool +func QuickMatch(pattern, s string) bool +func PathMatch(pattern, s string) bool +func GlobMatch(pattern, s string) bool +func LikeMatch(pattern, s string) bool +func MatchNodePath(pattern, s string, sep string) bool // source at strutil/convert.go -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) { return ToIntSlice(s, sep...) } -func ToIntSlice(s string, sep ...string) (ints []int, err error) -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) +func Quote(s string) string +func Unquote(s string) string +func Join(sep string, ss ...string) string +func JoinList(sep string, ss []string) string +func JoinAny(sep string, parts ...any) string +func Implode(sep string, ss ...string) string +func String(val any) (string, error) +func ToString(val any) (string, error) +func QuietString(in any) string +func SafeString(in any) string +func MustString(in any) string +func StringOrErr(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 Int2(s string) int +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 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 ToSlice(s string, sep ...string) []string +func ToOSArgs(s string) []string +func ToDuration(s string) (time.Duration, error) +// source at strutil/crypto.go +func Md5(src any) string +func MD5(src any) string +func GenMd5(src any) string +func Md5Bytes(src any) []byte +func HashPasswd(pwd, key string) string +func VerifyPasswd(pwdMAC, pwd, key string) bool // source at strutil/encode.go -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 +func EscapeJS(s string) string +func EscapeHTML(s string) string +func AddSlashes(s string) string +func StripSlashes(s string) string +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/ext.go +func NewComparator(src, dst string) *SimilarComparator +func Similarity(s, t string, rate float32) (float32, bool) // source at strutil/filter.go -func Trim(s string, cutSet ...string) string -func Ltrim(s string, cutSet ...string) string { return TrimLeft(s, cutSet...) } -func LTrim(s string, cutSet ...string) string { return TrimLeft(s, cutSet...) } -func TrimLeft(s string, cutSet ...string) string -func Rtrim(s string, cutSet ...string) string { return TrimRight(s, cutSet...) } -func RTrim(s string, cutSet ...string) string { return TrimRight(s, cutSet...) } -func TrimRight(s string, cutSet ...string) string -func FilterEmail(s string) string +func Trim(s string, cutSet ...string) string +func Ltrim(s string, cutSet ...string) string +func LTrim(s string, cutSet ...string) string +func TrimLeft(s string, cutSet ...string) string +func Rtrim(s string, cutSet ...string) string +func RTrim(s string, cutSet ...string) string +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) } -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 SnakeCase(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 +func Title(s string) string +func Lower(s string) string +func Lowercase(s string) string +func Upper(s string) string +func Uppercase(s string) string +func UpperWord(s string) string +func LowerFirst(s string) string +func UpperFirst(s string) string +func SnakeCase(s string, sep ...string) string +func Camel(s string, sep ...string) string +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 +func MicroTimeID() string +func MicroTimeHexID() string +func DatetimeNo(prefix string) 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 +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 PadChars[T byte | rune](cs []T, pad T, length int, pos PosFlag) []T +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 +func RepeatChars[T byte | rune](char T, times int) []T +// source at strutil/parse.go +func MustToTime(s string, layouts ...string) time.Time +func ToTime(s string, layouts ...string) (t time.Time, err error) +func ParseSizeRange(expr string, opt *ParseSizeOpt) (min, max uint64, err error) +func SafeByteSize(sizeStr string) uint64 +func ToByteSize(sizeStr string) (uint64, error) // source at strutil/random.go -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) +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) +func RandWithTpl(n int, letters string) string // 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) +func RuneIsWord(c rune) bool +func RuneIsLower(c rune) bool +func RuneIsUpper(c rune) bool +func RunePos(s string, ru rune) int +func IsSpaceRune(r rune) bool +func Utf8Len(s string) int +func Utf8len(s string) int +func RuneCount(s string) int +func RuneWidth(r rune) int +func TextWidth(s string) int +func Utf8Width(s string) int +func RunesWidth(rs []rune) (w int) +func Truncate(s string, w int, tail string) string +func TextTruncate(s string, w int, tail string) string +func Utf8Truncate(s string, w int, tail string) string +func TextSplit(s string, w int) []string +func Utf8Split(s string, w int) []string +func TextWrap(s string, w int) string +func WidthWrap(s string, w int) string +func WordWrap(s string, w int) string // 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) } -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) +func Cut(s, sep string) (before string, after string, found bool) +func QuietCut(s, sep string) (before string, after string) +func MustCut(s, sep string) (before string, after string) +func TrimCut(s, sep string) (string, string) +func SplitKV(s, sep string) (string, string) +func SplitValid(s, sep string) (ss []string) +func Split(s, sep string) (ss []string) +func SplitNValid(s, sep string, n int) (ss []string) +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, strict ...bool) (string, string) +func FirstLine(output string) string // source at strutil/strutil.go -func Replaces(str string, pairs map[string]string) 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 +func OrCond(cond bool, s1, s2 string) string +func OrElse(s, orVal string) string +func OrHandle(s string, fn func(s string) string) string +func Valid(ss ...string) string +func Replaces(str string, pairs map[string]string) string +func NewReplacer(pairs map[string]string) *strings.Replacer +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 +func SubstrCount(s string, substr string, params ...uint64) (int, error) ``` -### System +### System Utils > Package `github.com/gookit/goutil/sysutil` ```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 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) // source at sysutil/stack.go -func CallersInfos(skip, num int, filters ...func(file string, fc *runtime.Func) bool) []*CallerInfo +func CallersInfos(skip, num int, filters ...func(file string, fc *runtime.Func) bool) []*CallerInfo // source at sysutil/sysenv.go -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 +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 FindExecutable(binName string) (string, error) +func Executable(binName string) (string, error) +func HasExecutable(binName string) bool +func Getenv(name string, def ...string) string +func Environ() map[string]string +func EnvMapWith(newEnv map[string]string) map[string]string +func EnvPaths() []string +func SearchPath(keywords string, limit int) []string // source at sysutil/sysgo.go -func GoVersion() string -func ParseGoVersion(line string) (*GoInfo, error) -func OsGoInfo() (*GoInfo, error) +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 +func Workdir() string +func BinDir() string +func BinName() string +func BinFile() string +func Open(fileOrUrl string) error +func OpenBrowser(fileOrUrl string) error +func OpenFile(path string) error // 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 +func Kill(pid int, signal syscall.Signal) error +func ProcessExists(pid int) bool +// source at sysutil/sysutil_unix.go +func IsWin() bool +func IsWindows() bool +func IsMac() bool +func IsDarwin() bool +func IsLinux() bool +func OpenURL(URL string) error // source at sysutil/user.go -func MustFindUser(uname string) *user.User -func LoginUser() *user.User -func CurrentUser() *user.User -func UHomeDir() string -func UserHomeDir() string -func HomeDir() string -func UserDir(subPath string) string -func UserCacheDir(subPath string) string -func UserConfigDir(subPath string) string -func ExpandPath(path string) string +func MustFindUser(uname string) *user.User +func LoginUser() *user.User +func CurrentUser() *user.User +func UHomeDir() string +func UserHomeDir() string +func HomeDir() string +func UserDir(subPath string) string +func UserCacheDir(subPath string) string +func UserConfigDir(subPath string) string +func ExpandPath(path string) string +func ExpandHome(path string) string // source at sysutil/user_nonwin.go -func ChangeUserByName(newUname string) (err error) -func ChangeUserUidGid(newUid int, newGid int) (err error) +func ChangeUserByName(newUname string) (err error) +func ChangeUserUidGid(newUid int, newGid int) (err error) ``` -### Testing +### Testing Utils > Package `github.com/gookit/goutil/testutil` ```go // source at testutil/buffer.go -func NewBuffer() *Buffer +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()) +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() +func RevertOSEnv() +func MockCleanOsEnv(mp map[string]string, fn func()) +// source at testutil/fsmock.go +func NewDirEnt(fpath string, isDir ...bool) *DirEnt // 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 +func NewHttpRequest(method, path string, data *MD) *http.Request +func MockRequest(h http.Handler, method, path string, data *MD) *httptest.ResponseRecorder // source at testutil/testutil.go -func DiscardStdout() error -func ReadOutput() (s string) -func RewriteStdout() -func RestoreStdout(printData ...bool) (s string) -func RewriteStderr() -func RestoreStderr(printData ...bool) (s string) +func DiscardStdout() error +func ReadOutput() (s string) +func RewriteStdout() +func RestoreStdout(printData ...bool) (s string) +func RewriteStderr() +func RestoreStderr(printData ...bool) (s string) // source at testutil/writer.go -func NewTestWriter() *TestWriter +func NewTestWriter() *TestWriter ``` ### Timex @@ -1177,47 +1422,57 @@ func NewTestWriter() *TestWriter Provides an enhanced time.Time implementation, and add more commonly used functional methods. ```go +// source at timex/gotime.go +func SetLocalByName(tzName string) error +func NowAddDay(day int) time.Time +func NowAddHour(hour int) time.Time +func NowAddMinutes(minutes int) time.Time +func NowAddSec(seconds 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 +func AddSeconds(t time.Time, seconds int) time.Time +func AddSec(t time.Time, seconds int) time.Time +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 TodayStart() time.Time +func TodayEnd() time.Time // source at timex/template.go -func ToLayout(template string) string +func ToLayout(template string) string // source at timex/timex.go -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 +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 -func DateFormat(t time.Time, template string) string -func FormatByTpl(t time.Time, template string) string -func FormatUnix(sec int64) string -func FormatUnixBy(sec int64, layout string) string -func FormatUnixByTpl(sec int64, template string) string -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 -func AddSeconds(t time.Time, seconds int) time.Time -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 TodayStart() time.Time -func TodayEnd() time.Time -func HowLongAgo(sec int64) string -func ToDuration(s string) (time.Duration, error) +func NowUnix() int64 +func Format(t time.Time) string +func FormatBy(t time.Time, layout string) string +func Date(t time.Time, template ...string) string +func Datetime(t time.Time, template ...string) string +func DateFormat(t time.Time, template string) string +func FormatByTpl(t time.Time, template string) string +func FormatUnix(sec int64, layout ...string) string +func FormatUnixBy(sec int64, layout string) string +func FormatUnixByTpl(sec int64, template ...string) string +func HowLongAgo(sec int64) string +func ToTime(s string, layouts ...string) (time.Time, error) +func ToDur(s string) (time.Duration, error) +func ToDuration(s string) (time.Duration, error) +func IsDuration(s string) bool +func TryToTime(s string, bt time.Time) (time.Time, error) +func InRange(dst, start, end time.Time) bool +func ParseRange(expr string, opt *ParseRangeOpt) (start, end time.Time, err error) ``` #### Timex Usage diff --git a/vendor/github.com/gookit/goutil/arrutil/README.md b/vendor/github.com/gookit/goutil/arrutil/README.md index 9f9351ced..1a1467a95 100644 --- a/vendor/github.com/gookit/goutil/arrutil/README.md +++ b/vendor/github.com/gookit/goutil/arrutil/README.md @@ -12,6 +12,8 @@ go get github.com/gookit/goutil/arrutil ## Functions API +> **Note**: doc by run `go doc ./arrutil` + ```go func AnyToString(arr any) string func CloneSlice(data any) interface{} diff --git a/vendor/github.com/gookit/goutil/arrutil/arrutil.go b/vendor/github.com/gookit/goutil/arrutil/arrutil.go index 6f2f1bb5a..dff26561f 100644 --- a/vendor/github.com/gookit/goutil/arrutil/arrutil.go +++ b/vendor/github.com/gookit/goutil/arrutil/arrutil.go @@ -104,11 +104,15 @@ func RandomOne[T any](arr []T) T { } // 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)) +func Unique[T ~string | comdef.XintOrFloat](list []T) []T { + if len(list) < 2 { + return list + } - for _, t := range arr { + valMap := make(map[T]struct{}, len(list)) + uniArr := make([]T, 0, len(list)) + + for _, t := range list { if _, ok := valMap[t]; !ok { valMap[t] = struct{}{} uniArr = append(uniArr, t) @@ -116,3 +120,13 @@ func Unique[T ~string | comdef.XintOrFloat](arr []T) []T { } return uniArr } + +// IndexOf value in given slice. +func IndexOf[T ~string | comdef.XintOrFloat](val T, list []T) int { + for i, v := range list { + if v == val { + return i + } + } + return -1 +} diff --git a/vendor/github.com/gookit/goutil/arrutil/check.go b/vendor/github.com/gookit/goutil/arrutil/check.go index 93c7d838e..60117f07c 100644 --- a/vendor/github.com/gookit/goutil/arrutil/check.go +++ b/vendor/github.com/gookit/goutil/arrutil/check.go @@ -4,6 +4,7 @@ import ( "reflect" "strings" + "github.com/gookit/goutil/comdef" "github.com/gookit/goutil/mathutil" ) @@ -40,12 +41,48 @@ func StringsHas(ss []string, val string) bool { 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) +// NotIn check the given value whether not in the list +func NotIn[T comdef.ScalarType](value T, list []T) bool { + return !In(value, list) } -// Contains check array(strings, intXs, uintXs) should be contained the given value(int(X),string). +// In check the given value whether in the list +func In[T comdef.ScalarType](value T, list []T) bool { + for _, elem := range list { + if elem == value { + return true + } + } + return false +} + +// ContainsAll check given values is sub-list of sample list. +func ContainsAll[T comdef.ScalarType](list, values []T) bool { + return IsSubList(values, list) +} + +// IsSubList check given values is sub-list of sample list. +func IsSubList[T comdef.ScalarType](values, list []T) bool { + for _, value := range values { + if !In(value, list) { + return false + } + } + return true +} + +// IsParent check given values is parent-list of samples. +func IsParent[T comdef.ScalarType](values, list []T) bool { + return IsSubList(list, values) +} + +// 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 slice/array(strings, intXs, uintXs) should be contained the given value(int(X),string). +// +// TIP: Difference the In(), Contains() will try to convert value type, +// and Contains() support array type. func Contains(arr, val any) bool { if val == nil || arr == nil { return false diff --git a/vendor/github.com/gookit/goutil/arrutil/collection.go b/vendor/github.com/gookit/goutil/arrutil/collection.go index f17fa95af..ac556bfa3 100644 --- a/vendor/github.com/gookit/goutil/arrutil/collection.go +++ b/vendor/github.com/gookit/goutil/arrutil/collection.go @@ -160,6 +160,58 @@ func CloneSlice(data any) any { return reflect.AppendSlice(reflect.New(reflect.SliceOf(typeOfData.Elem())).Elem(), reflect.ValueOf(data)).Interface() } +// Differences 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 Differences[T any](first, second []T, fn Comparer) []T { + typeOfFirst := reflect.TypeOf(first) + if typeOfFirst.Kind() != reflect.Slice { + panic("collections.Excepts: first must be a slice") + } + + typeOfSecond := reflect.TypeOf(second) + if typeOfSecond.Kind() != reflect.Slice { + panic("collections.Excepts: second must be a slice") + } + + firstLen := len(first) + if firstLen == 0 { + return CloneSlice(second).([]T) + } + + secondLen := len(second) + if secondLen == 0 { + return CloneSlice(first).([]T) + } + + max := firstLen + if secondLen > firstLen { + max = secondLen + } + + result := make([]T, 0) + for i := 0; i < max; i++ { + if i < firstLen { + s := first[i] + if i, _ := TwowaySearch(second, s, fn); i < 0 { + result = append(result, s) + } + } + + if i < secondLen { + t := second[i] + if i, _ := TwowaySearch(first, t, fn); i < 0 { + result = append(result, t) + } + } + } + + return result +} + // Excepts Produces the set difference of two slice according to a comparer function. // // first: the first slice. MUST BE A SLICE. diff --git a/vendor/github.com/gookit/goutil/arrutil/collection_gte118.go b/vendor/github.com/gookit/goutil/arrutil/collection_gte118.go index f580c599e..fe406caf1 100644 --- a/vendor/github.com/gookit/goutil/arrutil/collection_gte118.go +++ b/vendor/github.com/gookit/goutil/arrutil/collection_gte118.go @@ -2,12 +2,14 @@ package arrutil // type MapFn func(obj T) (target V, find bool) -// Map an object list [object0{},object1{},...] to flatten list [object0.someKey, object1.someKey, ...] +// Map a list to new list +// +// eg: mapping [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 { + if target, ok := mapFn(obj); ok { flatArr = append(flatArr, target) } } diff --git a/vendor/github.com/gookit/goutil/arrutil/convert.go b/vendor/github.com/gookit/goutil/arrutil/convert.go index cff56bd09..34dbb3c43 100644 --- a/vendor/github.com/gookit/goutil/arrutil/convert.go +++ b/vendor/github.com/gookit/goutil/arrutil/convert.go @@ -6,7 +6,9 @@ import ( "strconv" "strings" + "github.com/gookit/goutil/comdef" "github.com/gookit/goutil/mathutil" + "github.com/gookit/goutil/reflects" "github.com/gookit/goutil/strutil" ) @@ -14,7 +16,7 @@ import ( var ErrInvalidType = errors.New("the input param type is invalid") /************************************************************* - * helper func for strings + * Join func for slice *************************************************************/ // JoinStrings alias of strings.Join @@ -27,32 +29,21 @@ 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 +// 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) } - - ints = append(ints, iVal) + sb.WriteString(strutil.QuietString(v)) } - 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 + return sb.String() } /************************************************************* @@ -88,14 +79,79 @@ func MustToInt64s(arr any) []int64 { func SliceToInt64s(arr []any) []int64 { i64s := make([]int64, len(arr)) for i, v := range arr { - i64s[i] = mathutil.MustInt64(v) + i64s[i] = mathutil.QuietInt64(v) } return i64s } +// StringsAsInts convert and ignore error +func StringsAsInts(ss []string) []int { + ints, _ := StringsTryInts(ss) + return ints +} + +// StringsToInts string slice to int slice +func StringsToInts(ss []string) (ints []int, err error) { + return StringsTryInts(ss) +} + +// StringsTryInts string slice to int slice +func StringsTryInts(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 +} + +// AnyToSlice convert any(allow: array,slice) to []any +func AnyToSlice(sl any) (ls []any, err error) { + rfKeys := reflect.ValueOf(sl) + if rfKeys.Kind() != reflect.Slice && rfKeys.Kind() != reflect.Array { + return nil, ErrInvalidType + } + + for i := 0; i < rfKeys.Len(); i++ { + ls = append(ls, rfKeys.Index(i).Interface()) + } + return +} + +// AnyToStrings convert array or slice to []string +func AnyToStrings(arr any) []string { + ret, _ := ToStrings(arr) + return ret +} + +// MustToStrings convert array or slice to []string +func MustToStrings(arr any) []string { + ret, err := ToStrings(arr) + if err != nil { + panic(err) + } + 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 +} + // ToStrings convert any(allow: array,slice) to []string func ToStrings(arr any) (ret []string, err error) { rv := reflect.ValueOf(arr) + if rv.Kind() == reflect.String { + return []string{rv.String()}, nil + } + if rv.Kind() != reflect.Slice && rv.Kind() != reflect.Array { err = ErrInvalidType return @@ -114,13 +170,47 @@ func ToStrings(arr any) (ret []string, err error) { // SliceToStrings convert []any to []string func SliceToStrings(arr []any) []string { + return QuietStrings(arr) +} + +// QuietStrings convert []any to []string +func QuietStrings(arr []any) []string { ss := make([]string, len(arr)) for i, v := range arr { - ss[i] = strutil.MustString(v) + ss[i] = strutil.QuietString(v) } return ss } +// ConvType convert type of slice elements to new type slice, by the given newElemTyp type. +// +// Supports conversion between []string, []intX, []uintX, []floatX. +// +// Usage: +// +// ints, _ := arrutil.ConvType([]string{"12", "23"}, 1) // []int{12, 23} +func ConvType[T any, R any](arr []T, newElemTyp R) ([]R, error) { + newArr := make([]R, len(arr)) + elemTyp := reflect.TypeOf(newElemTyp) + + for i, elem := range arr { + var anyElem any = elem + // type is same. + if _, ok := anyElem.(R); ok { + newArr[i] = anyElem.(R) + continue + } + + // need conv type. + rfVal, err := reflects.ValueByType(elem, elemTyp) + if err != nil { + return nil, err + } + newArr[i] = rfVal.Interface().(R) + } + return newArr, nil +} + // AnyToString simple and quickly convert any array, slice to string func AnyToString(arr any) string { return NewFormatter(arr).Format() @@ -143,26 +233,40 @@ func ToString(arr []any) string { if i > 0 { sb.WriteByte(',') } - sb.WriteString(strutil.MustString(v)) + sb.WriteString(strutil.QuietString(v)) } sb.WriteByte(']') return sb.String() } -// JoinSlice join []any slice to string. -func JoinSlice(sep string, arr ...any) string { - if arr == nil { - return "" - } +// CombineToMap combine two slice to map[K]V. +// +// If keys length is greater than values, the extra keys will be ignored. +func CombineToMap[K comdef.SortedType, V any](keys []K, values []V) map[K]V { + ln := len(values) + mp := make(map[K]V, len(keys)) - var sb strings.Builder - for i, v := range arr { - if i > 0 { - sb.WriteString(sep) + for i, key := range keys { + if i >= ln { + break } - sb.WriteString(strutil.MustString(v)) + mp[key] = values[i] } - - return sb.String() + return mp +} + +// CombineToSMap combine two string-slice to map[string]string +func CombineToSMap(keys, values []string) map[string]string { + ln := len(values) + mp := make(map[string]string, len(keys)) + + for i, key := range keys { + if ln > i { + mp[key] = values[i] + } else { + mp[key] = "" + } + } + return mp } diff --git a/vendor/github.com/gookit/goutil/arrutil/enum.go b/vendor/github.com/gookit/goutil/arrutil/enum.go index 5897b2218..a5a70f0ec 100644 --- a/vendor/github.com/gookit/goutil/arrutil/enum.go +++ b/vendor/github.com/gookit/goutil/arrutil/enum.go @@ -42,6 +42,11 @@ func (ss Strings) Join(sep string) string { // Has given element func (ss Strings) Has(sub string) bool { + return ss.Contains(sub) +} + +// Contains given element +func (ss Strings) Contains(sub string) bool { for _, s := range ss { if s == sub { return true @@ -49,3 +54,11 @@ func (ss Strings) Has(sub string) bool { } return false } + +// First element value. +func (ss Strings) First() string { + if len(ss) > 0 { + return ss[0] + } + return "" +} diff --git a/vendor/github.com/gookit/goutil/basefn/basefunc.go b/vendor/github.com/gookit/goutil/basefn/basefunc.go new file mode 100644 index 000000000..decc3187d --- /dev/null +++ b/vendor/github.com/gookit/goutil/basefn/basefunc.go @@ -0,0 +1,80 @@ +// Package basefn provide some no-dependents util functions +package basefn + +import "fmt" + +// Panicf format panic message use fmt.Sprintf +func Panicf(format string, v ...any) { + panic(fmt.Sprintf(format, v...)) +} + +// MustOK if error is not empty, will panic +func MustOK(err error) { + if err != nil { + panic(err) + } +} + +// Must if error is not empty, will panic +func Must[T any](v T, err error) T { + if err != nil { + panic(err) + } + return v +} + +// ErrOnFail return input error on cond is false, otherwise return nil +func ErrOnFail(cond bool, err error) error { + return OrError(cond, err) +} + +// OrError return input error on cond is false, otherwise return nil +func OrError(cond bool, err error) error { + if !cond { + return err + } + return nil +} + +// FirstOr get first elem or elseVal +func FirstOr[T any](sl []T, elseVal T) T { + if len(sl) > 0 { + return sl[0] + } + return elseVal +} + +// OrValue get +func OrValue[T any](cond bool, okVal, elVal T) T { + if cond { + return okVal + } + return elVal +} + +// OrReturn call okFunc() on condition is true, else call elseFn() +func OrReturn[T any](cond bool, okFn, elseFn func() T) T { + if cond { + return okFn() + } + return elseFn() +} + +// ErrFunc type +type ErrFunc func() error + +// CallOn call func on condition is true +func CallOn(cond bool, fn ErrFunc) error { + if cond { + return fn() + } + return nil +} + +// CallOrElse call okFunc() on condition is true, else call elseFn() +func CallOrElse(cond bool, okFn, elseFn ErrFunc) error { + if cond { + return okFn() + } + return elseFn() +} diff --git a/vendor/github.com/gookit/goutil/fmtutil/time.go b/vendor/github.com/gookit/goutil/basefn/extfunc.go similarity index 62% rename from vendor/github.com/gookit/goutil/fmtutil/time.go rename to vendor/github.com/gookit/goutil/basefn/extfunc.go index d6ccfc4d6..15be452cf 100644 --- a/vendor/github.com/gookit/goutil/fmtutil/time.go +++ b/vendor/github.com/gookit/goutil/basefn/extfunc.go @@ -1,6 +1,28 @@ -package fmtutil +package basefn -import "fmt" +import ( + "fmt" +) + +// DataSize format bytes number friendly. eg: 1024 => 1KB, 1024*1024 => 1MB +// +// Usage: +// +// file, err := os.Open(path) +// fl, err := file.Stat() +// fmtSize := DataSize(fl.Size()) +func DataSize(size uint64) string { + switch { + 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(size)/1024/1024/1024) + } +} var timeFormats = [][]int{ {0}, diff --git a/vendor/github.com/gookit/goutil/byteutil/README.md b/vendor/github.com/gookit/goutil/byteutil/README.md new file mode 100644 index 000000000..89147dc7c --- /dev/null +++ b/vendor/github.com/gookit/goutil/byteutil/README.md @@ -0,0 +1,55 @@ +# Bytes Util + +Provide some commonly bytes util functions. + +## Install + +```shell +go get github.com/gookit/goutil/byteutil +``` + +## Go docs + +- [Go docs](https://pkg.go.dev/github.com/gookit/goutil/byteutil) + +## Functions API + +> **Note**: doc by run `go doc ./byteutil` + +```go +func AppendAny(dst []byte, v any) []byte +func FirstLine(bs []byte) []byte +func IsNumChar(c byte) bool +func Md5(src any) []byte +func Random(length int) ([]byte, error) +func SafeString(bs []byte, err error) string +func StrOrErr(bs []byte, err error) (string, error) +func String(b []byte) string +func ToString(b []byte) string +type Buffer struct{ ... } +func NewBuffer() *Buffer +type BytesEncoder interface{ ... } +type ChanPool struct{ ... } +func NewChanPool(maxSize int, width int, capWidth int) *ChanPool +type StdEncoder struct{ ... } +func NewStdEncoder(encFn func(src []byte) []byte, decFn func(src []byte) ([]byte, error)) *StdEncoder +``` + +## Code Check & Testing + +```bash +gofmt -w -l ./ +golint ./... +``` + +**Testing**: + +```shell +go test -v ./byteutil/... +``` + +**Test limit by regexp**: + +```shell +go test -v -run ^TestSetByKeys ./byteutil/... +``` diff --git a/vendor/github.com/gookit/goutil/byteutil/buffer.go b/vendor/github.com/gookit/goutil/byteutil/buffer.go new file mode 100644 index 000000000..c0a6917fe --- /dev/null +++ b/vendor/github.com/gookit/goutil/byteutil/buffer.go @@ -0,0 +1,65 @@ +package byteutil + +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 +} diff --git a/vendor/github.com/gookit/goutil/byteutil/byteutil.go b/vendor/github.com/gookit/goutil/byteutil/byteutil.go new file mode 100644 index 000000000..45c394556 --- /dev/null +++ b/vendor/github.com/gookit/goutil/byteutil/byteutil.go @@ -0,0 +1,115 @@ +package byteutil + +import ( + "bytes" + "fmt" + "math/rand" + "strconv" + "time" + "unsafe" +) + +// Random bytes generate +func Random(length int) ([]byte, error) { + b := make([]byte, length) + // Note that err == nil only if we read len(b) bytes. + if _, err := rand.Read(b); err != nil { + return nil, err + } + return b, nil +} + +// FirstLine from command output +func FirstLine(bs []byte) []byte { + if i := bytes.IndexByte(bs, '\n'); i >= 0 { + return bs[0:i] + } + return bs +} + +// StrOrErr convert to string, return empty string on error. +func StrOrErr(bs []byte, err error) (string, error) { + if err != nil { + return "", err + } + return string(bs), err +} + +// SafeString convert to string, return empty string on error. +func SafeString(bs []byte, err error) string { + if err != nil { + return "" + } + return string(bs) +} + +// String unsafe convert bytes to string +func String(b []byte) string { + return *(*string)(unsafe.Pointer(&b)) +} + +// ToString convert bytes to string +func ToString(b []byte) string { + return *(*string)(unsafe.Pointer(&b)) +} + +// AppendAny append any value to byte slice +func AppendAny(dst []byte, v any) []byte { + if v == nil { + return append(dst, ""...) + } + + switch val := v.(type) { + case []byte: + dst = append(dst, val...) + case string: + dst = append(dst, val...) + case int: + dst = strconv.AppendInt(dst, int64(val), 10) + case int8: + dst = strconv.AppendInt(dst, int64(val), 10) + case int16: + dst = strconv.AppendInt(dst, int64(val), 10) + case int32: + dst = strconv.AppendInt(dst, int64(val), 10) + case int64: + dst = strconv.AppendInt(dst, val, 10) + case uint: + dst = strconv.AppendUint(dst, uint64(val), 10) + case uint8: + dst = strconv.AppendUint(dst, uint64(val), 10) + case uint16: + dst = strconv.AppendUint(dst, uint64(val), 10) + case uint32: + dst = strconv.AppendUint(dst, uint64(val), 10) + case uint64: + dst = strconv.AppendUint(dst, val, 10) + case float32: + dst = strconv.AppendFloat(dst, float64(val), 'f', -1, 32) + case float64: + dst = strconv.AppendFloat(dst, val, 'f', -1, 64) + case bool: + dst = strconv.AppendBool(dst, val) + case time.Time: + dst = val.AppendFormat(dst, time.RFC3339) + case time.Duration: + dst = strconv.AppendInt(dst, int64(val), 10) + case error: + dst = append(dst, val.Error()...) + case fmt.Stringer: + dst = append(dst, val.String()...) + default: + dst = append(dst, fmt.Sprint(v)...) + } + return dst +} + +// Cut bytes. like the strings.Cut() +func Cut(bs []byte, sep byte) (before, after []byte, found bool) { + if i := bytes.IndexByte(bs, sep); i >= 0 { + return bs[:i], bs[i+1:], true + } + + before = bs + return +} diff --git a/vendor/github.com/gookit/goutil/byteutil/bytex.go b/vendor/github.com/gookit/goutil/byteutil/bytex.go new file mode 100644 index 000000000..e66ea61f5 --- /dev/null +++ b/vendor/github.com/gookit/goutil/byteutil/bytex.go @@ -0,0 +1,19 @@ +// Package byteutil Provide some bytes utils functions or structs +package byteutil + +import ( + "crypto/md5" + "fmt" +) + +// Md5 Generate a 32-bit md5 bytes +func Md5(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) +} diff --git a/vendor/github.com/gookit/goutil/byteutil/check.go b/vendor/github.com/gookit/goutil/byteutil/check.go new file mode 100644 index 000000000..7be9e4b4b --- /dev/null +++ b/vendor/github.com/gookit/goutil/byteutil/check.go @@ -0,0 +1,4 @@ +package byteutil + +// IsNumChar returns true if the given character is a numeric, otherwise false. +func IsNumChar(c byte) bool { return c >= '0' && c <= '9' } diff --git a/vendor/github.com/gookit/goutil/byteutil/encoder.go b/vendor/github.com/gookit/goutil/byteutil/encoder.go new file mode 100644 index 000000000..8ec965e5d --- /dev/null +++ b/vendor/github.com/gookit/goutil/byteutil/encoder.go @@ -0,0 +1,63 @@ +package byteutil + +import ( + "encoding/base64" + "encoding/hex" +) + +// BytesEncoder interface +type BytesEncoder interface { + Encode(src []byte) []byte + Decode(src []byte) ([]byte, error) +} + +// StdEncoder implement the BytesEncoder +type StdEncoder struct { + encodeFn func(src []byte) []byte + decodeFn func(src []byte) ([]byte, error) +} + +// NewStdEncoder instance +func NewStdEncoder(encFn func(src []byte) []byte, decFn func(src []byte) ([]byte, error)) *StdEncoder { + return &StdEncoder{ + encodeFn: encFn, + decodeFn: decFn, + } +} + +// Encode input +func (e *StdEncoder) Encode(src []byte) []byte { + return e.encodeFn(src) +} + +// Decode input +func (e *StdEncoder) Decode(src []byte) ([]byte, error) { + return e.decodeFn(src) +} + +var ( + // HexEncoder instance + HexEncoder = NewStdEncoder(func(src []byte) []byte { + dst := make([]byte, hex.EncodedLen(len(src))) + hex.Encode(dst, src) + return dst + }, func(src []byte) ([]byte, error) { + n, err := hex.Decode(src, src) + return src[:n], err + }) + + // B64Encoder instance + B64Encoder = NewStdEncoder(func(src []byte) []byte { + b64Dst := make([]byte, base64.StdEncoding.EncodedLen(len(src))) + base64.StdEncoding.Encode(b64Dst, src) + return b64Dst + }, func(src []byte) ([]byte, error) { + dBuf := make([]byte, base64.StdEncoding.DecodedLen(len(src))) + n, err := base64.StdEncoding.Decode(dBuf, src) + if err != nil { + return nil, err + } + + return dBuf[:n], err + }) +) diff --git a/vendor/github.com/gookit/goutil/byteutil/pool.go b/vendor/github.com/gookit/goutil/byteutil/pool.go new file mode 100644 index 000000000..c6f09e018 --- /dev/null +++ b/vendor/github.com/gookit/goutil/byteutil/pool.go @@ -0,0 +1,64 @@ +package byteutil + +// ChanPool struct +// +// Usage: +// +// bp := strutil.NewByteChanPool(500, 1024, 1024) +// buf:=bp.Get() +// defer bp.Put(buf) +// // use buf do something ... +// +// refer https://www.flysnow.org/2020/08/21/golang-chan-byte-pool.html +// from https://github.com/minio/minio/blob/master/internal/bpool/bpool.go +type ChanPool struct { + c chan []byte + w int + wcap int +} + +// NewChanPool instance +func NewChanPool(maxSize int, width int, capWidth int) *ChanPool { + return &ChanPool{ + c: make(chan []byte, maxSize), + w: width, + wcap: capWidth, + } +} + +// Get gets a []byte from the BytePool, or creates a new one if none are +// available in the pool. +func (bp *ChanPool) Get() (b []byte) { + select { + case b = <-bp.c: + // reuse existing buffer + default: + // create new buffer + if bp.wcap > 0 { + b = make([]byte, bp.w, bp.wcap) + } else { + b = make([]byte, bp.w) + } + } + return +} + +// Put returns the given Buffer to the BytePool. +func (bp *ChanPool) Put(b []byte) { + select { + case bp.c <- b: + // buffer went back into pool + default: + // buffer didn't go back into pool, just discard + } +} + +// Width returns the width of the byte arrays in this pool. +func (bp *ChanPool) Width() (n int) { + return bp.w +} + +// WidthCap returns the cap width of the byte arrays in this pool. +func (bp *ChanPool) WidthCap() (n int) { + return bp.wcap +} diff --git a/vendor/github.com/gookit/goutil/cliutil/cmdline/builder.go b/vendor/github.com/gookit/goutil/cliutil/cmdline/builder.go index 41cf7f32c..1dceeec77 100644 --- a/vendor/github.com/gookit/goutil/cliutil/cmdline/builder.go +++ b/vendor/github.com/gookit/goutil/cliutil/cmdline/builder.go @@ -2,27 +2,28 @@ package cmdline import ( "strings" + + "github.com/gookit/goutil/strutil" ) // LineBuilder build command line string. // codes refer from strings.Builder type LineBuilder struct { - buf []byte + strings.Builder } // NewBuilder create func NewBuilder(binFile string, args ...string) *LineBuilder { b := &LineBuilder{} - b.AddArg(binFile) + + if binFile != "" { + b.AddArg(binFile) + } + b.AddArray(args) return b } -// LineBuild build command line string by given args. -func LineBuild(binFile string, args []string) string { - return NewBuilder(binFile, args...).String() -} - // AddArg to builder func (b *LineBuilder) AddArg(arg string) { _, _ = b.WriteString(arg) @@ -40,52 +41,45 @@ func (b *LineBuilder) AddArray(args []string) { } } +// AddAny args to builder +func (b *LineBuilder) AddAny(args ...any) { + for _, arg := range args { + _, _ = b.WriteString(strutil.SafeString(arg)) + } +} + // WriteString arg string to the builder, will auto quote special string. // refer strconv.Quote() func (b *LineBuilder) WriteString(a string) (int, error) { var quote byte - if strings.ContainsRune(a, '"') { + if pos := strings.IndexByte(a, '"'); pos > -1 { quote = '\'' - } else if a == "" || strings.ContainsRune(a, '\'') || strings.ContainsRune(a, ' ') { + // fix: a = `--pretty=format:"one two three"` + if pos > 0 && '"' == a[len(a)-1] { + quote = 0 + } + } else if pos := strings.IndexByte(a, '\''); pos > -1 { + quote = '"' + // fix: a = "--pretty=format:'one two three'" + if pos > 0 && '\'' == a[len(a)-1] { + quote = 0 + } + } else if a == "" || strings.ContainsRune(a, ' ') { quote = '"' } - // add sep on first write. - if b.buf != nil { - b.buf = append(b.buf, ' ') + // add sep on not-first write. + if b.Len() != 0 { + _ = b.WriteByte(' ') } - // no quote char + // no quote char OR not need quote if quote == 0 { - b.buf = append(b.buf, a...) - return len(a) + 1, nil + return b.Builder.WriteString(a) } - b.buf = append(b.buf, quote) // add start quote - b.buf = append(b.buf, a...) - b.buf = append(b.buf, quote) // add end quote - return len(a) + 3, nil + _ = b.WriteByte(quote) // add start quote + n, err := b.Builder.WriteString(a) + _ = b.WriteByte(quote) // add end quote + return n, err } - -// String to command line string -func (b *LineBuilder) String() string { - return string(b.buf) -} - -// Len of the builder -func (b *LineBuilder) Len() int { - return len(b.buf) -} - -// Reset builder -func (b *LineBuilder) Reset() { - b.buf = nil -} - -// grow copies the buffer to a new, larger buffer so that there are at least n -// bytes of capacity beyond len(b.buf). -// func (b *LineBuilder) grow(n int) { -// buf := make([]byte, len(b.buf), 2*cap(b.buf)+n) -// copy(buf, b.buf) -// b.buf = buf -// } diff --git a/vendor/github.com/gookit/goutil/cliutil/cmdline/cmdline.go b/vendor/github.com/gookit/goutil/cliutil/cmdline/cmdline.go index 6dbebc50d..d048720fe 100644 --- a/vendor/github.com/gookit/goutil/cliutil/cmdline/cmdline.go +++ b/vendor/github.com/gookit/goutil/cliutil/cmdline/cmdline.go @@ -1,2 +1,12 @@ // Package cmdline provide quick build and parse cmd line string. package cmdline + +// LineBuild build command line string by given args. +func LineBuild(binFile string, args []string) string { + return NewBuilder(binFile, args...).String() +} + +// ParseLine input command line text. alias of the StringToOSArgs() +func ParseLine(line string) []string { + return NewParser(line).Parse() +} diff --git a/vendor/github.com/gookit/goutil/cliutil/cmdline/parser.go b/vendor/github.com/gookit/goutil/cliutil/cmdline/parser.go index 0e4ffa621..e59f0928c 100644 --- a/vendor/github.com/gookit/goutil/cliutil/cmdline/parser.go +++ b/vendor/github.com/gookit/goutil/cliutil/cmdline/parser.go @@ -1,9 +1,13 @@ package cmdline import ( - "os" + "bytes" "os/exec" "strings" + + "github.com/gookit/goutil/comdef" + "github.com/gookit/goutil/internal/comfunc" + "github.com/gookit/goutil/strutil" ) // LineParser struct @@ -11,7 +15,7 @@ import ( type LineParser struct { parsed bool // Line the full input command line text - // eg `kite top sub -a "the a message" --foo val1 --bar "val 2"` + // eg `kite top sub -a "this is a message" --foo val1 --bar "val 2"` Line string // ParseEnv parse ENV var on the line. ParseEnv bool @@ -19,6 +23,11 @@ type LineParser struct { nodes []string // the parsed args args []string + + // temp value + quoteChar byte + quoteIndex int // if > 0, mark is not on start + tempNode bytes.Buffer } // NewParser create @@ -26,11 +35,10 @@ func NewParser(line string) *LineParser { return &LineParser{Line: line} } -// ParseLine input command line text. alias of the StringToOSArgs() -func ParseLine(line string) []string { - p := &LineParser{Line: line} - - return p.Parse() +// WithParseEnv with parse ENV var +func (p *LineParser) WithParseEnv() *LineParser { + p.ParseEnv = true + return p } // AlsoEnvParse input command line text to os.Args, will parse ENV var @@ -39,90 +47,13 @@ func (p *LineParser) AlsoEnvParse() []string { return p.Parse() } -// Parse input command line text to os.Args -func (p *LineParser) Parse() []string { - if p.parsed { - return p.args - } +// NewExecCmd quick create exec.Cmd by cmdline string +func (p *LineParser) NewExecCmd() *exec.Cmd { + // parse get bin and args + binName, args := p.BinAndArgs() - p.parsed = true - p.Line = strings.TrimSpace(p.Line) - if p.Line == "" { - return p.args - } - - // enable parse Env var - if p.ParseEnv { - p.Line = os.ExpandEnv(p.Line) - } - - p.nodes = strings.Split(p.Line, " ") - if len(p.nodes) == 1 { - p.args = p.nodes - return p.args - } - - // temp value - var quoteChar, fullNode string - for _, node := range p.nodes { - if node == "" { - continue - } - - nodeLen := len(node) - start, end := node[:1], node[nodeLen-1:] - - var clearTemp bool - if start == "'" || start == `"` { - noStart := node[1:] - if quoteChar == "" { // start - // only one words. eg: `-m "msg"` - if end == start { - p.args = append(p.args, node[1:nodeLen-1]) - continue - } - - fullNode += noStart - quoteChar = start - } else if quoteChar == start { // invalid. eg: `-m "this is "message` `-m "this is "message"` - p.appendWithPrefix(strings.Trim(node, quoteChar), fullNode) - clearTemp = true // clear temp value - } else if quoteChar == end { // eg: `"has inner 'quote'"` - p.appendWithPrefix(node[:nodeLen-1], fullNode) - clearTemp = true // clear temp value - } else { // goon. eg: `-m "the 'some' message"` - fullNode += " " + node - } - } else if end == "'" || end == `"` { - noEnd := node[:nodeLen-1] - if quoteChar == "" { // end - p.appendWithPrefix(noEnd, fullNode) - clearTemp = true // clear temp value - } else if quoteChar == end { // end - p.appendWithPrefix(noEnd, fullNode) - clearTemp = true // clear temp value - } else { // goon. eg: `-m "the 'some' message"` - fullNode += " " + node - } - } else { - if quoteChar != "" { - fullNode += " " + node - } else { - p.args = append(p.args, node) - } - } - - // clear temp value - if clearTemp { - quoteChar, fullNode = "", "" - } - } - - if fullNode != "" { - p.args = append(p.args, fullNode) - } - - return p.args + // create a new Cmd instance + return exec.Command(binName, args...) } // BinAndArgs get binName and args @@ -141,19 +72,103 @@ func (p *LineParser) BinAndArgs() (bin string, args []string) { return } -// NewExecCmd quick create exec.Cmd by cmdline string -func (p *LineParser) NewExecCmd() *exec.Cmd { - // parse get bin and args - binName, args := p.BinAndArgs() +// Parse input command line text to os.Args +func (p *LineParser) Parse() []string { + if p.parsed { + return p.args + } - // create a new Cmd instance - return exec.Command(binName, args...) + p.parsed = true + p.Line = strings.TrimSpace(p.Line) + if p.Line == "" { + return p.args + } + + // enable parse Env var + if p.ParseEnv { + p.Line = comfunc.ParseEnvVar(p.Line, nil) + } + + p.nodes = strings.Split(p.Line, " ") + if len(p.nodes) == 1 { + p.args = p.nodes + return p.args + } + + for i := 0; i < len(p.nodes); i++ { + node := p.nodes[i] + if node == "" { + continue + } + + p.parseNode(node) + } + + p.nodes = p.nodes[:0] + if p.tempNode.Len() > 0 { + p.appendTempNode() + } + return p.args } -func (p *LineParser) appendWithPrefix(node, prefix string) { - if prefix != "" { - p.args = append(p.args, prefix+" "+node) +func (p *LineParser) parseNode(node string) { + maxIdx := len(node) - 1 + start, end := node[0], node[maxIdx] + + // in quotes + if p.quoteChar != 0 { + p.tempNode.WriteByte(' ') + + // end quotes + if end == p.quoteChar { + if p.quoteIndex > 0 { + p.tempNode.WriteString(node) // eg: node="--pretty=format:'one two'" + } else { + p.tempNode.WriteString(node[:maxIdx]) // remove last quote + } + p.appendTempNode() + } else { // goon ... write to temp node + p.tempNode.WriteString(node) + } + return + } + + // quote start + if start == comdef.DoubleQuote || start == comdef.SingleQuote { + // only one words. eg: `-m "msg"` + if end == start { + p.args = append(p.args, node[1:maxIdx]) + return + } + + p.quoteChar = start + p.tempNode.WriteString(node[1:]) + } else if end == comdef.DoubleQuote || end == comdef.SingleQuote { + p.args = append(p.args, node) // only one node: `msg"` } else { - p.args = append(p.args, node) + // eg: --pretty=format:'one two three' + if strutil.ContainsByte(node, comdef.DoubleQuote) { + p.quoteIndex = 1 // mark is not on start + p.quoteChar = comdef.DoubleQuote + } else if strutil.ContainsByte(node, comdef.SingleQuote) { + p.quoteIndex = 1 + p.quoteChar = comdef.SingleQuote + } + + // in quote, append to temp-node + if p.quoteChar != 0 { + p.tempNode.WriteString(node) + } else { + p.args = append(p.args, node) + } } } + +func (p *LineParser) appendTempNode() { + p.args = append(p.args, p.tempNode.String()) + + // reset context value + p.quoteChar = 0 + p.quoteIndex = 0 + p.tempNode.Reset() +} diff --git a/vendor/github.com/gookit/goutil/comdef/consts.go b/vendor/github.com/gookit/goutil/comdef/consts.go index 1534a196b..5f1cf2b1f 100644 --- a/vendor/github.com/gookit/goutil/comdef/consts.go +++ b/vendor/github.com/gookit/goutil/comdef/consts.go @@ -23,3 +23,5 @@ const ( // NoIdx invalid index or length const NoIdx = -1 + +// const VarPathReg = `(\w[\w-]*(?:\.[\w-]+)*)` diff --git a/vendor/github.com/gookit/goutil/comdef/types.go b/vendor/github.com/gookit/goutil/comdef/types.go index 0a26e2903..2fe167f2d 100644 --- a/vendor/github.com/gookit/goutil/comdef/types.go +++ b/vendor/github.com/gookit/goutil/comdef/types.go @@ -7,12 +7,11 @@ type Int interface { // Uint interface type type Uint interface { - ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr } // 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 } @@ -26,11 +25,19 @@ type IntOrFloat interface { Int | Float } -// XintOrFloat interface type. all (x)int and float types +// XintOrFloat interface type. all int, uint and float types type XintOrFloat interface { Int | Uint | Float } +// SortedType interface type. +// that supports the operators < <= >= >. +// +// contains: (x)int, float, ~string types +type SortedType interface { + Int | Uint | Float | ~string +} + // ScalarType interface type. // // contains: (x)int, float, ~string, ~bool types diff --git a/vendor/github.com/gookit/goutil/envutil/README.md b/vendor/github.com/gookit/goutil/envutil/README.md index 1f727c7e1..85d18c90b 100644 --- a/vendor/github.com/gookit/goutil/envutil/README.md +++ b/vendor/github.com/gookit/goutil/envutil/README.md @@ -14,6 +14,8 @@ go get github.com/gookit/goutil/envutil ## Functions API +> **Note**: doc by run `go doc ./envutil` + ```go func Environ() map[string]string func GetBool(name string, def ...bool) bool diff --git a/vendor/github.com/gookit/goutil/envutil/envutil.go b/vendor/github.com/gookit/goutil/envutil/envutil.go index 9680cefdd..f9a312da2 100644 --- a/vendor/github.com/gookit/goutil/envutil/envutil.go +++ b/vendor/github.com/gookit/goutil/envutil/envutil.go @@ -1,3 +1,4 @@ +// Package envutil provide some commonly ENV util functions. package envutil import ( @@ -41,9 +42,27 @@ func ParseValue(val string) (newVal string) { return comfunc.ParseEnvVar(val, ValueGetter) } -// SetEnvs to os -func SetEnvs(mp map[string]string) { +// SetEnvMap set multi ENV(string-map) to os +func SetEnvMap(mp map[string]string) { for key, value := range mp { _ = os.Setenv(key, value) } } + +// SetEnvs set multi k-v ENV pairs to os +func SetEnvs(kvPairs ...string) { + if len(kvPairs)%2 == 1 { + panic("envutil.SetEnvs: odd argument count") + } + + for i := 0; i < len(kvPairs); i += 2 { + _ = os.Setenv(kvPairs[i], kvPairs[i+1]) + } +} + +// UnsetEnvs from os +func UnsetEnvs(keys ...string) { + for _, key := range keys { + _ = os.Unsetenv(key) + } +} diff --git a/vendor/github.com/gookit/goutil/envutil/get.go b/vendor/github.com/gookit/goutil/envutil/get.go index 19b7ee439..6250c5b38 100644 --- a/vendor/github.com/gookit/goutil/envutil/get.go +++ b/vendor/github.com/gookit/goutil/envutil/get.go @@ -2,6 +2,7 @@ package envutil import ( "os" + "path/filepath" "github.com/gookit/goutil/internal/comfunc" "github.com/gookit/goutil/strutil" @@ -40,7 +41,45 @@ func GetBool(name string, def ...bool) bool { return false } -// Environ like os.Environ, but will returns key-value map[string]string data. -func Environ() map[string]string { - return comfunc.Environ() +// GetMulti ENV values by input names. +func GetMulti(names ...string) map[string]string { + valMap := make(map[string]string, len(names)) + + for _, name := range names { + if val := os.Getenv(name); val != "" { + valMap[name] = val + } + } + return valMap +} + +// EnvPaths get and split $PATH to []string +func EnvPaths() []string { + return filepath.SplitList(os.Getenv("PATH")) +} + +// EnvMap like os.Environ, but will returns key-value map[string]string data. +func EnvMap() map[string]string { return comfunc.Environ() } + +// Environ like os.Environ, but will returns key-value map[string]string data. +func Environ() map[string]string { return comfunc.Environ() } + +// SearchEnvKeys values by given keywords +func SearchEnvKeys(keywords string) map[string]string { + return SearchEnv(keywords, false) +} + +// SearchEnv values by given keywords +func SearchEnv(keywords string, matchValue bool) map[string]string { + founded := make(map[string]string) + + for name, val := range comfunc.Environ() { + if strutil.IContains(name, keywords) { + founded[name] = val + } else if matchValue && strutil.IContains(val, keywords) { + founded[name] = val + } + } + + return founded } diff --git a/vendor/github.com/gookit/goutil/errorx/assert.go b/vendor/github.com/gookit/goutil/errorx/assert.go new file mode 100644 index 000000000..696b45885 --- /dev/null +++ b/vendor/github.com/gookit/goutil/errorx/assert.go @@ -0,0 +1,61 @@ +package errorx + +import ( + "errors" + "fmt" + + "github.com/gookit/goutil/arrutil" + "github.com/gookit/goutil/comdef" + "github.com/gookit/goutil/internal/comfunc" +) + +// IsTrue assert result is true, otherwise will return error +func IsTrue(result bool, fmtAndArgs ...any) error { + if !result { + return errors.New(formatErrMsg("result should be True", fmtAndArgs)) + } + return nil +} + +// IsFalse assert result is false, otherwise will return error +func IsFalse(result bool, fmtAndArgs ...any) error { + if result { + return errors.New(formatErrMsg("result should be False", fmtAndArgs)) + } + return nil +} + +// IsIn value should be in the list, otherwise will return error +func IsIn[T comdef.ScalarType](value T, list []T, fmtAndArgs ...any) error { + if arrutil.NotIn(value, list) { + var errMsg string + if len(fmtAndArgs) > 0 { + errMsg = comfunc.FormatTplAndArgs(fmtAndArgs) + } else { + errMsg = fmt.Sprintf("value should be in the %v", list) + } + return errors.New(errMsg) + } + return nil +} + +// NotIn value should not be in the list, otherwise will return error +func NotIn[T comdef.ScalarType](value T, list []T, fmtAndArgs ...any) error { + if arrutil.In(value, list) { + var errMsg string + if len(fmtAndArgs) > 0 { + errMsg = comfunc.FormatTplAndArgs(fmtAndArgs) + } else { + errMsg = fmt.Sprintf("value should not be in the %v", list) + } + return errors.New(errMsg) + } + return nil +} + +func formatErrMsg(errMsg string, fmtAndArgs []any) string { + if len(fmtAndArgs) > 0 { + errMsg = comfunc.FormatTplAndArgs(fmtAndArgs) + } + return errMsg +} diff --git a/vendor/github.com/gookit/goutil/errorx/errors.go b/vendor/github.com/gookit/goutil/errorx/errors.go index a87c2b3c7..0dffef5d4 100644 --- a/vendor/github.com/gookit/goutil/errorx/errors.go +++ b/vendor/github.com/gookit/goutil/errorx/errors.go @@ -37,6 +37,11 @@ func Fail(code int, msg string) ErrorR { return &errorR{code: code, msg: msg} } +// Failf code with error response +func Failf(code int, tpl string, v ...any) ErrorR { + return &errorR{code: code, msg: fmt.Sprintf(tpl, v...)} +} + // Suc success response reply func Suc(msg string) ErrorR { return &errorR{code: 0, msg: msg} @@ -110,6 +115,8 @@ func (e ErrMap) One() error { // Errors multi error list type Errors []error + +// ErrList alias for Errors type ErrList = Errors // Error string diff --git a/vendor/github.com/gookit/goutil/errorx/util.go b/vendor/github.com/gookit/goutil/errorx/util.go index 54cf9ea60..8558785a2 100644 --- a/vendor/github.com/gookit/goutil/errorx/util.go +++ b/vendor/github.com/gookit/goutil/errorx/util.go @@ -5,11 +5,31 @@ import ( "fmt" ) +// E new a raw go error. alias of errors.New() +func E(msg string) error { + return errors.New(msg) +} + +// Err new a raw go error. alias of errors.New() +func Err(msg string) error { + return errors.New(msg) +} + // Raw new a raw go error. alias of errors.New() func Raw(msg string) error { return errors.New(msg) } +// Ef new a raw go error. alias of errors.New() +func Ef(tpl string, vars ...any) error { + return fmt.Errorf(tpl, vars...) +} + +// Errf new a raw go error. alias of errors.New() +func Errf(tpl string, vars ...any) error { + return fmt.Errorf(tpl, vars...) +} + // Rawf new a raw go error. alias of errors.New() func Rawf(tpl string, vars ...any) error { return fmt.Errorf(tpl, vars...) @@ -54,7 +74,7 @@ func ToErrorX(err error) (ex *ErrorX, ok bool) { return } -// Has check err has contains target, or err is eq target. +// Has contains target error, or err is eq target. // alias of errors.Is() func Has(err, target error) bool { return errors.Is(err, target) diff --git a/vendor/github.com/gookit/goutil/fmtutil/fmtutil.go b/vendor/github.com/gookit/goutil/fmtutil/fmtutil.go deleted file mode 100644 index fcfd27ddd..000000000 --- a/vendor/github.com/gookit/goutil/fmtutil/fmtutil.go +++ /dev/null @@ -1,2 +0,0 @@ -// 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 deleted file mode 100644 index a403d40ce..000000000 --- a/vendor/github.com/gookit/goutil/fmtutil/format.go +++ /dev/null @@ -1,116 +0,0 @@ -package fmtutil - -import ( - "encoding/json" - "fmt" - "strconv" - "strings" - "unicode" -) - -// data size -const ( - OneKByte = 1024 - OneMByte = 1024 * 1024 - OneGByte = 1024 * 1024 -) - -// DataSize format bytes number friendly. -// -// Usage: -// -// file, err := os.Open(path) -// fl, err := file.Stat() -// fmtSize := DataSize(fl.Size()) -func DataSize(size uint64) string { - switch { - 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(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 any) (string, error) { - out, err := json.MarshalIndent(v, "", " ") - return string(out), err -} - -// 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) - if err != nil { - return []int{}, err - } - - ints = append(ints, iVal) - } - return -} - -// ArgsWithSpaces it like Println, will add spaces for each argument -func ArgsWithSpaces(args []any) (message string) { - if ln := len(args); ln == 0 { - message = "" - } else if ln == 1 { - message = fmt.Sprint(args[0]) - } else { - message = fmt.Sprintln(args...) - // clear last "\n" - message = message[:len(message)-1] - } - return -} diff --git a/vendor/github.com/gookit/goutil/fsutil/README.md b/vendor/github.com/gookit/goutil/fsutil/README.md new file mode 100644 index 000000000..0515b6d5e --- /dev/null +++ b/vendor/github.com/gookit/goutil/fsutil/README.md @@ -0,0 +1,136 @@ +# FileSystem Util + +`fsutil` Provide some commonly file system util functions. + +## Install + +```shell +go get github.com/gookit/goutil/fsutil +``` + +## Go docs + +- [Go docs](https://pkg.go.dev/github.com/gookit/goutil/fsutil) + +## Find files + +```go +// find all files in dir +fsutil.FindInDir("./", func(filePath string, de fs.DirEntry) error { + fmt.Println(filePath) + return nil +}) + +// find files with filters +fsutil.FindInDir("./", func(filePath string, de fs.DirEntry) error { + fmt.Println(filePath) + return nil +}, fsutil.ExcludeDotFile) +``` + +## Functions API + +> **Note**: doc by run `go doc ./fsutil` + +```go +func ApplyFilters(fPath string, ent fs.DirEntry, filters []FilterFunc) bool +func CopyFile(srcPath, dstPath string) error +func CreateFile(fpath string, filePerm, dirPerm os.FileMode, fileFlag ...int) (*os.File, error) +func DeleteIfExist(fPath string) error +func DeleteIfFileExist(fPath string) error +func Dir(fpath string) string +func DiscardReader(src io.Reader) +func ExcludeDotFile(_ string, ent fs.DirEntry) bool +func Expand(pathStr string) string +func ExpandPath(pathStr string) string +func Extname(fpath string) string +func FileExists(path string) bool +func FileExt(fpath string) string +func FindInDir(dir string, handleFn HandleFunc, filters ...FilterFunc) (e error) +func GetContents(in any) []byte +func GlobWithFunc(pattern string, fn func(filePath string) error) (err error) +func IsAbsPath(aPath string) bool +func IsDir(path string) bool +func IsFile(path string) bool +func IsImageFile(path string) bool +func IsZipFile(filepath string) bool +func JoinPaths(elem ...string) string +func JoinSubPaths(basePath string, elem ...string) string +func LineScanner(in any) *bufio.Scanner +func MimeType(path string) (mime string) +func MkDirs(perm os.FileMode, dirPaths ...string) error +func MkParentDir(fpath string) error +func MkSubDirs(perm os.FileMode, parentDir string, subDirs ...string) error +func Mkdir(dirPath string, perm os.FileMode) error +func MustCopyFile(srcPath, dstPath string) +func MustCreateFile(filePath string, filePerm, dirPerm os.FileMode) *os.File +func MustReadFile(filePath string) []byte +func MustReadReader(r io.Reader) []byte +func MustRemove(fPath string) +func Name(fpath string) string +func NewIOReader(in any) (r io.Reader, err error) +func OSTempDir(pattern string) (string, error) +func OSTempFile(pattern string) (*os.File, error) +func OnlyFindDir(_ string, ent fs.DirEntry) bool +func OnlyFindFile(_ string, ent fs.DirEntry) bool +func OpenAppendFile(filepath string) (*os.File, error) +func OpenFile(filepath string, flag int, perm os.FileMode) (*os.File, error) +func OpenReadFile(filepath string) (*os.File, error) +func OpenTruncFile(filepath string) (*os.File, error) +func PathExists(path string) bool +func PathMatch(pattern, s string) bool +func PathName(fpath string) string +func PutContents(filePath string, data any, fileFlag ...int) (int, error) +func QuickOpenFile(filepath string, fileFlag ...int) (*os.File, error) +func QuietRemove(fPath string) +func ReadAll(in any) []byte +func ReadExistFile(filePath string) []byte +func ReadFile(filePath string) []byte +func ReadOrErr(in any) ([]byte, error) +func ReadReader(r io.Reader) []byte +func ReadString(in any) string +func ReadStringOrErr(in any) (string, error) +func ReaderMimeType(r io.Reader) (mime string) +func Realpath(pathStr string) string +func Remove(fPath string) error +func ResolvePath(pathStr string) string +func RmFileIfExist(fPath string) error +func RmIfExist(fPath string) error +func SearchNameUp(dirPath, name string) string +func SearchNameUpx(dirPath, name string) (string, bool) +func SlashPath(path string) string +func SplitPath(pathStr string) (dir, name string) +func Suffix(fpath string) string +func TempDir(dir, pattern string) (string, error) +func TempFile(dir, pattern string) (*os.File, error) +func TextScanner(in any) *scanner.Scanner +func ToAbsPath(p string) string +func UnixPath(path string) string +func Unzip(archive, targetDir string) (err error) +func WalkDir(dir string, fn fs.WalkDirFunc) error +func WriteFile(filePath string, data any, perm os.FileMode, fileFlag ...int) error +func WriteOSFile(f *os.File, data any) (n int, err error) +type FilterFunc func(fPath string, ent fs.DirEntry) bool +func ExcludeSuffix(ss ...string) FilterFunc +func IncludeSuffix(ss ...string) FilterFunc +type HandleFunc func(fPath string, ent fs.DirEntry) error +``` + +## Code Check & Testing + +```bash +gofmt -w -l ./ +golint ./... +``` + +**Testing**: + +```shell +go test -v ./fsutil/... +``` + +**Test limit by regexp**: + +```shell +go test -v -run ^TestSetByKeys ./fsutil/... +``` diff --git a/vendor/github.com/gookit/goutil/fsutil/check.go b/vendor/github.com/gookit/goutil/fsutil/check.go index 4411b3988..918ea7819 100644 --- a/vendor/github.com/gookit/goutil/fsutil/check.go +++ b/vendor/github.com/gookit/goutil/fsutil/check.go @@ -4,14 +4,17 @@ import ( "bytes" "os" "path" + "path/filepath" ) +// perm for create dir or file var ( - // DefaultDirPerm perm and flags for create log file DefaultDirPerm os.FileMode = 0775 DefaultFilePerm os.FileMode = 0665 OnlyReadFilePerm os.FileMode = 0444 +) +var ( // DefaultFileFlags for create and write DefaultFileFlags = os.O_CREATE | os.O_WRONLY | os.O_APPEND // OnlyReadFileFlags open file for read @@ -41,7 +44,7 @@ func PathExists(path string) bool { // IsDir reports whether the named directory exists. func IsDir(path string) bool { - if path == "" { + if path == "" || len(path) > 468 { return false } @@ -58,7 +61,7 @@ func FileExists(path string) bool { // IsFile reports whether the named file or directory exists. func IsFile(path string) bool { - if path == "" { + if path == "" || len(path) > 468 { return false } @@ -70,7 +73,13 @@ func IsFile(path string) bool { // IsAbsPath is abs path. func IsAbsPath(aPath string) bool { - return path.IsAbs(aPath) + if len(aPath) > 0 { + if aPath[0] == '/' { + return true + } + return filepath.IsAbs(aPath) + } + return false } // ImageMimeTypes refer net/http package @@ -118,3 +127,12 @@ func IsZipFile(filepath string) bool { return bytes.Equal(buf, []byte("PK\x03\x04")) } + +// PathMatch check for a string. alias of path.Match() +func PathMatch(pattern, s string) bool { + ok, err := path.Match(pattern, s) + if err != nil { + ok = false + } + return ok +} diff --git a/vendor/github.com/gookit/goutil/fsutil/find.go b/vendor/github.com/gookit/goutil/fsutil/find.go new file mode 100644 index 000000000..66d76e760 --- /dev/null +++ b/vendor/github.com/gookit/goutil/fsutil/find.go @@ -0,0 +1,156 @@ +package fsutil + +import ( + "io/fs" + "os" + "path/filepath" + + "github.com/gookit/goutil/arrutil" + "github.com/gookit/goutil/strutil" +) + +// SearchNameUp find file/dir name in dirPath or parent dirs, +// return the name of directory path +// +// Usage: +// +// repoDir := fsutil.SearchNameUp("/path/to/dir", ".git") +func SearchNameUp(dirPath, name string) string { + dir, _ := SearchNameUpx(dirPath, name) + return dir +} + +// SearchNameUpx find file/dir name in dirPath or parent dirs, +// return the name of directory path and dir is changed. +func SearchNameUpx(dirPath, name string) (string, bool) { + var level int + dirPath = ToAbsPath(dirPath) + + for { + namePath := filepath.Join(dirPath, name) + if PathExists(namePath) { + return dirPath, level > 0 + } + + level++ + prevLn := len(dirPath) + dirPath = filepath.Dir(dirPath) + if prevLn == len(dirPath) { + return "", false + } + } +} + +// WalkDir walks the file tree rooted at root, calling fn for each file or +// directory in the tree, including root. +func WalkDir(dir string, fn fs.WalkDirFunc) error { + return filepath.WalkDir(dir, fn) +} + +// GlobWithFunc handle matched file +// +// - TIP: will be not find in subdir. +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 + // + // - return False will skip handle the file. + FilterFunc func(fPath string, ent fs.DirEntry) bool + + // HandleFunc type for FindInDir + HandleFunc func(fPath string, ent fs.DirEntry) error +) + +// OnlyFindDir on find +func OnlyFindDir(_ string, ent fs.DirEntry) bool { + return ent.IsDir() +} + +// OnlyFindFile on find +func OnlyFindFile(_ string, ent fs.DirEntry) bool { + return !ent.IsDir() +} + +// ExcludeNames on find +func ExcludeNames(names ...string) FilterFunc { + return func(_ string, ent fs.DirEntry) bool { + return !arrutil.StringsHas(names, ent.Name()) + } +} + +// IncludeSuffix on find +func IncludeSuffix(ss ...string) FilterFunc { + return func(_ string, ent fs.DirEntry) bool { + return strutil.HasOneSuffix(ent.Name(), ss) + } +} + +// ExcludeDotFile on find +func ExcludeDotFile(_ string, ent fs.DirEntry) bool { + return ent.Name()[0] != '.' +} + +// ExcludeSuffix on find +func ExcludeSuffix(ss ...string) FilterFunc { + return func(_ string, ent fs.DirEntry) bool { + return !strutil.HasOneSuffix(ent.Name(), ss) + } +} + +// ApplyFilters handle +func ApplyFilters(fPath string, ent fs.DirEntry, filters []FilterFunc) bool { + for _, filter := range filters { + if !filter(fPath, ent) { + return true + } + } + return false +} + +// FindInDir code refer the go pkg: path/filepath.glob() +// +// - TIP: will be not find in subdir. +// +// 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 || !fi.IsDir() { + return // ignore I/O error + } + + // names, _ := d.Readdirnames(-1) + // sort.Strings(names) + + des, err := os.ReadDir(dir) + if err != nil { + return + } + + for _, ent := range des { + filePath := dir + "/" + ent.Name() + + // apply filters + if len(filters) > 0 && ApplyFilters(filePath, ent, filters) { + continue + } + + if err := handleFn(filePath, ent); err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/gookit/goutil/fsutil/fsutil.go b/vendor/github.com/gookit/goutil/fsutil/fsutil.go index 36ac591b3..c4517ca18 100644 --- a/vendor/github.com/gookit/goutil/fsutil/fsutil.go +++ b/vendor/github.com/gookit/goutil/fsutil/fsutil.go @@ -5,6 +5,10 @@ import ( "io" "net/http" "os" + "path/filepath" + "strings" + + "github.com/gookit/goutil/internal/comfunc" ) const ( @@ -77,3 +81,49 @@ func ReaderMimeType(r io.Reader) (mime string) { return http.DetectContentType(buf[:n]) } + +// JoinPaths elements, alias of filepath.Join() +func JoinPaths(elem ...string) string { + return filepath.Join(elem...) +} + +// JoinSubPaths elements, like the filepath.Join() +func JoinSubPaths(basePath string, elem ...string) string { + paths := make([]string, len(elem)+1) + paths[0] = basePath + copy(paths[1:], elem) + return filepath.Join(paths...) +} + +// SlashPath alias of filepath.ToSlash +func SlashPath(path string) string { + return filepath.ToSlash(path) +} + +// UnixPath like of filepath.ToSlash, but always replace +func UnixPath(path string) string { + if !strings.ContainsRune(path, '\\') { + return path + } + return strings.ReplaceAll(path, "\\", "/") +} + +// ToAbsPath convert process. will expand home dir +// +// TIP: will don't check path +func ToAbsPath(p string) string { + if len(p) == 0 || IsAbsPath(p) { + return p + } + + // expand home dir + if p[0] == '~' { + return comfunc.ExpandHome(p) + } + + wd, err := os.Getwd() + if err != nil { + return p + } + return filepath.Join(wd, p) +} diff --git a/vendor/github.com/gookit/goutil/fsutil/info.go b/vendor/github.com/gookit/goutil/fsutil/info.go index 14e680664..32ab28b96 100644 --- a/vendor/github.com/gookit/goutil/fsutil/info.go +++ b/vendor/github.com/gookit/goutil/fsutil/info.go @@ -1,7 +1,6 @@ package fsutil import ( - "io/ioutil" "os" "path" "path/filepath" @@ -9,116 +8,60 @@ import ( "github.com/gookit/goutil/internal/comfunc" ) -// Dir get dir path, without last name. -func Dir(fpath string) string { - return filepath.Dir(fpath) -} +// Dir get dir path from filepath, 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) -} +func PathName(fpath string) string { return path.Base(fpath) } -// Name get file/dir name from full path +// Name get file/dir name from full path. +// +// eg: path/to/main.go => main.go func Name(fpath string) string { + if fpath == "" { + return "" + } return filepath.Base(fpath) } // FileExt get filename ext. alias of path.Ext() -func FileExt(fpath string) string { - return path.Ext(fpath) +// +// eg: path/to/main.go => ".go" +func FileExt(fpath string) string { return path.Ext(fpath) } + +// Extname get filename ext. alias of path.Ext() +// +// eg: path/to/main.go => "go" +func Extname(fpath string) string { + if ext := path.Ext(fpath); len(ext) > 0 { + return ext[1:] + } + return "" } // Suffix get filename ext. alias of path.Ext() -func Suffix(fpath string) string { - return path.Ext(fpath) -} +// +// eg: path/to/main.go => ".go" +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) + return comfunc.ExpandHome(pathStr) } // ExpandPath will parse `~` as user home dir path. func ExpandPath(pathStr string) string { - return comfunc.ExpandPath(pathStr) + return comfunc.ExpandHome(pathStr) } -// Realpath returns the shortest path name equivalent to path by purely lexical processing. -func Realpath(pathStr string) string { - return path.Clean(pathStr) +// ResolvePath will parse `~` and env var in path +func ResolvePath(pathStr string) string { + pathStr = comfunc.ExpandHome(pathStr) + // return comfunc.ParseEnvVar() + return os.ExpandEnv(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/info_nonwin.go b/vendor/github.com/gookit/goutil/fsutil/info_nonwin.go new file mode 100644 index 000000000..67c7cfece --- /dev/null +++ b/vendor/github.com/gookit/goutil/fsutil/info_nonwin.go @@ -0,0 +1,19 @@ +//go:build !windows + +package fsutil + +import ( + "path" + + "github.com/gookit/goutil/internal/comfunc" +) + +// Realpath returns the shortest path name equivalent to path by purely lexical processing. +func Realpath(pathStr string) string { + pathStr = comfunc.ExpandHome(pathStr) + + if !IsAbsPath(pathStr) { + pathStr = JoinSubPaths(comfunc.Workdir(), pathStr) + } + return path.Clean(pathStr) +} diff --git a/vendor/github.com/gookit/goutil/fsutil/info_windows.go b/vendor/github.com/gookit/goutil/fsutil/info_windows.go new file mode 100644 index 000000000..dc21ebe76 --- /dev/null +++ b/vendor/github.com/gookit/goutil/fsutil/info_windows.go @@ -0,0 +1,17 @@ +package fsutil + +import ( + "path/filepath" + + "github.com/gookit/goutil/internal/comfunc" +) + +// Realpath returns the shortest path name equivalent to path by purely lexical processing. +func Realpath(pathStr string) string { + pathStr = comfunc.ExpandHome(pathStr) + + if !IsAbsPath(pathStr) { + pathStr = JoinSubPaths(comfunc.Workdir(), pathStr) + } + return filepath.Clean(pathStr) +} diff --git a/vendor/github.com/gookit/goutil/fsutil/operate.go b/vendor/github.com/gookit/goutil/fsutil/operate.go index 310252428..5bf1e600a 100644 --- a/vendor/github.com/gookit/goutil/fsutil/operate.go +++ b/vendor/github.com/gookit/goutil/fsutil/operate.go @@ -4,11 +4,13 @@ import ( "archive/zip" "fmt" "io" - "io/ioutil" + "io/fs" "os" "path" "path/filepath" "strings" + + "github.com/gookit/goutil/basefn" ) // Mkdir alias of os.MkdirAll() @@ -16,6 +18,27 @@ func Mkdir(dirPath string, perm os.FileMode) error { return os.MkdirAll(dirPath, perm) } +// MkDirs batch make multi dirs at once +func MkDirs(perm os.FileMode, dirPaths ...string) error { + for _, dirPath := range dirPaths { + if err := os.MkdirAll(dirPath, perm); err != nil { + return err + } + } + return nil +} + +// MkSubDirs batch make multi sub-dirs at once +func MkSubDirs(perm os.FileMode, parentDir string, subDirs ...string) error { + for _, dirName := range subDirs { + dirPath := parentDir + "/" + dirName + if err := os.MkdirAll(dirPath, perm); err != nil { + return err + } + } + return nil +} + // MkParentDir quick create parent dir func MkParentDir(fpath string) error { dirPath := filepath.Dir(fpath) @@ -25,61 +48,11 @@ func MkParentDir(fpath string) error { 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 +// some commonly 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 @@ -88,6 +61,10 @@ const ( ) // OpenFile like os.OpenFile, but will auto create dir. +// +// Usage: +// +// file, err := OpenFile("path/to/file.txt", FsCWFlags, 0666) func OpenFile(filepath string, flag int, perm os.FileMode) (*os.File, error) { fileDir := path.Dir(filepath) if err := os.MkdirAll(fileDir, DefaultDirPerm); err != nil { @@ -101,20 +78,39 @@ func OpenFile(filepath string, flag int, perm os.FileMode) (*os.File, error) { return file, nil } -/* TODO MustOpenFile() */ - -// QuickOpenFile like os.OpenFile, open for write, if not exists, will create it. +// MustOpenFile like os.OpenFile, but will auto create dir. // -// Tip: file flag default is FsCWAFlags -func QuickOpenFile(filepath string, fileFlag ...int) (*os.File, error) { - flag := FsCWAFlags - if len(fileFlag) > 0 { - flag = fileFlag[0] +// Usage: +// +// file := MustOpenFile("path/to/file.txt", FsCWFlags, 0666) +func MustOpenFile(filepath string, flag int, perm os.FileMode) *os.File { + file, err := OpenFile(filepath, flag, perm) + if err != nil { + panic(err) } + return file +} +// QuickOpenFile like os.OpenFile, open for append write. if not exists, will create it. +// +// Alias of OpenAppendFile() +func QuickOpenFile(filepath string, fileFlag ...int) (*os.File, error) { + flag := basefn.FirstOr(fileFlag, FsCWAFlags) return OpenFile(filepath, flag, DefaultFilePerm) } +// OpenAppendFile like os.OpenFile, open for append write. if not exists, will create it. +func OpenAppendFile(filepath string, filePerm ...os.FileMode) (*os.File, error) { + perm := basefn.FirstOr(filePerm, DefaultFilePerm) + return OpenFile(filepath, FsCWAFlags, perm) +} + +// OpenTruncFile like os.OpenFile, open for override write. if not exists, will create it. +func OpenTruncFile(filepath string, filePerm ...os.FileMode) (*os.File, error) { + perm := basefn.FirstOr(filePerm, DefaultFilePerm) + return OpenFile(filepath, FsCWTFlags, perm) +} + // OpenReadFile like os.OpenFile, open file for read contents func OpenReadFile(filepath string) (*os.File, error) { return os.OpenFile(filepath, FsRFlags, OnlyReadFilePerm) @@ -134,11 +130,7 @@ func CreateFile(fpath string, filePerm, dirPerm os.FileMode, fileFlag ...int) (* } } - flag := FsCWTFlags - if len(fileFlag) > 0 { - flag = fileFlag[0] - } - + flag := basefn.FirstOr(fileFlag, FsCWAFlags) return os.OpenFile(fpath, flag, filePerm) } @@ -151,105 +143,6 @@ func MustCreateFile(filePath string, filePerm, dirPerm os.FileMode) *os.File { 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 // ************************************************************ @@ -302,6 +195,18 @@ func DeleteIfFileExist(fPath string) error { return nil } +// RemoveSub removes all sub files and dirs of dirPath, but not remove dirPath. +func RemoveSub(dirPath string, fns ...FilterFunc) error { + return FindInDir(dirPath, func(fPath string, ent fs.DirEntry) error { + if ent.IsDir() { + if err := RemoveSub(fPath, fns...); err != nil { + return err + } + } + return os.Remove(fPath) + }, fns...) +} + // ************************************************************ // other operates // ************************************************************ @@ -319,7 +224,6 @@ func Unzip(archive, targetDir string) (err error) { } for _, file := range reader.File { - if strings.Contains(file.Name, "..") { return fmt.Errorf("illegal file path in zip: %v", file.Name) } diff --git a/vendor/github.com/gookit/goutil/fsutil/opread.go b/vendor/github.com/gookit/goutil/fsutil/opread.go new file mode 100644 index 000000000..23f552332 --- /dev/null +++ b/vendor/github.com/gookit/goutil/fsutil/opread.go @@ -0,0 +1,137 @@ +package fsutil + +import ( + "bufio" + "errors" + "io" + "os" + "text/scanner" +) + +// NewIOReader instance by input file path or io.Reader +func NewIOReader(in any) (r io.Reader, err error) { + switch typIn := in.(type) { + case string: // as file path + return OpenReadFile(typIn) + case io.Reader: + return typIn, nil + } + return nil, errors.New("invalid input type, allow: string, io.Reader") +} + +// DiscardReader anything from the reader +func DiscardReader(src io.Reader) { + _, _ = io.Copy(io.Discard, src) +} + +// ReadFile read file contents, will panic on error +func ReadFile(filePath string) []byte { + return MustReadFile(filePath) +} + +// MustReadFile read file contents, will panic on error +func MustReadFile(filePath string) []byte { + bs, err := os.ReadFile(filePath) + if err != nil { + panic(err) + } + return bs +} + +// ReadReader read contents from io.Reader, will panic on error +func ReadReader(r io.Reader) []byte { return MustReadReader(r) } + +// 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 +} + +// ReadString read contents from path or io.Reader, will panic on in type error +func ReadString(in any) string { + return string(GetContents(in)) +} + +// ReadStringOrErr read contents from path or io.Reader, will panic on in type error +func ReadStringOrErr(in any) (string, error) { + r, err := NewIOReader(in) + if err != nil { + return "", err + } + + bs, err := io.ReadAll(r) + if err != nil { + return "", err + } + return string(bs), nil +} + +// ReadAll read contents from path or io.Reader, will panic on in type error +func ReadAll(in any) []byte { return GetContents(in) } + +// GetContents read contents from path or io.Reader, will panic on in type error +func GetContents(in any) []byte { + r, err := NewIOReader(in) + if err != nil { + panic(err) + } + return MustReadReader(r) +} + +// ReadOrErr read contents from path or io.Reader, will panic on in type error +func ReadOrErr(in any) ([]byte, error) { + r, err := NewIOReader(in) + if err != nil { + return nil, err + } + return io.ReadAll(r) +} + +// ReadExistFile read file contents if existed, will panic on error +func ReadExistFile(filePath string) []byte { + if IsFile(filePath) { + bs, err := os.ReadFile(filePath) + if err != nil { + panic(err) + } + return bs + } + return nil +} + +// TextScanner from filepath or io.Reader, will panic on in type error +// +// Usage: +// +// s := fsutil.TextScanner("/path/to/file") +// for tok := s.Scan(); tok != scanner.EOF; tok = s.Scan() { +// fmt.Printf("%s: %s\n", s.Position, s.TokenText()) +// } +func TextScanner(in any) *scanner.Scanner { + var s scanner.Scanner + r, err := NewIOReader(in) + if err != nil { + panic(err) + } + + s.Init(r) + s.Filename = "text-scanner" + return &s +} + +// LineScanner create from filepath or io.Reader +// +// s := fsutil.LineScanner("/path/to/file") +// for s.Scan() { +// fmt.Println(s.Text()) +// } +func LineScanner(in any) *bufio.Scanner { + r, err := NewIOReader(in) + if err != nil { + panic(err) + } + return bufio.NewScanner(r) +} diff --git a/vendor/github.com/gookit/goutil/fsutil/opwrite.go b/vendor/github.com/gookit/goutil/fsutil/opwrite.go new file mode 100644 index 000000000..d264a3ca0 --- /dev/null +++ b/vendor/github.com/gookit/goutil/fsutil/opwrite.go @@ -0,0 +1,101 @@ +package fsutil + +import ( + "io" + "os" + + "github.com/gookit/goutil/basefn" +) + +// ************************************************************ +// 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 FsCWTFlags (override write) +// +// Usage: +// +// fsutil.PutContents(filePath, contents, fsutil.FsCWAFlags) // append write +func PutContents(filePath string, data any, fileFlag ...int) (int, error) { + f, err := QuickOpenFile(filePath, basefn.FirstOr(fileFlag, FsCWTFlags)) + if err != nil { + return 0, err + } + + return WriteOSFile(f, data) +} + +// 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 (override write) +// +// Usage: +// +// fsutil.WriteFile(filePath, contents, fsutil.DefaultFilePerm, fsutil.FsCWAFlags) +func WriteFile(filePath string, data any, perm os.FileMode, fileFlag ...int) error { + flag := basefn.FirstOr(fileFlag, FsCWTFlags) + f, err := OpenFile(filePath, flag, perm) + if err != nil { + return err + } + + _, err = WriteOSFile(f, data) + return err +} + +// WriteOSFile write data to give os.File, then close file. +// +// data type allow: string, []byte, io.Reader +func WriteOSFile(f *os.File, data any) (n int, err error) { + switch typData := data.(type) { + case []byte: + n, err = f.Write(typData) + case string: + n, err = f.WriteString(typData) + case io.Reader: // eg: buffer + var n64 int64 + n64, err = io.Copy(f, typData) + n = int(n64) + default: + _ = f.Close() + panic("WriteFile: data type only allow: []byte, string, io.Reader") + } + + if err1 := f.Close(); err1 != nil && err == nil { + err = err1 + } + return n, 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) + } +} diff --git a/vendor/github.com/gookit/goutil/func.go b/vendor/github.com/gookit/goutil/func.go new file mode 100644 index 000000000..0573f1d10 --- /dev/null +++ b/vendor/github.com/gookit/goutil/func.go @@ -0,0 +1,37 @@ +package goutil + +import "github.com/gookit/goutil/stdutil" + +// FuncName get func name +func FuncName(f any) string { + return stdutil.FuncName(f) +} + +// 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 +} + +// ErrFunc type +type ErrFunc func() error + +// CallOn call func on condition is true +func CallOn(cond bool, fn ErrFunc) error { + if cond { + return fn() + } + return nil +} + +// CallOrElse call okFunc() on condition is true, else call elseFn() +func CallOrElse(cond bool, okFn, elseFn ErrFunc) error { + if cond { + return okFn() + } + return elseFn() +} diff --git a/vendor/github.com/gookit/goutil/goutil.go b/vendor/github.com/gookit/goutil/goutil.go index 97a1fe90e..6298c9f74 100644 --- a/vendor/github.com/gookit/goutil/goutil.go +++ b/vendor/github.com/gookit/goutil/goutil.go @@ -11,14 +11,9 @@ import ( // 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 +// Panicf format panic message use fmt.Sprintf +func Panicf(format string, v ...any) { + panic(fmt.Sprintf(format, v...)) } // PanicIfErr if error is not empty, will panic @@ -42,14 +37,12 @@ func MustOK(err error) { } } -// Panicf format panic message use fmt.Sprintf -func Panicf(format string, v ...any) { - panic(fmt.Sprintf(format, v...)) -} - -// FuncName get func name -func FuncName(f any) string { - return stdutil.FuncName(f) +// Must if error is not empty, will panic +func Must[T any](v T, err error) T { + if err != nil { + panic(err) + } + return v } // PkgName get current package name. alias of stdutil.PkgName() @@ -61,3 +54,32 @@ func FuncName(f any) string { func PkgName(funcName string) string { return stdutil.PkgName(funcName) } + +// ErrOnFail return input error on cond is false, otherwise return nil +func ErrOnFail(cond bool, err error) error { + return OrError(cond, err) +} + +// OrError return input error on cond is false, otherwise return nil +func OrError(cond bool, err error) error { + if !cond { + return err + } + return nil +} + +// OrValue get +func OrValue[T any](cond bool, okVal, elVal T) T { + if cond { + return okVal + } + return elVal +} + +// OrReturn call okFunc() on condition is true, else call elseFn() +func OrReturn[T any](cond bool, okFn, elseFn func() T) T { + if cond { + return okFn() + } + return elseFn() +} diff --git a/vendor/github.com/gookit/goutil/group.go b/vendor/github.com/gookit/goutil/group.go index 770c9e236..90a3204da 100644 --- a/vendor/github.com/gookit/goutil/group.go +++ b/vendor/github.com/gookit/goutil/group.go @@ -3,6 +3,7 @@ package goutil import ( "context" + "github.com/gookit/goutil/structs" "golang.org/x/sync/errgroup" ) @@ -44,3 +45,38 @@ func (g *ErrGroup) Add(handlers ...func() error) { g.Go(handler) } } + +// RunFn func +type RunFn func(ctx *structs.Data) error + +// QuickRun struct +type QuickRun struct { + ctx *structs.Data + // err error + fns []RunFn +} + +// NewQuickRun instance +func NewQuickRun() *QuickRun { + return &QuickRun{ + ctx: structs.NewData(), + } +} + +// Add func for run +func (p *QuickRun) Add(fns ...RunFn) *QuickRun { + p.fns = append(p.fns, fns...) + return p +} + +// Run all func +func (p *QuickRun) Run() error { + for i, fn := range p.fns { + p.ctx.Set("index", i) + + if err := fn(p.ctx); err != nil { + return err + } + } + return nil +} diff --git a/vendor/github.com/gookit/goutil/internal/comfunc/comfunc.go b/vendor/github.com/gookit/goutil/internal/comfunc/comfunc.go index 017bed6c3..bd9e7525c 100644 --- a/vendor/github.com/gookit/goutil/internal/comfunc/comfunc.go +++ b/vendor/github.com/gookit/goutil/internal/comfunc/comfunc.go @@ -1,9 +1,12 @@ package comfunc import ( + "fmt" "os" "regexp" + "strconv" "strings" + "time" ) // Environ like os.Environ, but will returns key-value map[string]string data. @@ -65,7 +68,6 @@ func ParseEnvVar(val string, getFn func(string) string) (newVal string) { 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]) } @@ -77,3 +79,96 @@ func ParseEnvVar(val string, getFn func(string) string) (newVal string) { return eVal }) } + +// FormatTplAndArgs message +func FormatTplAndArgs(fmtAndArgs []any) string { + if len(fmtAndArgs) == 0 || fmtAndArgs == nil { + return "" + } + + ln := len(fmtAndArgs) + first := fmtAndArgs[0] + + if ln == 1 { + if msgAsStr, ok := first.(string); ok { + return msgAsStr + } + return fmt.Sprintf("%+v", first) + } + + // is template string. + if tplStr, ok := first.(string); ok { + return fmt.Sprintf(tplStr, fmtAndArgs[1:]...) + } + return fmt.Sprint(fmtAndArgs...) +} + +var ( + // TIP: extend unit d,w + // time.ParseDuration() is not supported. eg: "1d", "2w" + durStrReg = regexp.MustCompile(`^(-?\d+)(ns|us|µs|ms|s|m|h|d|w)$`) + // match long duration string, such as "1hour", "2hours", "3minutes", "4mins", "5days", "1weeks" + // time.ParseDuration() is not supported. + durStrRegL = regexp.MustCompile(`^(-?\d+)([a-zA-Z]{3,})$`) +) + +// IsDuration check the string is a duration string. +func IsDuration(s string) bool { + if s == "0" || durStrReg.MatchString(s) { + return true + } + return durStrRegL.MatchString(s) +} + +// ToDuration parses a duration string. such as "300ms", "-1.5h" or "2h45m". +// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". +// +// Diff of time.ParseDuration: +// - support extend unit d, w at the end of string. such as "1d", "2w". +// - support long string unit at end. such as "1hour", "2hours", "3minutes", "4mins", "5days", "1weeks". +// +// If the string is not a valid duration string, it will return an error. +func ToDuration(s string) (time.Duration, error) { + ln := len(s) + if ln == 0 { + return 0, fmt.Errorf("empty duration string") + } + + s = strings.ToLower(s) + if s == "0" { + return 0, nil + } + + // extend unit d,w, time.ParseDuration() is not supported. eg: "1d", "2w" + if lastUnit := s[ln-1]; lastUnit == 'd' { + s = s + "ay" + } else if lastUnit == 'w' { + s = s + "eek" + } + + // long unit, time.ParseDuration() is not supported. eg: "-3sec" => [3sec -3 sec] + ss := durStrRegL.FindStringSubmatch(s) + if len(ss) == 3 { + num, unit := ss[1], ss[2] + + // convert to short unit + switch unit { + case "week", "weeks": + // max unit is hour, so need convert by 24 * 7 * n + n, _ := strconv.Atoi(num) + s = strconv.Itoa(n*24*7) + "h" + case "day", "days": + // max unit is hour, so need convert by 24 * n + n, _ := strconv.Atoi(num) + s = strconv.Itoa(n*24) + "h" + case "hour", "hours": + s = num + "h" + case "min", "mins", "minute", "minutes": + s = num + "m" + case "sec", "secs", "second", "seconds": + s = num + "s" + } + } + + return time.ParseDuration(s) +} diff --git a/vendor/github.com/gookit/goutil/internal/comfunc/sysfunc.go b/vendor/github.com/gookit/goutil/internal/comfunc/sysfunc.go index a78296717..aef90e947 100644 --- a/vendor/github.com/gookit/goutil/internal/comfunc/sysfunc.go +++ b/vendor/github.com/gookit/goutil/internal/comfunc/sysfunc.go @@ -8,8 +8,14 @@ import ( "strings" ) -// ExpandPath will parse first `~` as user home dir path. -func ExpandPath(pathStr string) string { +// Workdir get +func Workdir() string { + dir, _ := os.Getwd() + return dir +} + +// ExpandHome will parse first `~` as user home dir path. +func ExpandHome(pathStr string) string { if len(pathStr) == 0 { return pathStr } @@ -26,7 +32,6 @@ func ExpandPath(pathStr string) string { if err != nil { return pathStr } - return homeDir + pathStr[1:] } @@ -47,7 +52,7 @@ func ExecCmd(binName string, args []string, workDir ...string) (string, error) { } // ShellExec exec command by shell -// cmdLine eg. "ls -al" +// cmdLine e.g. "ls -al" func ShellExec(cmdLine string, shells ...string) (string, error) { // shell := "/bin/sh" shell := "sh" @@ -73,23 +78,26 @@ var curShell string // // eg "/bin/zsh" "/bin/bash". // if onlyName=true, will return "zsh", "bash" -func CurrentShell(onlyName bool) (path string) { +func CurrentShell(onlyName bool) (binPath string) { var err error if curShell == "" { - path, err = ShellExec("echo $SHELL") - if err != nil { - return "" + binPath = os.Getenv("SHELL") + if len(binPath) == 0 { + binPath, err = ShellExec("echo $SHELL") + if err != nil { + return "" + } } - path = strings.TrimSpace(path) + binPath = strings.TrimSpace(binPath) // cache result - curShell = path + curShell = binPath } else { - path = curShell + binPath = curShell } - if onlyName && len(path) > 0 { - path = filepath.Base(path) + if onlyName && len(binPath) > 0 { + binPath = filepath.Base(binPath) } return } @@ -106,6 +114,5 @@ func HasShellEnv(shell string) bool { 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 index 11be66ed3..c7d392d8e 100644 --- a/vendor/github.com/gookit/goutil/jsonutil/jsonutil.go +++ b/vendor/github.com/gookit/goutil/jsonutil/jsonutil.go @@ -5,7 +5,6 @@ import ( "bytes" "encoding/json" "io" - "io/ioutil" "os" "regexp" "strings" @@ -18,7 +17,16 @@ func WriteFile(filePath string, data any) error { if err != nil { return err } - return ioutil.WriteFile(filePath, jsonBytes, 0664) + return os.WriteFile(filePath, jsonBytes, 0664) +} + +// WritePretty write pretty data to JSON file +func WritePretty(filePath string, data any) error { + bs, err := json.MarshalIndent(data, "", " ") + if err != nil { + return err + } + return os.WriteFile(filePath, bs, 0664) } // ReadFile Read JSON file data @@ -92,6 +100,35 @@ func Mapping(src, dst any) error { return Decode(bts, dst) } +// IsJSON check if the string is valid JSON. (Note: uses json.Unmarshal) +func IsJSON(s string) bool { + if s == "" { + return false + } + + var js json.RawMessage + return json.Unmarshal([]byte(s), &js) == nil +} + +// IsJSONFast simple and fast check input string is valid JSON. +func IsJSONFast(s string) bool { + ln := len(s) + if ln < 2 { + return false + } + if ln == 2 { + return s == "{}" || s == "[]" + } + + // object + if s[0] == '{' { + return s[ln-1] == '}' && s[1] == '"' + } + + // array + return s[0] == '[' && s[ln-1] == ']' +} + // `(?s:` enable match multi line var jsonMLComments = regexp.MustCompile(`(?s:/\*.*?\*/\s*)`) diff --git a/vendor/github.com/gookit/goutil/maputil/check.go b/vendor/github.com/gookit/goutil/maputil/check.go index d2c10ff2b..1f3194465 100644 --- a/vendor/github.com/gookit/goutil/maputil/check.go +++ b/vendor/github.com/gookit/goutil/maputil/check.go @@ -21,7 +21,25 @@ func HasKey(mp, key any) (ok bool) { return } -// HasAllKeys check of the given map. +// HasOneKey check of the given map. return the first exist key +func HasOneKey(mp any, keys ...any) (ok bool, key any) { + rftVal := reflect.Indirect(reflect.ValueOf(mp)) + if rftVal.Kind() != reflect.Map { + return + } + + for _, key = range keys { + for _, keyRv := range rftVal.MapKeys() { + if reflects.IsEqual(keyRv.Interface(), key) { + return true, key + } + } + } + + return false, nil +} + +// HasAllKeys check of the given map. return the first not exist key func HasAllKeys(mp any, keys ...any) (ok bool, noKey any) { rftVal := reflect.Indirect(reflect.ValueOf(mp)) if rftVal.Kind() != reflect.Map { diff --git a/vendor/github.com/gookit/goutil/maputil/convert.go b/vendor/github.com/gookit/goutil/maputil/convert.go index da77b89f2..4ded4cfde 100644 --- a/vendor/github.com/gookit/goutil/maputil/convert.go +++ b/vendor/github.com/gookit/goutil/maputil/convert.go @@ -1,9 +1,12 @@ package maputil import ( + "errors" "reflect" "strings" + "github.com/gookit/goutil/arrutil" + "github.com/gookit/goutil/comdef" "github.com/gookit/goutil/reflects" "github.com/gookit/goutil/strutil" ) @@ -15,22 +18,54 @@ func KeyToLower(src map[string]string) map[string]string { 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)) + strMp := make(map[string]string, len(src)) for k, v := range src { - newMp[k] = strutil.MustString(v) + strMp[k] = strutil.SafeString(v) } - - return newMp + return strMp } -// HttpQueryString convert map[string]any data to http query string. -func HttpQueryString(data map[string]any) string { +// CombineToSMap combine two string-slice to SMap(map[string]string) +func CombineToSMap(keys, values []string) SMap { + return arrutil.CombineToSMap(keys, values) +} + +// CombineToMap combine two any slice to map[K]V. alias of arrutil.CombineToMap +func CombineToMap[K comdef.SortedType, V any](keys []K, values []V) map[K]V { + return arrutil.CombineToMap(keys, values) +} + +// ToAnyMap convert map[TYPE1]TYPE2 to map[string]any +func ToAnyMap(mp any) map[string]any { + amp, _ := TryAnyMap(mp) + return amp +} + +// TryAnyMap convert map[TYPE1]TYPE2 to map[string]any +func TryAnyMap(mp any) (map[string]any, error) { + if aMp, ok := mp.(map[string]any); ok { + return aMp, nil + } + + rv := reflect.Indirect(reflect.ValueOf(mp)) + if rv.Kind() != reflect.Map { + return nil, errors.New("input is not a map value") + } + + anyMp := make(map[string]any, rv.Len()) + for _, key := range rv.MapKeys() { + anyMp[key.String()] = rv.MapIndex(key).Interface() + } + return anyMp, nil +} + +// 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)) diff --git a/vendor/github.com/gookit/goutil/maputil/data.go b/vendor/github.com/gookit/goutil/maputil/data.go index 6720d514b..4049a8ba5 100644 --- a/vendor/github.com/gookit/goutil/maputil/data.go +++ b/vendor/github.com/gookit/goutil/maputil/data.go @@ -3,12 +3,15 @@ package maputil import ( "strings" + "github.com/gookit/goutil/arrutil" "github.com/gookit/goutil/mathutil" "github.com/gookit/goutil/strutil" ) // Data an map data type type Data map[string]any + +// Map alias of Data type Map = Data // Has value on the data map @@ -123,6 +126,14 @@ func (d Data) Int64(key string) int64 { return 0 } +// Uint value get +func (d Data) Uint(key string) uint64 { + if val, ok := d.GetByPath(key); ok { + return mathutil.QuietUint(val) + } + return 0 +} + // Str value get by key func (d Data) Str(key string) string { if val, ok := d.GetByPath(key); ok { @@ -137,14 +148,15 @@ func (d Data) Bool(key string) bool { if !ok { return false } - if bl, ok := val.(bool); ok { - return bl - } - if str, ok := val.(string); ok { - return strutil.QuietBool(str) + switch tv := val.(type) { + case string: + return strutil.QuietBool(tv) + case bool: + return tv + default: + return false } - return false } // Strings get []string value @@ -154,10 +166,16 @@ func (d Data) Strings(key string) []string { return nil } - if ss, ok := val.([]string); ok { - return ss + switch typVal := val.(type) { + case string: + return []string{typVal} + case []string: + return typVal + case []any: + return arrutil.SliceToStrings(typVal) + default: + return nil } - return nil } // StrSplit get strings by split key value @@ -176,6 +194,11 @@ func (d Data) StringsByStr(key string) []string { return nil } +// StrMap get map[string]string value +func (d Data) StrMap(key string) map[string]string { + return d.StringMap(key) +} + // StringMap get map[string]string value func (d Data) StringMap(key string) map[string]string { val, ok := d.GetByPath(key) @@ -183,10 +206,14 @@ func (d Data) StringMap(key string) map[string]string { return nil } - if smp, ok := val.(map[string]string); ok { - return smp + switch tv := val.(type) { + case map[string]string: + return tv + case map[string]any: + return ToStringMap(tv) + default: + return nil } - return nil } // Sub get sub value as new Data @@ -217,3 +244,17 @@ func (d Data) ToStringMap() map[string]string { func (d Data) String() string { return ToString(d) } + +// Load other data to current data map +func (d Data) Load(sub map[string]any) { + for name, val := range sub { + d[name] = val + } +} + +// LoadSMap to data +func (d Data) LoadSMap(smp map[string]string) { + for name, val := range smp { + d[name] = val + } +} diff --git a/vendor/github.com/gookit/goutil/maputil/get.go b/vendor/github.com/gookit/goutil/maputil/get.go index 151d918cf..c2236c47a 100644 --- a/vendor/github.com/gookit/goutil/maputil/get.go +++ b/vendor/github.com/gookit/goutil/maputil/get.go @@ -6,6 +6,12 @@ import ( "strings" ) +// some consts for separators +const ( + Wildcard = "*" + PathSep = "." +) + // DeepGet value by key path. eg "top" "top.sub" func DeepGet(mp map[string]any, path string) (val any) { val, _ = GetByPath(path, mp) @@ -25,48 +31,96 @@ func GetByPath(path string, mp map[string]any) (val any, ok bool) { } // no sub key - if len(mp) == 0 || !strings.ContainsRune(path, '.') { + if len(mp) == 0 || strings.IndexByte(path, '.') < 1 { return nil, false } // has sub key. eg. "top.sub" keys := strings.Split(path, ".") - topK := keys[0] + return GetByPathKeys(mp, keys) +} + +// GetByPathKeys get value by path keys from a map(map[string]any). eg "top" "top.sub" +// +// Example: +// +// mp := map[string]any{ +// "top": map[string]any{ +// "sub": "value", +// }, +// } +// val, ok := GetByPathKeys(mp, []string{"top", "sub"}) // return "value", true +func GetByPathKeys(mp map[string]any, keys []string) (val any, ok bool) { + kl := len(keys) + if kl == 0 { + return mp, true + } // find top item data use top key var item any + + topK := keys[0] if item, ok = mp[topK]; !ok { return } - for _, k := range keys[1:] { + // find sub item data use sub key + for i, k := range keys[1:] { switch tData := item.(type) { - case map[string]string: // is simple map + case map[string]string: // is string map if item, ok = tData[k]; !ok { return } - case map[string]any: // is map(decode from toml/json) + case map[string]any: // is map(decode from toml/json/yaml) if item, ok = tData[k]; !ok { return } - case map[any]any: // is map(decode from yaml) + case map[any]any: // is map(decode from yaml.v2) if item, ok = tData[k]; !ok { return } - case []any: // is a slice - if item, ok = getBySlice(k, tData); !ok { - return + case []map[string]any: // is an any-map slice + if k == Wildcard { + if kl == i+2 { + return tData, true + } + + sl := make([]any, 0, len(tData)) + for _, v := range tData { + if val, ok = GetByPathKeys(v, keys[i+2:]); ok { + sl = append(sl, val) + } + } + return sl, true } - 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() + + // k is index number + idx, err := strconv.Atoi(k) + if err != nil { + return nil, false } - if item, ok = getBySlice(k, sData); !ok { - return + + if idx >= len(tData) { + return nil, false } - default: // error + item = tData[idx] + default: + rv := reflect.ValueOf(tData) + // check is slice + if rv.Kind() == reflect.Slice { + i, err := strconv.Atoi(k) + if err != nil { + return nil, false + } + if i >= rv.Len() { + return nil, false + } + + item = rv.Index(i).Interface() + continue + } + + // as error return nil, false } } @@ -74,17 +128,6 @@ func GetByPath(path string, mp map[string]any) (val any, ok bool) { 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)) @@ -101,14 +144,26 @@ func Keys(mp any) (keys []string) { // 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 { + rv := reflect.Indirect(reflect.ValueOf(mp)) + if rv.Kind() != reflect.Map { return } - values = make([]any, 0, rftVal.Len()) - for _, key := range rftVal.MapKeys() { - values = append(values, rftVal.MapIndex(key).Interface()) + values = make([]any, 0, rv.Len()) + for _, key := range rv.MapKeys() { + values = append(values, rv.MapIndex(key).Interface()) } return } + +// EachAnyMap iterates the given map and calls the given function for each item. +func EachAnyMap(mp any, fn func(key string, val any)) { + rv := reflect.Indirect(reflect.ValueOf(mp)) + if rv.Kind() != reflect.Map { + panic("not a map value") + } + + for _, key := range rv.MapKeys() { + fn(key.String(), rv.MapIndex(key).Interface()) + } +} diff --git a/vendor/github.com/gookit/goutil/maputil/maputil.go b/vendor/github.com/gookit/goutil/maputil/maputil.go index e661f06d9..4e047c289 100644 --- a/vendor/github.com/gookit/goutil/maputil/maputil.go +++ b/vendor/github.com/gookit/goutil/maputil/maputil.go @@ -16,6 +16,26 @@ const ( KeySepChar = '.' ) +// SimpleMerge simple merge two data map by string key. +// will merge the src to dst map +func SimpleMerge(src, dst map[string]any) map[string]any { + if len(src) == 0 { + return dst + } + + if len(dst) == 0 { + return src + } + + for key, val := range src { + dst[key] = val + } + return dst +} + +// func DeepMerge(src, dst map[string]any, deep int) map[string]any { TODO +// } + // 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) @@ -23,6 +43,13 @@ func MergeSMap(src, dst map[string]string, ignoreCase bool) map[string]string { // MergeStringMap simple merge two string map. merge src to dst map func MergeStringMap(src, dst map[string]string, ignoreCase bool) map[string]string { + if len(src) == 0 { + return dst + } + if len(dst) == 0 { + return src + } + for k, v := range src { if ignoreCase { k = strings.ToLower(k) diff --git a/vendor/github.com/gookit/goutil/maputil/smap.go b/vendor/github.com/gookit/goutil/maputil/smap.go index d38598214..47c9ad991 100644 --- a/vendor/github.com/gookit/goutil/maputil/smap.go +++ b/vendor/github.com/gookit/goutil/maputil/smap.go @@ -111,6 +111,15 @@ func (m SMap) Values() []string { return ss } +// ToKVPairs slice convert. eg: {k1:v1,k2:v2} => {k1,v1,k2,v2} +func (m SMap) ToKVPairs() []string { + pairs := make([]string, 0, len(m)*2) + for k, v := range m { + pairs = append(pairs, k, v) + } + return pairs +} + // String data to string func (m SMap) String() string { return ToString2(m) diff --git a/vendor/github.com/gookit/goutil/mathutil/check.go b/vendor/github.com/gookit/goutil/mathutil/check.go index 45c499275..055fc4df0 100644 --- a/vendor/github.com/gookit/goutil/mathutil/check.go +++ b/vendor/github.com/gookit/goutil/mathutil/check.go @@ -1,6 +1,8 @@ package mathutil -// Compare intX,floatX value by given op. returns `srcVal op(=,!=,<,<=,>,>=) dstVal` +import "github.com/gookit/goutil/comdef" + +// Compare any intX,floatX value by given op. returns `srcVal op(=,!=,<,<=,>,>=) dstVal` // // Usage: // @@ -42,40 +44,54 @@ func Compare(srcVal, dstVal any, op string) (ok bool) { return CompInt64(srcInt, dstInt, op) } -// CompInt64 compare int64, returns the srcI64 op dstI64 -func CompInt64(srcI64, dstI64 int64, op string) (ok bool) { +// CompInt compare int,uint value. returns `srcVal op(=,!=,<,<=,>,>=) dstVal` +func CompInt[T comdef.Xint](srcVal, dstVal T, op string) (ok bool) { + return CompValue(srcVal, dstVal, op) +} + +// CompInt64 compare int64 value. returns `srcVal op(=,!=,<,<=,>,>=) dstVal` +func CompInt64(srcVal, dstVal int64, op string) bool { + return CompValue(srcVal, dstVal, op) +} + +// CompFloat compare float64,float32 value. returns `srcVal op(=,!=,<,<=,>,>=) dstVal` +func CompFloat[T comdef.Float](srcVal, dstVal T, op string) (ok bool) { + return CompValue(srcVal, dstVal, op) +} + +// CompValue compare intX,uintX,floatX value. returns `srcVal op(=,!=,<,<=,>,>=) dstVal` +func CompValue[T comdef.XintOrFloat](srcVal, dstVal T, op string) (ok bool) { switch op { case "<", "lt": - ok = srcI64 < dstI64 + ok = srcVal < dstVal case "<=", "lte": - ok = srcI64 <= dstI64 + ok = srcVal <= dstVal case ">", "gt": - ok = srcI64 > dstI64 + ok = srcVal > dstVal case ">=", "gte": - ok = srcI64 >= dstI64 + ok = srcVal >= dstVal case "=", "eq": - ok = srcI64 == dstI64 + ok = srcVal == dstVal case "!=", "ne", "neq": - ok = srcI64 != dstI64 + ok = srcVal != dstVal } 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 +// InRange check if val in int/float range [min, max] +func InRange[T comdef.IntOrFloat](val, min, max T) bool { + return val >= min && val <= max +} + +// OutRange check if val not in int/float range [min, max] +func OutRange[T comdef.IntOrFloat](val, min, max T) bool { + return val < min || val > max +} + +// InUintRange check if val in unit range [min, max] +func InUintRange[T comdef.Uint](val, min, max T) bool { + if max == 0 { + return val >= min + } + return val >= min && val <= max } diff --git a/vendor/github.com/gookit/goutil/mathutil/convert.go b/vendor/github.com/gookit/goutil/mathutil/convert.go index 2b64ba682..4034e3918 100644 --- a/vendor/github.com/gookit/goutil/mathutil/convert.go +++ b/vendor/github.com/gookit/goutil/mathutil/convert.go @@ -172,6 +172,12 @@ func Int64(in any) (int64, error) { return ToInt64(in) } +// SafeInt64 convert value to int64, will ignore error +func SafeInt64(in any) int64 { + i64, _ := ToInt64(in) + return i64 +} + // QuietInt64 convert value to int64, will ignore error func QuietInt64(in any) int64 { i64, _ := ToInt64(in) @@ -366,7 +372,7 @@ func TryToString(val any, 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: @@ -382,8 +388,8 @@ func TryToString(val any, defaultAsErr bool) (str string, err error) { case float64: str = strconv.FormatFloat(value, 'f', -1, 64) case time.Duration: - str = strconv.FormatUint(uint64(value.Nanoseconds()), 10) - case json.Number: + str = strconv.FormatInt(int64(value), 10) + case fmt.Stringer: str = value.String() default: if defaultAsErr { diff --git a/vendor/github.com/gookit/goutil/mathutil/mathutil.go b/vendor/github.com/gookit/goutil/mathutil/mathutil.go index c379efc5b..52a2e145d 100644 --- a/vendor/github.com/gookit/goutil/mathutil/mathutil.go +++ b/vendor/github.com/gookit/goutil/mathutil/mathutil.go @@ -1,10 +1,42 @@ +// Package mathutil provide math(int, number) util functions. eg: convert, math calc, random package mathutil -import "math" +import ( + "math" -// MaxFloat compare and return max value -func MaxFloat(x, y float64) float64 { - return math.Max(x, y) + "github.com/gookit/goutil/comdef" +) + +// Min compare two value and return max value +func Min[T comdef.XintOrFloat](x, y T) T { + if x < y { + return x + } + return y +} + +// Max compare two value and return max value +func Max[T comdef.XintOrFloat](x, y T) T { + if x > y { + return x + } + return y +} + +// SwapMin compare and always return [min, max] value +func SwapMin[T comdef.XintOrFloat](x, y T) (T, T) { + if x < y { + return x, y + } + return y, x +} + +// SwapMax compare and always return [max, min] value +func SwapMax[T comdef.XintOrFloat](x, y T) (T, T) { + if x > y { + return x, y + } + return y, x } // MaxInt compare and return max value @@ -38,3 +70,16 @@ func SwapMaxI64(x, y int64) (int64, int64) { } return y, x } + +// MaxFloat compare and return max value +func MaxFloat(x, y float64) float64 { + return math.Max(x, y) +} + +// OrElse return s OR nv(new-value) on s is empty +func OrElse[T comdef.XintOrFloat](in, nv T) T { + if in != 0 { + return in + } + return nv +} diff --git a/vendor/github.com/gookit/goutil/mathutil/number.go b/vendor/github.com/gookit/goutil/mathutil/number.go index 8c5d29b47..5b1359463 100644 --- a/vendor/github.com/gookit/goutil/mathutil/number.go +++ b/vendor/github.com/gookit/goutil/mathutil/number.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "github.com/gookit/goutil/fmtutil" + "github.com/gookit/goutil/basefn" ) // IsNumeric returns true if the given character is a numeric, otherwise false. @@ -17,7 +17,6 @@ func Percent(val, total int) float64 { if total == 0 { return float64(0) } - return (float64(val) / float64(total)) * 100 } @@ -26,12 +25,13 @@ func ElapsedTime(startTime time.Time) string { return fmt.Sprintf("%.3f", time.Since(startTime).Seconds()*1000) } -// DataSize format value. alias format.DataSize() +// DataSize format value to data size string. eg: 1024 => 1KB, 1024*1024 => 1MB +// alias format.DataSize() func DataSize(size uint64) string { - return fmtutil.DataSize(size) + return basefn.DataSize(size) } // HowLongAgo calc time. alias format.HowLongAgo() func HowLongAgo(sec int64) string { - return fmtutil.HowLongAgo(sec) + return basefn.HowLongAgo(sec) } diff --git a/vendor/github.com/gookit/goutil/reflects/check.go b/vendor/github.com/gookit/goutil/reflects/check.go index c7e5c0390..acd256454 100644 --- a/vendor/github.com/gookit/goutil/reflects/check.go +++ b/vendor/github.com/gookit/goutil/reflects/check.go @@ -5,7 +5,7 @@ import ( "reflect" ) -// HasChild check. eg: array, slice, map, struct +// HasChild type check. eg: array, slice, map, struct func HasChild(v reflect.Value) bool { switch v.Kind() { case reflect.Array, reflect.Slice, reflect.Map, reflect.Struct: @@ -14,6 +14,34 @@ func HasChild(v reflect.Value) bool { return false } +// IsArrayOrSlice check. eg: array, slice +func IsArrayOrSlice(k reflect.Kind) bool { + return k == reflect.Slice || k == reflect.Array +} + +// IsSimpleKind kind in: string, bool, intX, uintX, floatX +func IsSimpleKind(k reflect.Kind) bool { + if reflect.String == k { + return true + } + return k > reflect.Invalid && k <= reflect.Float64 +} + +// IsAnyInt check is intX or uintX type +func IsAnyInt(k reflect.Kind) bool { + return k >= reflect.Int && k <= reflect.Uintptr +} + +// IsIntx check is intX or uintX type +func IsIntx(k reflect.Kind) bool { + return k >= reflect.Int && k <= reflect.Int64 +} + +// IsUintX check is intX or uintX type +func IsUintX(k reflect.Kind) bool { + return k >= reflect.Uint && k <= reflect.Uintptr +} + // IsNil reflect value func IsNil(v reflect.Value) bool { switch v.Kind() { diff --git a/vendor/github.com/gookit/goutil/reflects/conv.go b/vendor/github.com/gookit/goutil/reflects/conv.go index 16fa6fe9f..1dfa303a5 100644 --- a/vendor/github.com/gookit/goutil/reflects/conv.go +++ b/vendor/github.com/gookit/goutil/reflects/conv.go @@ -38,12 +38,19 @@ func BaseTypeVal(v reflect.Value) (value any, err error) { // 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 { + // handle kind: string, bool, intX, uintX, floatX + if typ.Kind() == reflect.String || typ.Kind() <= reflect.Float64 { return ValueByKind(val, typ.Kind()) } - // check type. like map, slice newRv := reflect.ValueOf(val) + + // try auto convert slice type + if IsArrayOrSlice(newRv.Kind()) && IsArrayOrSlice(typ.Kind()) { + return ConvSlice(newRv, typ.Elem()) + } + + // check type. like map if newRv.Type() == typ { return newRv, nil } @@ -123,6 +130,34 @@ func ValueByKind(val any, kind reflect.Kind) (rv reflect.Value, err error) { return } +// ConvSlice make new type slice from old slice, will auto convert element type. +// +// TIPs: +// +// Only support kind: string, bool, intX, uintX, floatX +func ConvSlice(oldSlRv reflect.Value, newElemTyp reflect.Type) (rv reflect.Value, err error) { + if !IsArrayOrSlice(oldSlRv.Kind()) { + panic("only allow array or slice type value") + } + + // do not need convert type + if oldSlRv.Type().Elem() == newElemTyp { + return oldSlRv, nil + } + + newSlTyp := reflect.SliceOf(newElemTyp) + newSlRv := reflect.MakeSlice(newSlTyp, 0, 0) + for i := 0; i < oldSlRv.Len(); i++ { + newElemV, err := ValueByKind(oldSlRv.Index(i).Interface(), newElemTyp.Kind()) + if err != nil { + return reflect.Value{}, err + } + + newSlRv = reflect.Append(newSlRv, newElemV) + } + return newSlRv, nil +} + // String convert func String(rv reflect.Value) string { s, _ := ValToString(rv, false) diff --git a/vendor/github.com/gookit/goutil/reflects/util.go b/vendor/github.com/gookit/goutil/reflects/util.go index 41b21e81b..44d17166b 100644 --- a/vendor/github.com/gookit/goutil/reflects/util.go +++ b/vendor/github.com/gookit/goutil/reflects/util.go @@ -4,6 +4,7 @@ import ( "fmt" "reflect" "strconv" + "unsafe" ) // Elem returns the value that the interface v contains @@ -49,23 +50,56 @@ func Len(v reflect.Value) int { return -1 } -// SliceSubKind get sub-elem kind of the array, slice, variadic-var. +// SliceSubKind get sub-elem kind of the array, slice, variadic-var. alias SliceElemKind() +func SliceSubKind(typ reflect.Type) reflect.Kind { + return SliceElemKind(typ) +} + +// SliceElemKind 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 { +// SliceElemKind(reflect.TypeOf([]string{"abc"})) // reflect.String +func SliceElemKind(typ reflect.Type) reflect.Kind { + if typ.Kind() == reflect.Slice || typ.Kind() == reflect.Array { return typ.Elem().Kind() } return reflect.Invalid } -// SetValue to a reflect.Value +// UnexportedValue quickly get unexported value by reflect.Value +// +// NOTE: this method is unsafe, use it carefully. +// should ensure rv is addressable by field.CanAddr() +// +// refer: https://stackoverflow.com/questions/42664837/how-to-access-unexported-struct-fields +func UnexportedValue(rv reflect.Value) any { + if rv.CanAddr() { + // create new value from addr, now can be read and set. + return reflect.NewAt(rv.Type(), unsafe.Pointer(rv.UnsafeAddr())).Elem().Interface() + } + + // If the rv is not addressable this trick won't work, but you can create an addressable copy like this + rs2 := reflect.New(rv.Type()).Elem() + rs2.Set(rv) + rv = rs2.Field(0) + rv = reflect.NewAt(rv.Type(), unsafe.Pointer(rv.UnsafeAddr())).Elem() + // Now rv can be read. TIP: Setting will succeed but only affects the temporary copy. + return rv.Interface() +} + +// SetUnexportedValue quickly set unexported field value by reflect +// +// NOTE: this method is unsafe, use it carefully. +// should ensure rv is addressable by field.CanAddr() +func SetUnexportedValue(rv reflect.Value, value any) { + reflect.NewAt(rv.Type(), unsafe.Pointer(rv.UnsafeAddr())).Elem().Set(reflect.ValueOf(value)) +} + +// SetValue to a `reflect.Value`. will auto convert type if needed. 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)) @@ -76,12 +110,44 @@ func SetValue(rv reflect.Value, val any) error { } rv1, err := ValueByType(val, rv.Type()) - if err != nil { - return err + if err == nil { + rv.Set(rv1) + } + return err +} + +// SetRValue to a `reflect.Value`. will direct set value without convert type. +func SetRValue(rv, val reflect.Value) { + if rv.Kind() == reflect.Ptr { + if rv.IsNil() { + elemTyp := rv.Type().Elem() + rv.Set(reflect.New(elemTyp)) + } + rv = reflect.Indirect(rv) } - rv.Set(rv1) - return nil + rv.Set(val) +} + +// EachMap process any map data +func EachMap(mp reflect.Value, fn func(key, val reflect.Value)) { + if fn == nil { + return + } + if mp.Kind() != reflect.Map { + panic("only allow map value data") + } + + for _, key := range mp.MapKeys() { + fn(key, mp.MapIndex(key)) + } +} + +// EachStrAnyMap process any map data as string key and any value +func EachStrAnyMap(mp reflect.Value, fn func(key string, val any)) { + EachMap(mp, func(key, val reflect.Value) { + fn(String(key), val.Interface()) + }) } // FlatFunc custom collect handle func diff --git a/vendor/github.com/gookit/goutil/stdio/ioutil.go b/vendor/github.com/gookit/goutil/stdio/ioutil.go index 64b0861ec..1bf65c0f4 100644 --- a/vendor/github.com/gookit/goutil/stdio/ioutil.go +++ b/vendor/github.com/gookit/goutil/stdio/ioutil.go @@ -7,8 +7,8 @@ import ( ) // QuietFprint to writer, will ignore error -func QuietFprint(w io.Writer, ss ...string) { - _, _ = fmt.Fprint(w, strings.Join(ss, "")) +func QuietFprint(w io.Writer, a ...any) { + _, _ = fmt.Fprint(w, a...) } // QuietFprintf to writer, will ignore error @@ -17,25 +17,11 @@ func QuietFprintf(w io.Writer, tpl string, vs ...any) { } // QuietFprintln to writer, will ignore error -func QuietFprintln(w io.Writer, ss ...string) { - _, _ = fmt.Fprintln(w, strings.Join(ss, "")) +func QuietFprintln(w io.Writer, a ...any) { + _, _ = fmt.Fprintln(w, a...) } // 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 index 8a0146e8c..48f773863 100644 --- a/vendor/github.com/gookit/goutil/stdio/stdio.go +++ b/vendor/github.com/gookit/goutil/stdio/stdio.go @@ -1,2 +1,89 @@ // Package stdio provide some standard IO util functions. package stdio + +import ( + "bufio" + "bytes" + "io" + "os" + "strings" +) + +// DiscardReader anything from the reader +func DiscardReader(src io.Reader) { + _, _ = io.Copy(io.Discard, src) +} + +// ReadString read contents from io.Reader +func ReadString(r io.Reader) string { + bs, err := io.ReadAll(r) + if err != nil { + return "" + } + return string(bs) +} + +// 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 +} + +// NewIOReader instance by input: string, bytes, io.Reader +func NewIOReader(in any) io.Reader { + switch typIn := in.(type) { + case []byte: + return bytes.NewReader(typIn) + case string: + return strings.NewReader(typIn) + case io.Reader: + return typIn + } + panic("invalid input type for create reader") +} + +// NewScanner instance by input data or reader +func NewScanner(in any) *bufio.Scanner { + switch typIn := in.(type) { + case io.Reader: + return bufio.NewScanner(typIn) + case []byte: + return bufio.NewScanner(bytes.NewReader(typIn)) + case string: + return bufio.NewScanner(strings.NewReader(typIn)) + case *bufio.Scanner: + return typIn + default: + panic("invalid input type for create scanner") + } +} + +// WriteByte to stdout +func WriteByte(b byte) { + _, _ = os.Stdout.Write([]byte{b}) +} + +// WriteBytes to stdout +func WriteBytes(bs []byte) { + _, _ = os.Stdout.Write(bs) +} + +// WritelnBytes to stdout +func WritelnBytes(bs []byte) { + _, _ = os.Stdout.Write(bs) + _, _ = os.Stdout.Write([]byte("\n")) +} + +// WriteString to stdout +func WriteString(s string) { + _, _ = os.Stdout.WriteString(s) +} + +// Writeln to stdout +func Writeln(s string) { + _, _ = os.Stdout.WriteString(s) + _, _ = os.Stdout.Write([]byte("\n")) +} diff --git a/vendor/github.com/gookit/goutil/stdio/writer.go b/vendor/github.com/gookit/goutil/stdio/writer.go index 76070cbb1..8a9b9fc23 100644 --- a/vendor/github.com/gookit/goutil/stdio/writer.go +++ b/vendor/github.com/gookit/goutil/stdio/writer.go @@ -33,6 +33,9 @@ func (w *WriteWrapper) WriteByte(c byte) error { // WriteString data func (w *WriteWrapper) WriteString(s string) (n int, err error) { + if sw, ok := w.Out.(io.StringWriter); ok { + return sw.WriteString(s) + } return w.Out.Write([]byte(s)) } diff --git a/vendor/github.com/gookit/goutil/structs/convert.go b/vendor/github.com/gookit/goutil/structs/convert.go index 3535f89bf..8a379fe02 100644 --- a/vendor/github.com/gookit/goutil/structs/convert.go +++ b/vendor/github.com/gookit/goutil/structs/convert.go @@ -2,7 +2,11 @@ package structs import ( "errors" + "fmt" "reflect" + + "github.com/gookit/goutil/maputil" + "github.com/gookit/goutil/reflects" ) // ToMap quickly convert structs to map by reflect @@ -25,16 +29,73 @@ func TryToMap(st any, optFns ...MapOptFunc) (map[string]any, error) { return StructToMap(st, optFns...) } +// ToSMap quickly and safe convert structs to map[string]string by reflect +func ToSMap(st any, optFns ...MapOptFunc) map[string]string { + mp, _ := StructToMap(st, optFns...) + return maputil.ToStringMap(mp) +} + +// TryToSMap quickly convert structs to map[string]string by reflect +func TryToSMap(st any, optFns ...MapOptFunc) (map[string]string, error) { + mp, err := StructToMap(st, optFns...) + if err != nil { + return nil, err + } + return maputil.ToStringMap(mp), nil +} + +// MustToSMap alias of ToStringMap(), but will panic on error +func MustToSMap(st any, optFns ...MapOptFunc) map[string]string { + mp, err := StructToMap(st, optFns...) + if err != nil { + panic(err) + } + return maputil.ToStringMap(mp) +} + +// ToString quickly format struct to string +func ToString(st any, optFns ...MapOptFunc) string { + mp, err := StructToMap(st, optFns...) + if err == nil { + return maputil.ToString(mp) + } + return fmt.Sprint(st) +} + const defaultFieldTag = "json" -// MapOptions struct +// MapOptions for convert struct to map type MapOptions struct { + // TagName for map filed. default is "json" TagName string + // ParseDepth for parse. TODO support depth + ParseDepth int + // MergeAnonymous struct fields to parent map. default is true + MergeAnonymous bool + // ExportPrivate export private fields. default is false + ExportPrivate bool } // MapOptFunc define type MapOptFunc func(opt *MapOptions) +// WithMapTagName set tag name for map field +func WithMapTagName(tagName string) MapOptFunc { + return func(opt *MapOptions) { + opt.TagName = tagName + } +} + +// MergeAnonymous merge anonymous struct fields to parent map +func MergeAnonymous(opt *MapOptions) { + opt.MergeAnonymous = true +} + +// ExportPrivate merge anonymous struct fields to parent map +func ExportPrivate(opt *MapOptions) { + opt.ExportPrivate = true +} + // 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) { @@ -43,13 +104,9 @@ func StructToMap(st any, optFns ...MapOptFunc) (map[string]any, error) { return mp, nil } - obj := reflect.ValueOf(st) - if obj.Kind() == reflect.Ptr { - obj = obj.Elem() - } - + obj := reflect.Indirect(reflect.ValueOf(st)) if obj.Kind() != reflect.Struct { - return mp, errors.New("must be an struct") + return mp, errors.New("must be an struct value") } opt := &MapOptions{TagName: defaultFieldTag} @@ -57,23 +114,25 @@ func StructToMap(st any, optFns ...MapOptFunc) (map[string]any, error) { fn(opt) } - mp, err := structToMap(obj, opt.TagName) + _, err := structToMap(obj, opt, mp) return mp, err } -func structToMap(obj reflect.Value, tagName string) (map[string]any, error) { - refType := obj.Type() - mp := make(map[string]any) +func structToMap(obj reflect.Value, opt *MapOptions, mp map[string]any) (map[string]any, error) { + if mp == nil { + mp = make(map[string]any) + } + refType := obj.Type() 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' { + // skip un-exported field + if !opt.ExportPrivate && IsUnexported(name) { continue } - tagVal, ok := ft.Tag.Lookup(tagName) + tagVal, ok := ft.Tag.Lookup(opt.TagName) if ok && tagVal != "" { sMap, err := ParseTagValueDefault(name, tagVal) if err != nil { @@ -81,24 +140,33 @@ func structToMap(obj reflect.Value, tagName string) (map[string]any, error) { } name = sMap.Default("name", name) - // un-exported field - if name == "" { + if name == "" { // un-exported field continue } } - field := obj.Field(i) + field := reflect.Indirect(obj.Field(i)) if field.Kind() == reflect.Struct { - sub, err := structToMap(field, tagName) - if err != nil { - return nil, err + // collect anonymous struct values to parent. + if ft.Anonymous && opt.MergeAnonymous { + _, err := structToMap(field, opt, mp) + if err != nil { + return nil, err + } + } else { // collect struct values to submap + sub, err := structToMap(field, opt, nil) + if err != nil { + return nil, err + } + mp[name] = sub } - mp[name] = sub continue } if field.CanInterface() { mp[name] = field.Interface() + } else if field.CanAddr() { // for unexported field + mp[name] = reflects.UnexportedValue(field) } } diff --git a/vendor/github.com/gookit/goutil/structs/copy.go b/vendor/github.com/gookit/goutil/structs/copy.go new file mode 100644 index 000000000..d42735160 --- /dev/null +++ b/vendor/github.com/gookit/goutil/structs/copy.go @@ -0,0 +1,6 @@ +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/data.go b/vendor/github.com/gookit/goutil/structs/data.go index 63702110b..ae0081898 100644 --- a/vendor/github.com/gookit/goutil/structs/data.go +++ b/vendor/github.com/gookit/goutil/structs/data.go @@ -173,3 +173,22 @@ func (d *Data) BoolVal(key string) bool { func (d *Data) String() string { return maputil.ToString(d.data) } + +// OrderedMap data TODO +type OrderedMap struct { + maputil.Data + len int + keys []string + // vals []any +} + +// NewOrderedMap instance. +func NewOrderedMap(len int) *OrderedMap { + return &OrderedMap{len: len} +} + +// Set key and value to map +func (om *OrderedMap) Set(key string, val any) { + om.keys = append(om.keys, key) + om.Data.Set(key, val) +} diff --git a/vendor/github.com/gookit/goutil/structs/init.go b/vendor/github.com/gookit/goutil/structs/init.go new file mode 100644 index 000000000..7999b857e --- /dev/null +++ b/vendor/github.com/gookit/goutil/structs/init.go @@ -0,0 +1,179 @@ +package structs + +import ( + "errors" + "reflect" + + "github.com/gookit/goutil/internal/comfunc" + "github.com/gookit/goutil/reflects" + "github.com/gookit/goutil/strutil" +) + +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 +} + +// Init struct default value by field "default" tag. +func Init(ptr any, optFns ...InitOptFunc) error { + return InitDefaults(ptr, optFns...) +} + +// InitDefaults init struct default value by field "default" tag. +// +// TIPS: +// +// Support init field types: string, bool, intX, uintX, floatX, array, slice +// +// 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++ { + sf := rt.Field(i) + // skip don't exported field + if IsUnexported(sf.Name) { + continue + } + + val, hasTag := sf.Tag.Lookup(opt.TagName) + if !hasTag || val == "-" { + 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() { + // special: handle for pointer struct field + if fv.Kind() == reflect.Pointer { + fv = fv.Elem() + if fv.Kind() == reflect.Struct { + if err := initDefaults(fv, opt); err != nil { + return err + } + } + } + continue + } + + // handle for pointer field + if fv.Kind() == reflect.Pointer { + if fv.IsNil() { + fv.Set(reflect.New(fv.Type().Elem())) + } + + fv = fv.Elem() + if fv.Kind() == reflect.Struct { + if err := initDefaults(fv, opt); err != nil { + return err + } + continue + } + } else if fv.Kind() == reflect.Slice { + el := sf.Type.Elem() + isPtr := el.Kind() == reflect.Pointer + if isPtr { + el = el.Elem() + } + + // init sub struct in slice. like `[]SubStruct` or `[]*SubStruct` + if el.Kind() == reflect.Struct { + // make sub-struct and init. like: `SubStruct` + subFv := reflect.New(el) + subFvE := subFv.Elem() + if err := initDefaults(subFvE, opt); err != nil { + return err + } + + // make new slice and set value. + newFv := reflect.MakeSlice(reflect.SliceOf(sf.Type.Elem()), 0, 1) + if isPtr { + newFv = reflect.Append(newFv, subFv) + } else { + newFv = reflect.Append(newFv, subFvE) + } + fv.Set(newFv) + continue + } + } + + if err := initDefaultValue(fv, val, 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) + } + + var anyVal any = val + + // simple slice: convert simple kind(string,intX,uintX,...) to slice. eg: "1,2,3" => []int{1,2,3} + if reflects.IsArrayOrSlice(fv.Kind()) && reflects.IsSimpleKind(reflects.SliceElemKind(fv.Type())) { + ss := strutil.SplitTrimmed(val, ",") + valRv, err := reflects.ConvSlice(reflect.ValueOf(ss), fv.Type().Elem()) + if err == nil { + reflects.SetRValue(fv, valRv) + } + return err + } + + // set value + return reflects.SetValue(fv, anyVal) +} diff --git a/vendor/github.com/gookit/goutil/structs/structs.go b/vendor/github.com/gookit/goutil/structs/structs.go index fb09db0ce..c671f21cd 100644 --- a/vendor/github.com/gookit/goutil/structs/structs.go +++ b/vendor/github.com/gookit/goutil/structs/structs.go @@ -1,7 +1,15 @@ // 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 -// } +// IsExported field name on struct +func IsExported(name string) bool { + return name[0] >= 'A' && name[0] <= 'Z' +} + +// IsUnexported field name on struct +func IsUnexported(name string) bool { + if name[0] == '_' { + return true + } + return name[0] >= 'a' && name[0] <= 'z' +} diff --git a/vendor/github.com/gookit/goutil/structs/tags.go b/vendor/github.com/gookit/goutil/structs/tags.go index b6118998d..01ab9ff4d 100644 --- a/vendor/github.com/gookit/goutil/structs/tags.go +++ b/vendor/github.com/gookit/goutil/structs/tags.go @@ -204,6 +204,14 @@ func ParseTagValueDefault(field, tagVal string) (mp maputil.SMap, err error) { return } +// ParseTagValueQuick quick parse tag value string by sep(;) +func ParseTagValueQuick(tagVal string, defines []string) maputil.SMap { + parseFn := ParseTagValueDefine(";", defines) + + mp, _ := parseFn("", tagVal) + return mp +} + // ParseTagValueDefine parse tag value string by given defines. // // Examples: @@ -235,7 +243,11 @@ func ParseTagValueDefine(sep string, defines []string) TagValFunc { // ParseTagValueNamed parse k-v tag value string. it's like INI format contents. // -// eg: "name=int0;shorts=i;required=true;desc=int option message" +// Examples: +// +// eg: "name=val0;shorts=i;required=true;desc=a message" +// => +// {name: val0, shorts: i, required: true, desc: a message} func ParseTagValueNamed(field, tagVal string, keys ...string) (mp maputil.SMap, err error) { ss := strutil.Split(tagVal, ";") ln := len(ss) @@ -250,8 +262,7 @@ func ParseTagValueNamed(field, tagVal string, keys ...string) (mp maputil.SMap, return } - kvNodes := strings.SplitN(s, "=", 2) - key, val := kvNodes[0], strings.TrimSpace(kvNodes[1]) + key, val := strutil.TrimCut(s, "=") if len(keys) > 0 && !arrutil.StringsHas(keys, key) { err = fmt.Errorf("parse tag error on field '%s': invalid key name '%s'", field, key) return diff --git a/vendor/github.com/gookit/goutil/structs/value.go b/vendor/github.com/gookit/goutil/structs/value.go index 481e8a32b..52ae940c9 100644 --- a/vendor/github.com/gookit/goutil/structs/value.go +++ b/vendor/github.com/gookit/goutil/structs/value.go @@ -35,6 +35,11 @@ func (v *Value) Val() any { return v.V } +// Val get +// func (v *Value) ValOr[T any](defVal T) T { +// return v.V +// } + // Int value get func (v *Value) Int() int { if v.V == nil { @@ -102,7 +107,7 @@ func (v *Value) Strings() (ss []string) { return } -// SplitToStrings split string value to strings +// SplitToStrings split string value to strings. sep default is comma(,) func (v *Value) SplitToStrings(sep ...string) (ss []string) { if v.V == nil { return @@ -114,7 +119,7 @@ func (v *Value) SplitToStrings(sep ...string) (ss []string) { return } -// SplitToInts split string value to []int +// SplitToInts split string value to []int. sep default is comma(,) func (v *Value) SplitToInts(sep ...string) (ss []int) { if v.V == nil { return diff --git a/vendor/github.com/gookit/goutil/structs/wrapper.go b/vendor/github.com/gookit/goutil/structs/wrapper.go new file mode 100644 index 000000000..59bfe3b6d --- /dev/null +++ b/vendor/github.com/gookit/goutil/structs/wrapper.go @@ -0,0 +1,51 @@ +package structs + +import "reflect" + +// Wrapper struct for read or set field value TODO +type Wrapper struct { + // src any // source data struct + rv reflect.Value + + // FieldTagName field name for read/write value. default tag: json + FieldTagName string +} + +// Wrap create a struct wrapper +func Wrap(src any) *Wrapper { + return NewWrapper(src) +} + +// NewWrapper create a struct wrapper +func NewWrapper(src any) *Wrapper { + return WrapValue(reflect.ValueOf(src)) +} + +// WrapValue create a struct wrapper +func WrapValue(rv reflect.Value) *Wrapper { + rv = reflect.Indirect(rv) + if rv.Kind() != reflect.Struct { + panic("must be provider an struct value") + } + + return &Wrapper{rv: rv} +} + +// Get field value by name +func (r *Wrapper) Get(name string) any { + val, ok := r.Lookup(name) + if !ok { + return nil + } + return val +} + +// Lookup field value by name +func (r *Wrapper) Lookup(name string) (val any, ok bool) { + fv := r.rv.FieldByName(name) + if !fv.IsValid() { + return + } + + return fv.Interface(), true +} diff --git a/vendor/github.com/gookit/goutil/structs/setval.go b/vendor/github.com/gookit/goutil/structs/writer.go similarity index 50% rename from vendor/github.com/gookit/goutil/structs/setval.go rename to vendor/github.com/gookit/goutil/structs/writer.go index 6461540f0..ac306e110 100644 --- a/vendor/github.com/gookit/goutil/structs/setval.go +++ b/vendor/github.com/gookit/goutil/structs/writer.go @@ -5,111 +5,22 @@ import ( "fmt" "reflect" - "github.com/gookit/goutil/internal/comfunc" + "github.com/gookit/goutil/maputil" "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 { +// NewWriter create a struct writer +func NewWriter(ptr any) *Wrapper { rv := reflect.ValueOf(ptr) - if rv.Kind() != reflect.Ptr { - return errors.New("must be provider an pointer value") + if rv.Kind() != reflect.Pointer { + panic("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) + return WrapValue(rv) } /************************************************************* - * load values to a struct + * set values to a struct *************************************************************/ // SetOptFunc define @@ -127,15 +38,22 @@ type SetOptions struct { // // 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 +// WithParseDefault value by tag "default" +func WithParseDefault(opt *SetOptions) { + opt.ParseDefault = true +} + +// SetValues set values to struct ptr from map data. // // TIPS: // @@ -152,7 +70,7 @@ func SetValues(ptr any, data map[string]any, optFns ...SetOptFunc) error { } opt := &SetOptions{ - FieldTagName: "json", + FieldTagName: defaultFieldTag, DefaultValTag: defaultInitTag, } @@ -163,7 +81,7 @@ func SetValues(ptr any, data map[string]any, optFns ...SetOptFunc) error { } func setValues(rv reflect.Value, data map[string]any, opt *SetOptions) error { - if data == nil || len(data) == 0 { + if len(data) == 0 { return nil } @@ -184,7 +102,6 @@ func setValues(rv reflect.Value, data map[string]any, opt *SetOptions) error { if err != nil { return err } - name = info.Get("name") } @@ -192,20 +109,27 @@ func setValues(rv reflect.Value, data map[string]any, opt *SetOptions) error { val, ok := data[name] // set field value by default tag. - if !ok && fv.IsZero() { + if !ok && opt.ParseDefault && fv.IsZero() { defVal := ft.Tag.Get(opt.DefaultValTag) - if err := initDefaultValue(fv, defVal, opt.ParseDefaultEnv); err != nil { return err } continue } + // handle for pointer field + if fv.Kind() == reflect.Pointer { + if fv.IsNil() { + fv.Set(reflect.New(fv.Type().Elem())) + } + fv = fv.Elem() + } + // 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") + asMp, err := maputil.TryAnyMap(val) + if err != nil { + return fmt.Errorf("must provide map data for field %q, err=%v", ft.Name, err) } if err := setValues(fv, asMp, opt); err != nil { diff --git a/vendor/github.com/gookit/goutil/strutil/bytes.go b/vendor/github.com/gookit/goutil/strutil/bytes.go index fd1f54973..1b39b1832 100644 --- a/vendor/github.com/gookit/goutil/strutil/bytes.go +++ b/vendor/github.com/gookit/goutil/strutil/bytes.go @@ -1,69 +1,17 @@ package strutil import ( - "bytes" - "fmt" - "strings" + "github.com/gookit/goutil/byteutil" ) // Buffer wrap and extends the bytes.Buffer -type Buffer struct { - bytes.Buffer -} +type Buffer = byteutil.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: @@ -72,57 +20,9 @@ func (b *Buffer) ResetAndGet() string { // buf:=bp.Get() // defer bp.Put(buf) // // use buf do something ... -// -// refer https://www.flysnow.org/2020/08/21/golang-chan-byte-pool.html -// from https://github.com/minio/minio/blob/master/internal/bpool/bpool.go -type ByteChanPool struct { - c chan []byte - w int - wcap int -} +type ByteChanPool = byteutil.ChanPool // NewByteChanPool instance -func NewByteChanPool(maxSize int, width int, capWidth int) *ByteChanPool { - return &ByteChanPool{ - c: make(chan []byte, maxSize), - w: width, - wcap: capWidth, - } -} - -// Get gets a []byte from the BytePool, or creates a new one if none are -// available in the pool. -func (bp *ByteChanPool) Get() (b []byte) { - select { - case b = <-bp.c: - // reuse existing buffer - default: - // create new buffer - if bp.wcap > 0 { - b = make([]byte, bp.w, bp.wcap) - } else { - b = make([]byte, bp.w) - } - } - return -} - -// Put returns the given Buffer to the BytePool. -func (bp *ByteChanPool) Put(b []byte) { - select { - case bp.c <- b: - // buffer went back into pool - default: - // buffer didn't go back into pool, just discard - } -} - -// Width returns the width of the byte arrays in this pool. -func (bp *ByteChanPool) Width() (n int) { - return bp.w -} - -// WidthCap returns the cap width of the byte arrays in this pool. -func (bp *ByteChanPool) WidthCap() (n int) { - return bp.wcap +func NewByteChanPool(maxSize, width, capWidth int) *ByteChanPool { + return byteutil.NewChanPool(maxSize, width, capWidth) } diff --git a/vendor/github.com/gookit/goutil/strutil/check.go b/vendor/github.com/gookit/goutil/strutil/check.go index 194dd641e..8654d8412 100644 --- a/vendor/github.com/gookit/goutil/strutil/check.go +++ b/vendor/github.com/gookit/goutil/strutil/check.go @@ -1,6 +1,7 @@ package strutil import ( + "path" "regexp" "strings" "unicode" @@ -10,22 +11,13 @@ import ( // Equal check, alias of strings.EqualFold var Equal = strings.EqualFold -// NoCaseEq check two strings is equals and case-insensitivity -func NoCaseEq(s, t string) bool { - return strings.EqualFold(s, t) -} - // IsNumChar returns true if the given character is a numeric, otherwise false. -func IsNumChar(c byte) bool { - return c >= '0' && c <= '9' -} +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) -} +func IsNumeric(s string) bool { return numReg.MatchString(s) } // IsAlphabet char func IsAlphabet(char uint8) bool { @@ -47,16 +39,31 @@ func IsAlphaNum(c uint8) bool { } // StrPos alias of the strings.Index -func StrPos(s, sub string) int { - return strings.Index(s, sub) -} +func StrPos(s, sub string) int { return strings.Index(s, sub) } // BytePos alias of the strings.IndexByte -func BytePos(s string, bt byte) int { - return strings.IndexByte(s, bt) +func BytePos(s string, bt byte) int { return strings.IndexByte(s, bt) } + +// IEqual ignore case check given two string is equals. +func IEqual(s1, s2 string) bool { return strings.EqualFold(s1, s2) } + +// NoCaseEq check two strings is equals and case-insensitivity +func NoCaseEq(s, t string) bool { return strings.EqualFold(s, t) } + +// IContains ignore case check substr in the given string. +func IContains(s, sub string) bool { + return strings.Contains(strings.ToLower(s), strings.ToLower(sub)) } -// HasOneSub substr in the given string. +// ContainsByte in given string. +func ContainsByte(s string, c byte) bool { + return strings.IndexByte(s, c) >= 0 +} + +// ContainsOne substr(s) in the given string. alias of HasOneSub() +func ContainsOne(s string, subs []string) bool { return HasOneSub(s, subs) } + +// HasOneSub substr(s) in the given string. func HasOneSub(s string, subs []string) bool { for _, sub := range subs { if strings.Contains(s, sub) { @@ -66,6 +73,9 @@ func HasOneSub(s string, subs []string) bool { return false } +// ContainsAll substr(s) in the given string. alias of HasAllSubs() +func ContainsAll(s string, subs []string) bool { return HasAllSubs(s, subs) } + // HasAllSubs all substr in the given string. func HasAllSubs(s string, subs []string) bool { for _, sub := range subs { @@ -103,6 +113,16 @@ func HasSuffix(s string, suffix string) bool { return strings.HasSuffix(s, suffi // IsEndOf alias of the strings.HasSuffix func IsEndOf(s, suffix string) bool { return strings.HasSuffix(s, suffix) } +// HasOneSuffix the string end withs one of the subs +func HasOneSuffix(s string, suffixes []string) bool { + for _, suffix := range suffixes { + if strings.HasSuffix(s, suffix) { + return true + } + } + return false +} + // IsValidUtf8 valid utf8 string check func IsValidUtf8(s string) bool { return utf8.ValidString(s) } @@ -112,22 +132,16 @@ func IsValidUtf8(s string) bool { return utf8.ValidString(s) } 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 -} +func IsSpace(c byte) bool { return spaceTable[c] == 1 } // IsEmpty returns true if the given string is empty. 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 { - return IsBlankBytes([]byte(s)) -} +func IsBlank(s string) bool { return IsBlankBytes([]byte(s)) } // IsNotBlank returns true if the given string is not blank. -func IsNotBlank(s string) bool { - return !IsBlankBytes([]byte(s)) -} +func IsNotBlank(s string) bool { return !IsBlankBytes([]byte(s)) } // IsBlankBytes returns true if the given []byte is all space characters. func IsBlankBytes(bs []byte) bool { @@ -140,21 +154,35 @@ func IsBlankBytes(bs []byte) bool { } // IsSymbol reports whether the rune is a symbolic character. -func IsSymbol(r rune) bool { - return unicode.IsSymbol(r) +func IsSymbol(r rune) bool { return unicode.IsSymbol(r) } + +// HasEmpty value for input strings +func HasEmpty(ss ...string) bool { + for _, s := range ss { + if s == "" { + return true + } + } + return false +} + +// IsAllEmpty for input strings +func IsAllEmpty(ss ...string) bool { + for _, s := range ss { + if s != "" { + return false + } + } + return true } var verRegex = regexp.MustCompile(`^[0-9][\d.]+(-\w+)?$`) // IsVersion number. eg: 1.2.0 -func IsVersion(s string) bool { - return verRegex.MatchString(s) -} +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) -} +func Compare(s1, s2, op string) bool { return VersionCompare(s1, s2, op) } // VersionCompare for two version string. func VersionCompare(v1, v2, op string) bool { @@ -173,3 +201,128 @@ func VersionCompare(v1, v2, op string) bool { return v1 == v2 } } + +// SimpleMatch all sub-string in the give text string. +// +// Difference the ContainsAll: +// +// - start with ^ for exclude contains check. +// - end with $ for check end with keyword. +func SimpleMatch(s string, keywords []string) bool { + for _, keyword := range keywords { + kln := len(keyword) + if kln == 0 { + continue + } + + // exclude + if kln > 1 && keyword[0] == '^' { + if strings.Contains(s, keyword[1:]) { + return false + } + continue + } + + // end with + if kln > 1 && keyword[kln-1] == '$' { + return strings.HasSuffix(s, keyword[:kln-1]) + } + + // include + if !strings.Contains(s, keyword) { + return false + } + } + return true +} + +// QuickMatch check for a string. pattern can be a sub string. +func QuickMatch(pattern, s string) bool { + if strings.ContainsRune(pattern, '*') { + return GlobMatch(pattern, s) + } + return strings.Contains(s, pattern) +} + +// PathMatch check for a string match the pattern. alias of the path.Match() +// +// TIP: `*` can match any char, not contain `/`. +func PathMatch(pattern, s string) bool { + ok, err := path.Match(pattern, s) + if err != nil { + ok = false + } + return ok +} + +// GlobMatch check for a string match the pattern. +// +// Difference with PathMatch() is: `*` can match any char, contain `/`. +func GlobMatch(pattern, s string) bool { + // replace `/` to `S` for path.Match + pattern = strings.Replace(pattern, "/", "S", -1) + s = strings.Replace(s, "/", "S", -1) + + ok, err := path.Match(pattern, s) + if err != nil { + ok = false + } + return ok +} + +// LikeMatch simple check for a string match the pattern. pattern like the SQL LIKE. +func LikeMatch(pattern, s string) bool { + ln := len(pattern) + if ln < 2 { + return false + } + + // eg `%abc` `%abc%` + if pattern[0] == '%' { + if ln > 2 && pattern[ln-1] == '%' { + return strings.Contains(s, pattern[1:ln-1]) + } else { + return strings.HasSuffix(s, pattern[1:]) + } + } + + // eg `abc%` + if pattern[ln-1] == '%' { + return strings.HasPrefix(s, pattern[:ln-1]) + } + return pattern == s +} + +// MatchNodePath check for a string match the pattern. +// +// Use on pattern: +// - `*` match any to sep +// - `**` match any to end. only allow at start or end on pattern. +// +// Example: +// +// strutil.MatchNodePath() +func MatchNodePath(pattern, s string, sep string) bool { + if pattern == "**" || pattern == s { + return true + } + if pattern == "" { + return len(s) == 0 + } + + if i := strings.Index(pattern, "**"); i >= 0 { + if i == 0 { // at start + return strings.HasSuffix(s, pattern[2:]) + } + return strings.HasPrefix(s, pattern[:len(pattern)-2]) + } + + pattern = strings.Replace(pattern, sep, "/", -1) + s = strings.Replace(s, sep, "/", -1) + + ok, err := path.Match(pattern, s) + if err != nil { + ok = false + } + return ok +} diff --git a/vendor/github.com/gookit/goutil/strutil/convert.go b/vendor/github.com/gookit/goutil/strutil/convert.go index 5ae183a25..067876d40 100644 --- a/vendor/github.com/gookit/goutil/strutil/convert.go +++ b/vendor/github.com/gookit/goutil/strutil/convert.go @@ -1,7 +1,6 @@ package strutil import ( - "encoding/json" "errors" "fmt" "reflect" @@ -17,8 +16,10 @@ import ( ) var ( - ErrDateLayout = errors.New("invalid date layout string") - ErrInvalidParam = errors.New("invalid input parameter") + // ErrDateLayout error + ErrDateLayout = errors.New("invalid date layout string") + // ErrInvalidParam error + ErrInvalidParam = errors.New("invalid input for parse time") // some regex for convert string. toSnakeReg = regexp.MustCompile("[A-Z][a-z]") @@ -39,6 +40,8 @@ var ( func Quote(s string) string { return strconv.Quote(s) } // Unquote remove start and end quotes by single-quote or double-quote +// +// tip: strconv.Unquote cannot unquote single-quote func Unquote(s string) string { ln := len(s) if ln < 2 { @@ -70,6 +73,16 @@ 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) } +// JoinAny type to string +func JoinAny(sep string, parts ...any) string { + ss := make([]string, 0, len(parts)) + for _, part := range parts { + ss = append(ss, QuietString(part)) + } + + return strings.Join(ss, sep) +} + // Implode alias of strings.Join func Implode(sep string, ss ...string) string { return strings.Join(ss, sep) } @@ -77,39 +90,48 @@ func Implode(sep string, ss ...string) string { return strings.Join(ss, sep) } * convert value to string *************************************************************/ -// String convert val to string +// String convert value to string, return error on failed func String(val any) (string, error) { return AnyToString(val, true) } +// ToString convert value to string, return error on failed +func ToString(val any) (string, error) { + return AnyToString(val, true) +} + // 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 { +// SafeString convert value to string, will ignore error +func SafeString(in any) string { val, _ := AnyToString(in, false) return val } +// MustString convert value to string, will panic on error +func MustString(in any) string { + val, err := AnyToString(in, false) + if err != nil { + panic(err) + } + 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 any) (string, error) { - return AnyToString(val, true) -} - // AnyToString convert value to string. // -// if defaultAsErr: +// For defaultAsErr: // -// False will use fmt.Sprint convert complex type -// True will return error on fail. +// - 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 @@ -147,8 +169,8 @@ func AnyToString(val any, defaultAsErr bool) (str string, err error) { case []byte: str = string(value) case time.Duration: - str = value.String() - case json.Number: + str = strconv.FormatInt(int64(value), 10) + case fmt.Stringer: str = value.String() default: if defaultAsErr { @@ -203,7 +225,10 @@ func QuietBool(s string) bool { // MustBool convert, will panic on error func MustBool(s string) bool { - val, _ := comfunc.StrToBool(strings.TrimSpace(s)) + val, err := comfunc.StrToBool(strings.TrimSpace(s)) + if err != nil { + panic(err) + } return val } @@ -226,6 +251,12 @@ func ToInt(s string) (int, error) { return strconv.Atoi(strings.TrimSpace(s)) } +// Int2 convert string to int, will ignore error +func Int2(s string) int { + val, _ := ToInt(s) + return val +} + // QuietInt convert string to int, will ignore error func QuietInt(s string) int { val, _ := ToInt(s) @@ -234,8 +265,7 @@ func QuietInt(s string) int { // MustInt convert string to int, will panic on error func MustInt(s string) int { - val, _ := ToInt(s) - return val + return IntOrPanic(s) } // IntOrPanic convert value to int, will panic on error @@ -334,87 +364,8 @@ func ToSlice(s string, sep ...string) []string { // return cliutil.StringToOSArgs(s) // error: import cycle not allowed // } -// MustToTime convert date string to time.Time -func MustToTime(s string, layouts ...string) time.Time { - t, err := ToTime(s, layouts...) - if err != nil { - panic(err) - } - 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) { - // custom layout - if len(layouts) > 0 { - if len(layouts[0]) > 0 { - return time.Parse(layouts[0], s) - } - - 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) - } - - // 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.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) + return comfunc.ToDuration(s) } diff --git a/vendor/github.com/gookit/goutil/strutil/crypto.go b/vendor/github.com/gookit/goutil/strutil/crypto.go new file mode 100644 index 000000000..7c5337118 --- /dev/null +++ b/vendor/github.com/gookit/goutil/strutil/crypto.go @@ -0,0 +1,54 @@ +package strutil + +import ( + "crypto/hmac" + "crypto/md5" + "crypto/sha256" + "encoding/hex" + "fmt" +) + +// 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) +} + +// HashPasswd for quick hash an input password string +func HashPasswd(pwd, key string) string { + hm := hmac.New(sha256.New, []byte(key)) + hm.Write([]byte(pwd)) + + return hex.EncodeToString(hm.Sum(nil)) +} + +// VerifyPasswd for quick verify input password is valid +// +// - pwdMAC from db or config, generated by EncryptPasswd() +func VerifyPasswd(pwdMAC, pwd, key string) bool { + decBts, err := hex.DecodeString(pwdMAC) + if err != nil { + return false + } + + hm := hmac.New(sha256.New, []byte(key)) + hm.Write([]byte(pwd)) + + return hmac.Equal(decBts, hm.Sum(nil)) +} diff --git a/vendor/github.com/gookit/goutil/strutil/encode.go b/vendor/github.com/gookit/goutil/strutil/encode.go index a102e1881..70d50a8e1 100644 --- a/vendor/github.com/gookit/goutil/strutil/encode.go +++ b/vendor/github.com/gookit/goutil/strutil/encode.go @@ -2,11 +2,8 @@ package strutil import ( "bytes" - "crypto/md5" "encoding/base32" "encoding/base64" - "encoding/hex" - "fmt" "net/url" "strings" "text/template" @@ -73,28 +70,6 @@ func StripSlashes(s string) 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. func URLEncode(s string) string { if pos := strings.IndexRune(s, '?'); pos > -1 { // escape query data diff --git a/vendor/github.com/gookit/goutil/strutil/similar_find.go b/vendor/github.com/gookit/goutil/strutil/ext.go similarity index 51% rename from vendor/github.com/gookit/goutil/strutil/similar_find.go rename to vendor/github.com/gookit/goutil/strutil/ext.go index 94c4fe188..31134830d 100644 --- a/vendor/github.com/gookit/goutil/strutil/similar_find.go +++ b/vendor/github.com/gookit/goutil/strutil/ext.go @@ -1,6 +1,12 @@ package strutil +import ( + "fmt" + "strings" +) + // SimilarComparator definition +// // links: // // https://github.com/mkideal/cli/blob/master/fuzzy.go @@ -13,7 +19,17 @@ func NewComparator(src, dst string) *SimilarComparator { return &SimilarComparator{src, dst} } +// Similarity calc for two string. +// +// Usage: +// +// rate, ok := Similarity("hello", "he") +func Similarity(s, t string, rate float32) (float32, bool) { + return NewComparator(s, t).Similar(rate) +} + // Similar by minDifferRate +// // Usage: // // c := NewComparator("hello", "he") @@ -56,7 +72,6 @@ func min(x, y float32) float32 { if x < y { return x } - return y } @@ -64,14 +79,67 @@ func max(x, y int) int { if x > y { return x } - return y } -// Similarity calc for two string. -// Usage: -// -// rate, ok := Similarity("hello", "he") -func Similarity(s, t string, rate float32) (float32, bool) { - return NewComparator(s, t).Similar(rate) +// Builder struct +type Builder struct { + strings.Builder +} + +// Write bytes and no error report +func (b *Builder) Write(p []byte) { + _, _ = b.Builder.Write(p) +} + +// WriteRune and no error report +func (b *Builder) WriteRune(r rune) { + _, _ = b.Builder.WriteRune(r) +} + +// WriteByteNE write byte and no error report +func (b *Builder) WriteByteNE(c byte) { + _ = b.WriteByte(c) +} + +// WriteString to builder +func (b *Builder) WriteString(s string) { + _, _ = b.Builder.WriteString(s) +} + +// Writef write string by fmt.Sprintf formatted +func (b *Builder) Writef(tpl string, vs ...any) { + _, _ = b.Builder.WriteString(fmt.Sprintf(tpl, vs...)) +} + +// Writeln write string with newline. +func (b *Builder) Writeln(s string) { + _, _ = b.Builder.WriteString(s) + _ = b.WriteByte('\n') +} + +// WriteAny write any type value. +func (b *Builder) WriteAny(v any) { + _, _ = b.Builder.WriteString(QuietString(v)) +} + +// WriteAnys write any type values. +func (b *Builder) WriteAnys(vs ...any) { + for _, v := range vs { + _, _ = b.Builder.WriteString(QuietString(v)) + } +} + +// WriteMulti write multi byte at once. +func (b *Builder) WriteMulti(bs ...byte) { + for _, b2 := range bs { + _ = b.WriteByte(b2) + } +} + +// WriteStrings write multi string at once. +func (b *Builder) WriteStrings(ss ...string) { + for _, s := range ss { + _, _ = b.Builder.WriteString(s) + } } diff --git a/vendor/github.com/gookit/goutil/strutil/id.go b/vendor/github.com/gookit/goutil/strutil/id.go index 053ba10d7..028264916 100644 --- a/vendor/github.com/gookit/goutil/strutil/id.go +++ b/vendor/github.com/gookit/goutil/strutil/id.go @@ -1,6 +1,9 @@ package strutil import ( + "hash/crc32" + "math/rand" + "os" "strconv" "time" @@ -33,3 +36,33 @@ func MicroTimeHexID() string { return strconv.FormatInt(ms, 16) + strconv.FormatInt(int64(ri), 16) } + +// DatetimeNo generate. can use for order-no. +// +// No prefix, return like: 2023041410484904074285478388(len: 28) +func DatetimeNo(prefix string) string { + nt := time.Now() + pl := len(prefix) + bs := make([]byte, 0, 28+pl) + if pl > 0 { + bs = append(bs, prefix...) + } + + // micro datatime + bs = nt.AppendFormat(bs, "20060102150405.000000") + bs[14+pl] = '0' + + // host + name, err := os.Hostname() + if err != nil { + name = "default" + } + c32 := crc32.ChecksumIEEE([]byte(name)) // eg: 4006367001 + bs = strconv.AppendUint(bs, uint64(c32%99), 10) + + // rand 10000 - 99999 + rand.Seed(nt.UnixNano()) + bs = strconv.AppendInt(bs, 10000+rand.Int63n(89999), 10) + + return string(bs) +} diff --git a/vendor/github.com/gookit/goutil/strutil/padding.go b/vendor/github.com/gookit/goutil/strutil/padding.go index 97a06b4d4..30cb042cc 100644 --- a/vendor/github.com/gookit/goutil/strutil/padding.go +++ b/vendor/github.com/gookit/goutil/strutil/padding.go @@ -160,6 +160,10 @@ func RepeatBytes(char byte, times int) []byte { // RepeatChars repeat a byte char. func RepeatChars[T byte | rune](char T, times int) []T { + if times <= 0 { + return make([]T, 0) + } + chars := make([]T, 0, times) for i := 0; i < times; i++ { chars = append(chars, char) diff --git a/vendor/github.com/gookit/goutil/strutil/parse.go b/vendor/github.com/gookit/goutil/strutil/parse.go new file mode 100644 index 000000000..82fd51920 --- /dev/null +++ b/vendor/github.com/gookit/goutil/strutil/parse.go @@ -0,0 +1,229 @@ +package strutil + +import ( + "errors" + "strconv" + "strings" + "time" + "unicode" + + "github.com/gookit/goutil/byteutil" +) + +// MustToTime convert date string to time.Time +func MustToTime(s string, layouts ...string) time.Time { + t, err := ToTime(s, layouts...) + if err != nil { + panic(err) + } + 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}, + 23: {"2006-01-02 15:04:05.000", "2006-01-02 15:04:05.999"}, + 24: {time.ANSIC}, + 25: {time.RFC3339, time.StampNano}, + // time.Layout}, // must go >= 1.19 + 26: {"2006-01-02 15:04:05.000000"}, + 28: {time.UnixDate}, + 29: {time.RFC1123, "2006-01-02 15:04:05.000000000"}, + 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) { + // custom layout + if len(layouts) > 0 { + if len(layouts[0]) > 0 { + return time.Parse(layouts[0], s) + } + + 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) + } + + // 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.ParseInLocation(layout, s, time.Local) + return +} + +// ParseSizeOpt parse size expression options +type ParseSizeOpt struct { + // OneAsMax if only one size value, use it as max size. default is false + OneAsMax bool + // SepChar is the separator char for time range string. default is '~' + SepChar byte + // KeywordFn is the function for parse keyword time string. + KeywordFn func(string) (min, max uint64, err error) +} + +func ensureOpt(opt *ParseSizeOpt) *ParseSizeOpt { + if opt == nil { + opt = &ParseSizeOpt{SepChar: '~'} + } else { + if opt.SepChar == 0 { + opt.SepChar = '~' + } + } + return opt +} + +// ErrInvalidSizeExpr invalid size expression error +var ErrInvalidSizeExpr = errors.New("invalid size expr") + +// ParseSizeRange parse range size expression to min and max size. +// +// Expression format: +// +// "1KB~2MB" => 1KB to 2MB +// "-1KB" => <1KB +// "~1MB" => <1MB +// "< 1KB" => <1KB +// "1KB" => >1KB +// "1KB~" => >1KB +// ">1KB" => >1KB +// "+1KB" => >1KB +func ParseSizeRange(expr string, opt *ParseSizeOpt) (min, max uint64, err error) { + opt = ensureOpt(opt) + expr = strings.TrimSpace(expr) + if expr == "" { + err = ErrInvalidSizeExpr + return + } + + // parse size range. eg: "1KB~2MB" + if strings.IndexByte(expr, '~') > -1 { + s1, s2 := TrimCut(expr, "~") + if s1 != "" { + min, err = ToByteSize(s1) + if err != nil { + return + } + } + + if s2 != "" { + max, err = ToByteSize(s2) + } + return + } + + // parse single size. eg: "1KB" + if byteutil.IsNumChar(expr[0]) { + min, err = ToByteSize(expr) + if err != nil { + return + } + if opt.OneAsMax { + max = min + } + return + } + + // parse with prefix. eg: "<1KB", ">= 1KB", "-1KB", "+1KB" + switch expr[0] { + case '<', '-': + max, err = ToByteSize(strings.Trim(expr[1:], " =")) + case '>', '+': + min, err = ToByteSize(strings.Trim(expr[1:], " =")) + default: + // parse keyword. eg: "small", "large" + if opt.KeywordFn != nil { + min, max, err = opt.KeywordFn(expr) + } else { + err = ErrInvalidSizeExpr + } + } + return +} + +// SafeByteSize converts size string like 1GB/1g or 12mb/12M into an unsigned integer number of bytes +func SafeByteSize(sizeStr string) uint64 { + size, _ := ToByteSize(sizeStr) + return size +} + +// ToByteSize converts size string like 1GB/1g or 12mb/12M into an unsigned integer number of bytes +func ToByteSize(sizeStr string) (uint64, error) { + sizeStr = strings.TrimSpace(sizeStr) + lastPos := len(sizeStr) - 1 + if lastPos < 0 { + return 0, nil + } + + if sizeStr[lastPos] == 'b' || sizeStr[lastPos] == 'B' { + // last second char is k,m,g,t + lastSec := sizeStr[lastPos-1] + if lastSec > 'A' { + lastPos-- + } + } else if IsNumChar(sizeStr[lastPos]) { // not unit suffix. eg: 346 + return strconv.ParseUint(sizeStr, 10, 32) + } + + multiplier := float64(1) + switch unicode.ToLower(rune(sizeStr[lastPos])) { + case 'k': + multiplier = 1 << 10 + case 'm': + multiplier = 1 << 20 + case 'g': + multiplier = 1 << 30 + case 't': + multiplier = 1 << 40 + case 'p': + multiplier = 1 << 50 + default: // b + multiplier = 1 + } + + sizeNum := strings.TrimSpace(sizeStr[:lastPos]) + size, err := strconv.ParseFloat(sizeNum, 64) + if err != nil { + return 0, err + } + return uint64(size * multiplier), nil +} diff --git a/vendor/github.com/gookit/goutil/strutil/random.go b/vendor/github.com/gookit/goutil/strutil/random.go index 562039b27..fe404a017 100644 --- a/vendor/github.com/gookit/goutil/strutil/random.go +++ b/vendor/github.com/gookit/goutil/strutil/random.go @@ -1,20 +1,27 @@ package strutil import ( - "crypto/rand" "encoding/base64" mathRand "math/rand" "time" + + "github.com/gookit/goutil/byteutil" ) // some consts string chars const ( - Numbers = "0123456789" + Numbers = "0123456789" + HexChars = "0123456789abcdef" + AlphaBet = "abcdefghijklmnopqrstuvwxyz" AlphaBet1 = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz" + AlphaNum = "abcdefghijklmnopqrstuvwxyz0123456789" AlphaNum2 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" AlphaNum3 = "0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz" + + Base62Chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + Base64Chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/" ) // RandomChars generate give length random chars at `a-z` @@ -58,17 +65,11 @@ func RandomCharsV3(ln int) string { // RandomBytes generate func RandomBytes(length int) ([]byte, error) { - b := make([]byte, length) - _, err := rand.Read(b) - // Note that err == nil only if we read len(b) bytes. - if err != nil { - return nil, err - } - - return b, nil + return byteutil.Random(length) } // RandomString generate. +// // Example: // // // this will give us a 44 byte, base64 encoded output @@ -81,3 +82,20 @@ func RandomString(length int) (string, error) { b, err := RandomBytes(length) return base64.URLEncoding.EncodeToString(b), err } + +// RandWithTpl generate random string with give template +func RandWithTpl(n int, letters string) string { + if len(letters) == 0 { + letters = AlphaNum2 + } + + ln := len(letters) + cs := make([]byte, n) + for i := 0; i < n; i++ { + mathRand.Seed(int64(time.Now().Nanosecond())) + idx := mathRand.Intn(ln) + cs[i] = letters[idx] + } + + return byteutil.String(cs) +} diff --git a/vendor/github.com/gookit/goutil/strutil/runes.go b/vendor/github.com/gookit/goutil/strutil/runes.go index 41754f568..7acb46d80 100644 --- a/vendor/github.com/gookit/goutil/strutil/runes.go +++ b/vendor/github.com/gookit/goutil/strutil/runes.go @@ -88,6 +88,9 @@ func RunesWidth(rs []rune) (w int) { return w } +// Truncate alias of the Utf8Truncate() +func Truncate(s string, w int, tail string) string { return Utf8Truncate(s, w, tail) } + // TextTruncate alias of the Utf8Truncate() func TextTruncate(s string, w int, tail string) string { return Utf8Truncate(s, w, tail) } diff --git a/vendor/github.com/gookit/goutil/strutil/split.go b/vendor/github.com/gookit/goutil/strutil/split.go index bc423528b..807139e67 100644 --- a/vendor/github.com/gookit/goutil/strutil/split.go +++ b/vendor/github.com/gookit/goutil/strutil/split.go @@ -14,9 +14,19 @@ func Cut(s, sep string) (before string, after string, found bool) { return s, "", false } +// QuietCut always returns two substring. +func QuietCut(s, sep string) (before string, after string) { + before, after, _ = Cut(s, sep) + return +} + // MustCut always returns two substring. func MustCut(s, sep string) (before string, after string) { - before, after, _ = Cut(s, sep) + var ok bool + before, after, ok = Cut(s, sep) + if !ok { + panic("cannot split input string to two nodes") + } return } @@ -26,6 +36,9 @@ func TrimCut(s, sep string) (string, string) { return strings.TrimSpace(before), strings.TrimSpace(after) } +// SplitKV split string to key and value. +func SplitKV(s, sep string) (string, string) { return TrimCut(s, sep) } + // SplitValid string to slice. will trim each item and filter empty string node. func SplitValid(s, sep string) (ss []string) { return Split(s, sep) } @@ -113,14 +126,34 @@ 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:] +// SplitInlineComment for an inline text string. +func SplitInlineComment(val string, strict ...bool) (string, string) { + // strict check: must with space + if len(strict) > 0 && strict[0] { + if pos := strings.Index(val, " #"); pos > -1 { + return strings.TrimRight(val[0:pos], " "), val[pos+1:] + } + + if pos := strings.Index(val, " //"); pos > -1 { + return strings.TrimRight(val[0:pos], " "), val[pos+1:] + } + } else { + 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:] + } } - if pos := strings.Index(val, "//"); pos > -1 { - return strings.TrimRight(val[0:pos], " "), val[pos:] - } return val, "" } + +// FirstLine from command output +func FirstLine(output string) string { + if i := strings.IndexByte(output, '\n'); i >= 0 { + return output[0:i] + } + return output +} diff --git a/vendor/github.com/gookit/goutil/strutil/strutil.go b/vendor/github.com/gookit/goutil/strutil/strutil.go index ea1f69d5d..f90229343 100644 --- a/vendor/github.com/gookit/goutil/strutil/strutil.go +++ b/vendor/github.com/gookit/goutil/strutil/strutil.go @@ -4,11 +4,47 @@ package strutil import ( "bytes" "encoding/json" + "errors" "fmt" "strings" "text/template" ) +// OrCond return s1 on cond is True, OR return s2. +// Like: cond ? s1 : s2 +func OrCond(cond bool, s1, s2 string) string { + if cond { + return s1 + } + return s2 +} + +// OrElse return s OR orVal(new-value) on s is empty +func OrElse(s, orVal string) string { + if s != "" { + return s + } + return orVal +} + +// OrHandle return fn(s) on s is not empty. +func OrHandle(s string, fn func(s string) string) string { + if s != "" { + return fn(s) + } + return s +} + +// Valid return first not empty element. +func Valid(ss ...string) string { + for _, s := range ss { + if s != "" { + return s + } + } + return "" +} + // Replaces replace multi strings // // pairs: {old1: new1, old2: new2, ...} @@ -17,12 +53,16 @@ import ( // // strings.NewReplacer("old1", "new1", "old2", "new2").Replace(str) func Replaces(str string, pairs map[string]string) string { + return NewReplacer(pairs).Replace(str) +} + +// NewReplacer instance +func NewReplacer(pairs map[string]string) *strings.Replacer { ss := make([]string, len(pairs)*2) for old, newVal := range pairs { ss = append(ss, old, newVal) } - - return strings.NewReplacer(ss...).Replace(str) + return strings.NewReplacer(ss...) } // PrettyJSON get pretty Json string @@ -88,3 +128,35 @@ func WrapTag(s, tag string) string { } return fmt.Sprintf("<%s>%s", tag, s, tag) } + +// SubstrCount returns the number of times the substr substring occurs in the s string. +// Actually, it comes from strings.Count(). +// s The string to search in +// substr The substring to search for +// params[0] The offset where to start counting. +// params[1] The maximum length after the specified offset to search for the substring. +func SubstrCount(s string, substr string, params ...uint64) (int, error) { + larg := len(params) + hasArgs := larg != 0 + if hasArgs && larg > 2 { + return 0, errors.New("too many parameters") + } + if !hasArgs { + return strings.Count(s, substr), nil + } + strlen := len(s) + offset := 0 + end := strlen + if hasArgs { + offset = int(params[0]) + if larg == 2 { + length := int(params[1]) + end = offset + length + } + if end > strlen { + end = strlen + } + } + s = string([]rune(s)[offset:end]) + return strings.Count(s, substr), nil +} diff --git a/vendor/github.com/gookit/goutil/strutil/textutil/textutil.go b/vendor/github.com/gookit/goutil/strutil/textutil/textutil.go new file mode 100644 index 000000000..0c65d9b96 --- /dev/null +++ b/vendor/github.com/gookit/goutil/strutil/textutil/textutil.go @@ -0,0 +1,64 @@ +// Package textutil provide some extra text handle util +package textutil + +import ( + "fmt" + "strings" + + "github.com/gookit/goutil/arrutil" + "github.com/gookit/goutil/maputil" + "github.com/gookit/goutil/strutil" +) + +// ReplaceVars by regex replace given tpl vars. +// +// If format is empty, will use {const defaultVarFormat} +func ReplaceVars(text string, vars map[string]any, format string) string { + return NewVarReplacer(format).Replace(text, vars) +} + +// RenderSMap by regex replace given tpl vars. +// +// If format is empty, will use {const defaultVarFormat} +func RenderSMap(text string, vars map[string]string, format string) string { + return NewVarReplacer(format).RenderSimple(text, vars) +} + +// IsMatchAll keywords in the give text string. +// +// TIP: can use ^ for exclude match. +func IsMatchAll(s string, keywords []string) bool { + return strutil.SimpleMatch(s, keywords) +} + +// ParseInlineINI parse config string to string-map. it's like INI format contents. +// +// Examples: +// +// eg: "name=val0;shorts=i;required=true;desc=a message" +// => +// {name: val0, shorts: i, required: true, desc: a message} +func ParseInlineINI(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 inline config error: must match `KEY=VAL`") + return + } + + key, val := strutil.TrimCut(s, "=") + if len(keys) > 0 && !arrutil.StringsHas(keys, key) { + err = fmt.Errorf("parse inline config error: invalid key name %q", key) + return + } + + mp[key] = val + } + return +} diff --git a/vendor/github.com/gookit/goutil/strutil/textutil/var_replacer.go b/vendor/github.com/gookit/goutil/strutil/textutil/var_replacer.go new file mode 100644 index 000000000..29ef2c3d4 --- /dev/null +++ b/vendor/github.com/gookit/goutil/strutil/textutil/var_replacer.go @@ -0,0 +1,213 @@ +package textutil + +import ( + "reflect" + "regexp" + "strings" + + "github.com/gookit/goutil/arrutil" + "github.com/gookit/goutil/internal/comfunc" + "github.com/gookit/goutil/maputil" + "github.com/gookit/goutil/strutil" +) + +const defaultVarFormat = "{{,}}" + +// FallbackFn type +type FallbackFn = func(name string) (val string, ok bool) + +// VarReplacer struct +type VarReplacer struct { + init bool + + Left, Right string + lLen, rLen int + + varReg *regexp.Regexp + // flatten sub map in vars + flatSubs bool + parseEnv bool + // support parse default value. eg: {{ name | inhere }} + parseDef bool + // keepMissVars list. default False: will clear on each replace + keepMissVars bool + // missing vars list + missVars []string + // NotFound handler + NotFound FallbackFn +} + +// NewVarReplacer instance +func NewVarReplacer(format string, opFns ...func(vp *VarReplacer)) *VarReplacer { + vp := &VarReplacer{flatSubs: true} + for _, fn := range opFns { + fn(vp) + } + return vp.WithFormat(format) +} + +// NewFullReplacer instance +func NewFullReplacer(format string) *VarReplacer { + return NewVarReplacer(format, func(vp *VarReplacer) { + vp.WithParseEnv().WithParseDefault().KeepMissingVars() + }) +} + +// DisableFlatten on the input vars map +func (r *VarReplacer) DisableFlatten() *VarReplacer { + r.flatSubs = false + return r +} + +// KeepMissingVars on the replacement handle +func (r *VarReplacer) KeepMissingVars() *VarReplacer { + r.keepMissVars = true + return r +} + +// WithParseDefault value on the input template contents +func (r *VarReplacer) WithParseDefault() *VarReplacer { + r.parseDef = true + return r +} + +// WithParseEnv on the input vars value +func (r *VarReplacer) WithParseEnv() *VarReplacer { + r.parseEnv = true + return r +} + +// OnNotFound var handle +func (r *VarReplacer) OnNotFound(fn FallbackFn) *VarReplacer { + r.NotFound = fn + return r +} + +// WithFormat custom var template +func (r *VarReplacer) WithFormat(format string) *VarReplacer { + r.Left, r.Right = strutil.QuietCut(strutil.OrElse(format, defaultVarFormat), ",") + r.Init() + return r +} + +// Init var matcher +func (r *VarReplacer) Init() *VarReplacer { + if !r.init { + r.lLen, r.rLen = len(r.Left), len(r.Right) + if r.Right != "" { + r.varReg = regexp.MustCompile(regexp.QuoteMeta(r.Left) + `([\w\s\|.-]+)` + regexp.QuoteMeta(r.Right)) + } else { + // no right tag. eg: $name, $user.age + r.varReg = regexp.MustCompile(regexp.QuoteMeta(r.Left) + `(\w[\w-]*(?:\.[\w-]+)*)`) + } + } + + return r +} + +// ParseVars the text contents and collect vars +func (r *VarReplacer) ParseVars(s string) []string { + ss := arrutil.StringsMap(r.varReg.FindAllString(s, -1), func(val string) string { + return strings.TrimSpace(val[r.lLen : len(val)-r.rLen]) + }) + + return arrutil.Unique(ss) +} + +// Render any-map vars in the text contents +func (r *VarReplacer) Render(s string, tplVars map[string]any) string { + return r.Replace(s, tplVars) +} + +// Replace any-map vars in the text contents +func (r *VarReplacer) Replace(s string, tplVars map[string]any) string { + if !strings.Contains(s, r.Left) { + return s + } + if !r.parseDef && len(tplVars) == 0 { + return s + } + + var varMap map[string]string + + if r.flatSubs { + varMap = make(map[string]string, len(tplVars)*2) + maputil.FlatWithFunc(tplVars, func(path string, val reflect.Value) { + if val.Kind() == reflect.String { + if r.parseEnv { + varMap[path] = comfunc.ParseEnvVar(val.String(), nil) + } else { + varMap[path] = val.String() + } + } else { + varMap[path] = strutil.QuietString(val.Interface()) + } + }) + } else { + varMap = maputil.ToStringMap(tplVars) + } + + return r.Init().doReplace(s, varMap) +} + +// ReplaceSMap string-map vars in the text contents +func (r *VarReplacer) ReplaceSMap(s string, varMap map[string]string) string { + return r.RenderSimple(s, varMap) +} + +// RenderSimple string-map vars in the text contents. alias of ReplaceSMap() +func (r *VarReplacer) RenderSimple(s string, varMap map[string]string) string { + if len(varMap) == 0 || !strings.Contains(s, r.Left) { + return s + } + + if r.parseEnv { + for name, val := range varMap { + varMap[name] = comfunc.ParseEnvVar(val, nil) + } + } + + return r.Init().doReplace(s, varMap) +} + +// MissVars list +func (r *VarReplacer) MissVars() []string { + return r.missVars +} + +// ResetMissVars list +func (r *VarReplacer) ResetMissVars() { + r.missVars = make([]string, 0) +} + +// Replace string-map vars in the text contents +func (r *VarReplacer) doReplace(s string, varMap map[string]string) string { + if !r.keepMissVars { + r.missVars = make([]string, 0) // clear on each replace + } + + return r.varReg.ReplaceAllStringFunc(s, func(sub string) string { + name := strings.TrimSpace(sub[r.lLen : len(sub)-r.rLen]) + + var defVal string + if r.parseDef && strings.ContainsRune(name, '|') { + name, defVal = strutil.TrimCut(name, "|") + } + + if val, ok := varMap[name]; ok { + return val + } + + if r.NotFound != nil { + if val, ok := r.NotFound(name); ok { + return val + } + } + + if len(defVal) > 0 { + return defVal + } + r.missVars = append(r.missVars, name) + return sub + }) +} diff --git a/vendor/github.com/gookit/goutil/strutil/value.go b/vendor/github.com/gookit/goutil/strutil/value.go index 7e4bdc251..73c31aa7b 100644 --- a/vendor/github.com/gookit/goutil/strutil/value.go +++ b/vendor/github.com/gookit/goutil/strutil/value.go @@ -15,71 +15,84 @@ func (s *Value) Set(val string) error { } // IsEmpty check -func (s *Value) IsEmpty() bool { - return string(*s) == "" +func (s Value) IsEmpty() bool { + return string(s) == "" +} + +// IsBlank check +func (s Value) IsBlank() bool { + return strings.TrimSpace(string(s)) == "" } // IsStartWith prefix -func (s *Value) IsStartWith(sub string) bool { - return strings.HasPrefix(string(*s), sub) +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) +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) +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) +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) +func (s Value) Bytes() []byte { + return []byte(s) } // Val string -func (s *Value) Val() string { - return string(*s) +func (s Value) Val() string { + return string(s) } // Int convert -func (s *Value) Int() int { - return QuietInt(string(*s)) +func (s Value) Int() int { + return QuietInt(string(s)) } // Int64 convert -func (s *Value) Int64() int64 { - return QuietInt64(string(*s)) +func (s Value) Int64() int64 { + return QuietInt64(string(s)) } // Bool convert -func (s *Value) Bool() bool { - return QuietBool(string(*s)) +func (s Value) Bool() bool { + return QuietBool(string(s)) } // Value string -func (s *Value) String() string { - return string(*s) +func (s Value) String() string { + return string(s) +} + +// OrElse string +func (s Value) OrElse(or string) string { + if s != "" { + return string(s) + } + return or } // Split string -func (s *Value) Split(sep string) []string { - return strings.Split(string(*s), sep) +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) +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))) +// WithTrimSpace string and return new +func (s Value) WithTrimSpace() Value { + return Value(strings.TrimSpace(string(s))) } diff --git a/vendor/github.com/gookit/goutil/sysutil/cmdr/cmd.go b/vendor/github.com/gookit/goutil/sysutil/cmdr/cmd.go index b3f09d2b0..f91a41184 100644 --- a/vendor/github.com/gookit/goutil/sysutil/cmdr/cmd.go +++ b/vendor/github.com/gookit/goutil/sysutil/cmdr/cmd.go @@ -8,9 +8,9 @@ import ( "os/exec" "path/filepath" - "github.com/gookit/color" "github.com/gookit/goutil" "github.com/gookit/goutil/arrutil" + "github.com/gookit/goutil/cliutil/cmdline" "github.com/gookit/goutil/internal/comfunc" ) @@ -19,7 +19,10 @@ type Cmd struct { *exec.Cmd // Name of the command Name string - // inited bool + // DryRun if True, not real execute command + DryRun bool + // Vars mapping + Vars map[string]string // BeforeRun hook BeforeRun func(c *Cmd) @@ -27,37 +30,39 @@ type Cmd struct { 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) } +// NewCmdline instance +// +// see exec.Command +func NewCmdline(line string) *Cmd { + bin, args := cmdline.NewParser(line).WithParseEnv().BinAndArgs() + return NewCmd(bin, args...) +} + // NewCmd instance // // see exec.Command func NewCmd(bin string, args ...string) *Cmd { - return &Cmd{ - Cmd: exec.Command(bin, args...), - } + return WrapGoCmd(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...), - } + return WrapGoCmd(exec.CommandContext(ctx, bin, args...)) } -// PrintCmdline on before exec -func PrintCmdline(c *Cmd) { - color.Yellowln(">", c.Cmdline()) +// WrapGoCmd instance +func WrapGoCmd(cmd *exec.Cmd) *Cmd { + return &Cmd{ + Cmd: cmd, + Vars: make(map[string]string), + } } // ------------------------------------------------- @@ -70,6 +75,18 @@ func (c *Cmd) Config(fn func(c *Cmd)) *Cmd { return c } +// WithDryRun on exec command +func (c *Cmd) WithDryRun(dryRun bool) *Cmd { + c.DryRun = dryRun + return c +} + +// PrintCmdline on exec command +func (c *Cmd) PrintCmdline() *Cmd { + c.BeforeRun = PrintCmdline + return c +} + // OnBefore exec add hook func (c *Cmd) OnBefore(fn func(c *Cmd)) *Cmd { c.BeforeRun = fn @@ -99,7 +116,7 @@ func (c *Cmd) lookPath(name string) { c.Path = lp } if err != nil { - goutil.Panicf("look %q path error: %s", name, err.Error()) + goutil.Panicf("cmdr: look %q path error: %v", name, err) } } } @@ -116,21 +133,60 @@ func (c *Cmd) WithWorkDir(dir string) *Cmd { return c } -// WorkDirOnNot set, returns the current object -func (c *Cmd) WorkDirOnNot(dir string) *Cmd { - if c.Dir == "" { +// WorkDirOnNE set workdir on input is not empty +func (c *Cmd) WorkDirOnNE(dir string) *Cmd { + if dir == "" { c.Dir = dir } return c } -// OutputToStd output to OS stdout and error -func (c *Cmd) OutputToStd() *Cmd { +// WithEnvMap override set new ENV for run +func (c *Cmd) WithEnvMap(mp map[string]string) *Cmd { + if ln := len(mp); ln > 0 { + c.Env = make([]string, 0, ln) + for key, val := range mp { + c.Env = append(c.Env, key+"="+val) + } + } + return c +} + +// AppendEnv to the os ENV for run command +func (c *Cmd) AppendEnv(mp map[string]string) *Cmd { + if len(mp) > 0 { + // init env data + if c.Env == nil { + c.Env = os.Environ() + } + + for name, val := range mp { + c.Env = append(c.Env, name+"="+val) + } + } + + return c +} + +// OutputToOS output to OS stdout and error +func (c *Cmd) OutputToOS() *Cmd { + return c.ToOSStdoutStderr() +} + +// ToOSStdoutStderr output to OS stdout and error +func (c *Cmd) ToOSStdoutStderr() *Cmd { c.Stdout = os.Stdout c.Stderr = os.Stderr return c } +// ToOSStdout output to OS stdout +func (c *Cmd) ToOSStdout() *Cmd { + c.Stdout = os.Stdout + c.Stderr = os.Stdout + return c +} + // WithStdin returns the current argument func (c *Cmd) WithStdin(in io.Reader) *Cmd { c.Stdin = in @@ -204,6 +260,20 @@ func (c *Cmd) WithArgsIf(args []string, exprOk bool) *Cmd { return c } +// WithVars add vars and returns the current object +func (c *Cmd) WithVars(vs map[string]string) *Cmd { + if len(vs) > 0 { + c.Vars = vs + } + return c +} + +// SetVar add var and returns the current object +func (c *Cmd) SetVar(name, val string) *Cmd { + c.Vars[name] = val + return c +} + // ------------------------------------------------- // helper command // ------------------------------------------------- @@ -249,6 +319,11 @@ func (c *Cmd) ResetArgs() { } } +// Workdir of the command +func (c *Cmd) Workdir() string { + return c.Dir +} + // Cmdline to command line func (c *Cmd) Cmdline() string { return comfunc.Cmdline(c.Args) @@ -280,6 +355,11 @@ func (c *Cmd) Success() bool { return c.Run() == nil } +// HasStdout output setting. +func (c *Cmd) HasStdout() bool { + return c.Stdout != nil +} + // SafeLines run and return output as lines func (c *Cmd) SafeLines() []string { ss, _ := c.OutputLines() @@ -310,6 +390,10 @@ func (c *Cmd) Output() (string, error) { c.BeforeRun(c) } + if c.DryRun { + return "DRY-RUN: ok", nil + } + output, err := c.Cmd.Output() if c.AfterRun != nil { @@ -324,6 +408,10 @@ func (c *Cmd) CombinedOutput() (string, error) { c.BeforeRun(c) } + if c.DryRun { + return "DRY-RUN: ok", nil + } + output, err := c.Cmd.CombinedOutput() if c.AfterRun != nil { @@ -341,8 +429,7 @@ func (c *Cmd) MustRun() { // FlushRun runs command and flush output to stdout func (c *Cmd) FlushRun() error { - c.OutputToStd() - return c.Run() + return c.ToOSStdoutStderr().Run() } // Run runs command @@ -351,6 +438,10 @@ func (c *Cmd) Run() error { c.BeforeRun(c) } + if c.DryRun { + return nil + } + // do running err := c.Cmd.Run() @@ -358,13 +449,13 @@ func (c *Cmd) Run() error { c.AfterRun(c, err) } return err - - // if IsWindows() { - // return c.Spawn() - // } - // return c.Exec() } +// if IsWindows() { +// return c.Spawn() +// } +// return c.Exec() + // Spawn runs command with spawn(3) // func (c *Cmd) Spawn() error { // return c.Cmd.Run() diff --git a/vendor/github.com/gookit/goutil/sysutil/cmdr/cmdr.go b/vendor/github.com/gookit/goutil/sysutil/cmdr/cmdr.go index 695d98678..09161c474 100644 --- a/vendor/github.com/gookit/goutil/sysutil/cmdr/cmdr.go +++ b/vendor/github.com/gookit/goutil/sysutil/cmdr/cmdr.go @@ -1,7 +1,20 @@ // Package cmdr Provide for quick build and run a cmd, batch run multi cmd tasks package cmdr -import "strings" +import ( + "strings" + + "github.com/gookit/color" +) + +// PrintCmdline on before exec +func PrintCmdline(c *Cmd) { + if c.DryRun { + color.Yellowln("DRY-RUN>", c.Cmdline()) + } else { + color.Yellowln(">", c.Cmdline()) + } +} // OutputLines split output to lines func OutputLines(output string) []string { diff --git a/vendor/github.com/gookit/goutil/sysutil/cmdr/runner.go b/vendor/github.com/gookit/goutil/sysutil/cmdr/runner.go index c01ffd977..35f81dd79 100644 --- a/vendor/github.com/gookit/goutil/sysutil/cmdr/runner.go +++ b/vendor/github.com/gookit/goutil/sysutil/cmdr/runner.go @@ -2,12 +2,15 @@ package cmdr import ( "fmt" + "strings" "github.com/gookit/color" "github.com/gookit/goutil/arrutil" + "github.com/gookit/goutil/cliutil/cmdline" "github.com/gookit/goutil/errorx" "github.com/gookit/goutil/maputil" "github.com/gookit/goutil/mathutil" + "github.com/gookit/goutil/strutil/textutil" ) // Task struct @@ -24,6 +27,7 @@ type Task struct { PrevCond func(prev *Task) bool } +// NewTask instance func NewTask(cmd *Cmd) *Task { return &Task{ Cmd: cmd, @@ -44,6 +48,24 @@ func (t *Task) ensureID(idx int) { t.ID = id } +var rpl = textutil.NewVarReplacer("$").DisableFlatten() + +// RunWith command +func (t *Task) RunWith(ctx maputil.Data) error { + cmdVars := ctx.StringMap("cmdVars") + + if len(cmdVars) > 0 { + // rpl := strutil.NewReplacer(cmdVars) + for i, val := range t.Cmd.Args { + if strings.ContainsRune(val, '$') { + t.Cmd.Args[i] = rpl.RenderSimple(val, cmdVars) + } + } + } + + return t.Run() +} + // Run command func (t *Task) Run() error { if t.BeforeRun != nil { @@ -74,6 +96,9 @@ func (t *Task) IsSuccess() bool { return t.err == nil } +// RunnerHookFn func +type RunnerHookFn func(r *Runner, t *Task) bool + // Runner use for batch run multi task commands type Runner struct { prev *Task @@ -84,14 +109,19 @@ type Runner struct { Errs errorx.ErrMap // TODO Concurrent run - // common workdir - // wordDir string + + // Workdir common workdir + Workdir string + // EnvMap will append to task.Cmd on run + EnvMap map[string]string // Params for add custom params Params maputil.Map // DryRun dry run all commands DryRun bool + // OutToStd stdout and stderr + OutToStd bool // IgnoreErr continue on error IgnoreErr bool // BeforeRun hooks on each task. return false to skip current task. @@ -109,12 +139,19 @@ func NewRunner(fns ...func(rr *Runner)) *Runner { Params: make(maputil.Map), } + rr.OutToStd = true for _, fn := range fns { fn(rr) } return rr } +// WithOutToStd set +func (r *Runner) WithOutToStd() *Runner { + r.OutToStd = true + return r +} + // Add multitask at once func (r *Runner) Add(tasks ...*Task) *Runner { for _, task := range tasks { @@ -148,26 +185,32 @@ func (r *Runner) AddCmd(cmds ...*Cmd) *Runner { // GitCmd quick a git command task func (r *Runner) GitCmd(subCmd string, args ...string) *Runner { - r.AddTask(&Task{ + return 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{ + return 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{ + return r.AddTask(&Task{ Cmd: NewCmd(cmdName, arrutil.SliceToStrings(args)...), }) - return r +} + +// AddCmdline as a command task +func (r *Runner) AddCmdline(line string) *Runner { + bin, args := cmdline.NewParser(line).BinAndArgs() + + return r.AddTask(&Task{ + Cmd: NewCmd(bin, args...), + }) } // Run all tasks @@ -183,13 +226,14 @@ func (r *Runner) Run() error { } if r.DryRun { - color.Infof("DRY-RUN: task #%d execute completed\n", i+1) + color.Infof("DRY-RUN: task#%d execute completed\n\n", i+1) continue } if !r.RunTask(task) { break } + fmt.Println() // with newline. } if len(r.Errs) == 0 { @@ -198,12 +242,30 @@ func (r *Runner) Run() error { return r.Errs } +// StepRun one command +func (r *Runner) StepRun() error { + return nil // TODO +} + // RunTask command func (r *Runner) RunTask(task *Task) (goon bool) { + if len(r.EnvMap) > 0 { + task.Cmd.AppendEnv(r.EnvMap) + } + + if r.OutToStd && !task.Cmd.HasStdout() { + task.Cmd.ToOSStdoutStderr() + } + + // common workdir + if r.Workdir != "" && task.Cmd.Dir == "" { + task.Cmd.WithWorkDir(r.Workdir) + } + // do running - if err := task.Run(); err != nil { + if err := task.RunWith(r.Params); err != nil { r.Errs[task.ID] = err - color.Errorf("Task #%d run error: %s\n", task.Index()+1, err) + color.Errorf("Task#%d run error: %s\n", task.Index()+1, err) // not ignore error, stop. if !r.IgnoreErr { @@ -225,6 +287,14 @@ func (r *Runner) Len() int { return len(r.tasks) } +// Reset instance +func (r *Runner) Reset() *Runner { + r.prev = nil + r.tasks = make([]*Task, 0) + r.idMap = make(map[string]int, 0) + return r +} + // TaskIDs get func (r *Runner) TaskIDs() []string { ss := make([]string, 0, len(r.idMap)) diff --git a/vendor/github.com/gookit/goutil/sysutil/process/pidfile.go b/vendor/github.com/gookit/goutil/sysutil/process/pidfile.go deleted file mode 100644 index e225c8b40..000000000 --- a/vendor/github.com/gookit/goutil/sysutil/process/pidfile.go +++ /dev/null @@ -1,70 +0,0 @@ -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 deleted file mode 100644 index 6e839f1d5..000000000 --- a/vendor/github.com/gookit/goutil/sysutil/process/process.go +++ /dev/null @@ -1,9 +0,0 @@ -// Package process Provide some process handle util functions -package process - -import "os" - -// PID get process ID -func PID() int { - return os.Getpid() -} diff --git a/vendor/github.com/gookit/goutil/sysutil/process/process_darwin.go b/vendor/github.com/gookit/goutil/sysutil/process/process_darwin.go deleted file mode 100644 index 187ce94cb..000000000 --- a/vendor/github.com/gookit/goutil/sysutil/process/process_darwin.go +++ /dev/null @@ -1,16 +0,0 @@ -//go:build darwin -// +build darwin - -package process - -import ( - "golang.org/x/sys/unix" -) - -// Exists check process running by given pid -func Exists(pid int) bool { - // OS X does not have a proc filesystem. - // Use kill -0 pid to judge if the process exists. - err := unix.Kill(pid, 0) - return err == nil -} diff --git a/vendor/github.com/gookit/goutil/sysutil/process/process_unix.go b/vendor/github.com/gookit/goutil/sysutil/process/process_unix.go deleted file mode 100644 index 407a013b3..000000000 --- a/vendor/github.com/gookit/goutil/sysutil/process/process_unix.go +++ /dev/null @@ -1,18 +0,0 @@ -//go:build !windows && !darwin -// +build !windows,!darwin - -package process - -import ( - "os" - "path/filepath" - "strconv" -) - -// Exists check process running by given pid -func Exists(pid int) bool { - if _, err := os.Stat(filepath.Join("/proc", strconv.Itoa(pid))); err == nil { - return true - } - return false -} diff --git a/vendor/github.com/gookit/goutil/sysutil/process/process_windows.go b/vendor/github.com/gookit/goutil/sysutil/process/process_windows.go deleted file mode 100644 index 77238c08d..000000000 --- a/vendor/github.com/gookit/goutil/sysutil/process/process_windows.go +++ /dev/null @@ -1,28 +0,0 @@ -package process - -import ( - "golang.org/x/sys/windows" -) - -const ( - processQueryLimitedInformation = 0x1000 - - stillActive = 259 -) - -// Exists check process running by given pid -func Exists(pid int) bool { - h, err := windows.OpenProcess(processQueryLimitedInformation, false, uint32(pid)) - if err != nil { - return false - } - - var c uint32 - err = windows.GetExitCodeProcess(h, &c) - _ = windows.Close(h) - - if err != nil { - return c == stillActive - } - return true -} diff --git a/vendor/github.com/gookit/goutil/sysutil/sysenv.go b/vendor/github.com/gookit/goutil/sysutil/sysenv.go index 8e5303f0a..6573208bc 100644 --- a/vendor/github.com/gookit/goutil/sysutil/sysenv.go +++ b/vendor/github.com/gookit/goutil/sysutil/sysenv.go @@ -90,11 +90,6 @@ 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: @@ -104,7 +99,7 @@ func FindExecutable(binName string) (string, error) { return exec.LookPath(binName) } -// Executable find in the system +// Executable find in the system, alias of FindExecutable() // // Usage: // @@ -123,25 +118,65 @@ func HasExecutable(binName string) bool { return err == nil } +// Getenv get ENV value by key name, can with default value +func Getenv(name string, def ...string) string { + val := os.Getenv(name) + if val == "" && len(def) > 0 { + val = def[0] + } + return val +} + +// Environ like os.Environ, but will returns key-value map[string]string data. +func Environ() map[string]string { return comfunc.Environ() } + +// EnvMapWith like os.Environ, but will return key-value map[string]string data. +func EnvMapWith(newEnv map[string]string) map[string]string { + envMp := comfunc.Environ() + for name, value := range newEnv { + envMp[name] = value + } + return envMp +} + +// EnvPaths get and split $PATH to []string +func EnvPaths() []string { + return filepath.SplitList(os.Getenv("PATH")) +} + // SearchPath search executable files in the system $PATH // // Usage: // // sysutil.SearchPath("go") -func SearchPath(keywords string) []string { +func SearchPath(keywords string, limit int) []string { path := os.Getenv("PATH") ptn := "*" + keywords + "*" - list := make([]string, 0) + + checked := make(map[string]bool) for _, dir := range filepath.SplitList(path) { + // Unix shell semantics: path element "" means "." if dir == "" { - // Unix shell semantics: path element "" means "." dir = "." } + // mark dir is checked + if _, ok := checked[dir]; ok { + continue + } + + checked[dir] = true matches, err := filepath.Glob(filepath.Join(dir, ptn)) if err == nil && len(matches) > 0 { list = append(list, matches...) + size := len(list) + + // limit result size + if limit > 0 && size >= limit { + list = list[:limit] + break + } } } diff --git a/vendor/github.com/gookit/goutil/sysutil/sysutil.go b/vendor/github.com/gookit/goutil/sysutil/sysutil.go index 5be3456ed..60fe67b60 100644 --- a/vendor/github.com/gookit/goutil/sysutil/sysutil.go +++ b/vendor/github.com/gookit/goutil/sysutil/sysutil.go @@ -3,7 +3,7 @@ package sysutil import ( "os" - "path" + "path/filepath" ) // Workdir get @@ -14,11 +14,34 @@ func Workdir() string { // BinDir get func BinDir() string { - binFile := os.Args[0] - return path.Dir(binFile) + return filepath.Dir(os.Args[0]) +} + +// BinName get +func BinName() string { + return filepath.Base(os.Args[0]) } // BinFile get func BinFile() string { return os.Args[0] } + +// Open file or url address +func Open(fileOrURL string) error { + return OpenURL(fileOrURL) +} + +// OpenBrowser file or url address +func OpenBrowser(fileOrURL string) error { + return OpenURL(fileOrURL) +} + +// OpenFile opens new browser window for the file path. +func OpenFile(path string) error { + fpath, err := filepath.Abs(path) + if err != nil { + return err + } + return OpenURL("file://" + fpath) +} diff --git a/vendor/github.com/gookit/goutil/sysutil/sysutil_darwin.go b/vendor/github.com/gookit/goutil/sysutil/sysutil_darwin.go new file mode 100644 index 000000000..3fe2da509 --- /dev/null +++ b/vendor/github.com/gookit/goutil/sysutil/sysutil_darwin.go @@ -0,0 +1,36 @@ +package sysutil + +import "os/exec" + +// 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 true } + +// IsDarwin system +func IsDarwin() bool { return true } + +// IsLinux system +func IsLinux() bool { return false } + +// OpenURL 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 OpenURL(URL string) error { + return exec.Command("open", URL).Run() +} diff --git a/vendor/github.com/gookit/goutil/sysutil/sysutil_nonwin.go b/vendor/github.com/gookit/goutil/sysutil/sysutil_nonwin.go index d7446ede4..bfafdbdee 100644 --- a/vendor/github.com/gookit/goutil/sysutil/sysutil_nonwin.go +++ b/vendor/github.com/gookit/goutil/sysutil/sysutil_nonwin.go @@ -1,39 +1,11 @@ //go:build !windows -// +build !windows package sysutil 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 { return syscall.Kill(pid, signal) @@ -43,26 +15,3 @@ 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_unix.go b/vendor/github.com/gookit/goutil/sysutil/sysutil_unix.go new file mode 100644 index 000000000..8059b09ab --- /dev/null +++ b/vendor/github.com/gookit/goutil/sysutil/sysutil_unix.go @@ -0,0 +1,54 @@ +//go:build !windows && !darwin + +package sysutil + +import ( + "os/exec" + "strings" +) + +// 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 false } + +// IsDarwin system +func IsDarwin() bool { return false } + +// IsLinux system +func IsLinux() bool { + return true +} + +// There are multiple possible providers to open a browser on linux +// One of them is xdg-open, another is x-www-browser, then there's www-browser, etc. +// Look for one that exists and run it +var openBins = []string{"xdg-open", "x-www-browser", "www-browser"} + +// OpenURL Open file or 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 OpenURL(URL string) error { + for _, bin := range openBins { + if _, err := exec.LookPath(bin); err == nil { + return exec.Command(bin, URL).Run() + } + } + + return &exec.Error{Name: strings.Join(openBins, ","), Err: exec.ErrNotFound} +} diff --git a/vendor/github.com/gookit/goutil/sysutil/sysutil_windows.go b/vendor/github.com/gookit/goutil/sysutil/sysutil_windows.go index b53cdc3aa..ba458b174 100644 --- a/vendor/github.com/gookit/goutil/sysutil/sysutil_windows.go +++ b/vendor/github.com/gookit/goutil/sysutil/sysutil_windows.go @@ -5,36 +5,25 @@ package sysutil import ( "errors" - "os/exec" "syscall" - "github.com/gookit/goutil/sysutil/process" + "golang.org/x/sys/windows" ) // IsWin system. linux windows darwin -func IsWin() bool { - return true -} +func IsWin() bool { return true } // IsWindows system. linux windows darwin -func IsWindows() bool { - return true -} +func IsWindows() bool { return true } // IsMac system -func IsMac() bool { - return false -} +func IsMac() bool { return false } // IsDarwin system -func IsDarwin() bool { - return false -} +func IsDarwin() bool { return false } // IsLinux system -func IsLinux() bool { - return false -} +func IsLinux() bool { return false } // Kill a process by pid func Kill(pid int, signal syscall.Signal) error { @@ -43,10 +32,12 @@ func Kill(pid int, signal syscall.Signal) error { // ProcessExists check process exists by pid func ProcessExists(pid int) bool { - return process.Exists(pid) + panic("TIP: please use sysutil/process.Exists()") } -// OpenBrowser Open browser URL +// OpenURL Open file or browser URL +// +// - refers https://github.com/pkg/browser // // Mac: // @@ -60,6 +51,7 @@ func ProcessExists(pid int) bool { // Windows: // // cmd /c start https://github.com/inhere -func OpenBrowser(URL string) error { - return exec.Command("cmd", "/c", "start", URL).Run() +func OpenURL(url string) error { + // return exec.Command("cmd", "/C", "start", URL).Run() + return windows.ShellExecute(0, nil, windows.StringToUTF16Ptr(url), nil, nil, windows.SW_SHOWNORMAL) } diff --git a/vendor/github.com/gookit/goutil/sysutil/user.go b/vendor/github.com/gookit/goutil/sysutil/user.go index 4eeb6336b..76353857d 100644 --- a/vendor/github.com/gookit/goutil/sysutil/user.go +++ b/vendor/github.com/gookit/goutil/sysutil/user.go @@ -46,11 +46,9 @@ var homeDir string // UserHomeDir is alias of os.UserHomeDir, but ignore error func UserHomeDir() string { - if homeDir != "" { - return homeDir + if homeDir == "" { + homeDir, _ = os.UserHomeDir() } - - homeDir, _ = os.UserHomeDir() return homeDir } @@ -78,6 +76,7 @@ func UserConfigDir(subPath string) string { } // ExpandPath will parse `~` as user home dir path. -func ExpandPath(path string) string { - return comfunc.ExpandPath(path) -} +func ExpandPath(path string) string { return comfunc.ExpandHome(path) } + +// ExpandHome will parse `~` as user home dir path. +func ExpandHome(path string) string { return comfunc.ExpandHome(path) } diff --git a/vendor/github.com/gookit/goutil/sysutil/user_nonwin.go b/vendor/github.com/gookit/goutil/sysutil/user_nonwin.go index 556ee6ea8..6c52f5ac1 100644 --- a/vendor/github.com/gookit/goutil/sysutil/user_nonwin.go +++ b/vendor/github.com/gookit/goutil/sysutil/user_nonwin.go @@ -18,9 +18,9 @@ func ChangeUserByName(newUname string) (err error) { } // ChangeUserUidGid change work user by new username uid,gid -func ChangeUserUidGid(newUid int, newGid int) (err error) { - if newUid > 0 { - err = syscall.Setuid(newUid) +func ChangeUserUidGid(newUID int, newGid int) (err error) { + if newUID > 0 { + err = syscall.Setuid(newUID) // update group id if err == nil && newGid > 0 { diff --git a/vendor/github.com/imdario/mergo/CONTRIBUTING.md b/vendor/github.com/imdario/mergo/CONTRIBUTING.md new file mode 100644 index 000000000..0a1ff9f94 --- /dev/null +++ b/vendor/github.com/imdario/mergo/CONTRIBUTING.md @@ -0,0 +1,112 @@ + +# Contributing to mergo + +First off, thanks for taking the time to contribute! ❤️ + +All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions. 🎉 + +> And if you like the project, but just don't have time to contribute, that's fine. There are other easy ways to support the project and show your appreciation, which we would also be very happy about: +> - Star the project +> - Tweet about it +> - Refer this project in your project's readme +> - Mention the project at local meetups and tell your friends/colleagues + + +## Table of Contents + +- [Code of Conduct](#code-of-conduct) +- [I Have a Question](#i-have-a-question) +- [I Want To Contribute](#i-want-to-contribute) +- [Reporting Bugs](#reporting-bugs) +- [Suggesting Enhancements](#suggesting-enhancements) + +## Code of Conduct + +This project and everyone participating in it is governed by the +[mergo Code of Conduct](https://github.com/imdario/mergoblob/master/CODE_OF_CONDUCT.md). +By participating, you are expected to uphold this code. Please report unacceptable behavior +to <>. + + +## I Have a Question + +> If you want to ask a question, we assume that you have read the available [Documentation](https://pkg.go.dev/github.com/imdario/mergo). + +Before you ask a question, it is best to search for existing [Issues](https://github.com/imdario/mergo/issues) that might help you. In case you have found a suitable issue and still need clarification, you can write your question in this issue. It is also advisable to search the internet for answers first. + +If you then still feel the need to ask a question and need clarification, we recommend the following: + +- Open an [Issue](https://github.com/imdario/mergo/issues/new). +- Provide as much context as you can about what you're running into. +- Provide project and platform versions (nodejs, npm, etc), depending on what seems relevant. + +We will then take care of the issue as soon as possible. + +## I Want To Contribute + +> ### Legal Notice +> When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project license. + +### Reporting Bugs + + +#### Before Submitting a Bug Report + +A good bug report shouldn't leave others needing to chase you up for more information. Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report. Please complete the following steps in advance to help us fix any potential bug as fast as possible. + +- Make sure that you are using the latest version. +- Determine if your bug is really a bug and not an error on your side e.g. using incompatible environment components/versions (Make sure that you have read the [documentation](). If you are looking for support, you might want to check [this section](#i-have-a-question)). +- To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already a bug report existing for your bug or error in the [bug tracker](https://github.com/imdario/mergoissues?q=label%3Abug). +- Also make sure to search the internet (including Stack Overflow) to see if users outside of the GitHub community have discussed the issue. +- Collect information about the bug: +- Stack trace (Traceback) +- OS, Platform and Version (Windows, Linux, macOS, x86, ARM) +- Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what seems relevant. +- Possibly your input and the output +- Can you reliably reproduce the issue? And can you also reproduce it with older versions? + + +#### How Do I Submit a Good Bug Report? + +> You must never report security related issues, vulnerabilities or bugs including sensitive information to the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to . + + +We use GitHub issues to track bugs and errors. If you run into an issue with the project: + +- Open an [Issue](https://github.com/imdario/mergo/issues/new). (Since we can't be sure at this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.) +- Explain the behavior you would expect and the actual behavior. +- Please provide as much context as possible and describe the *reproduction steps* that someone else can follow to recreate the issue on their own. This usually includes your code. For good bug reports you should isolate the problem and create a reduced test case. +- Provide the information you collected in the previous section. + +Once it's filed: + +- The project team will label the issue accordingly. +- A team member will try to reproduce the issue with your provided steps. If there are no reproduction steps or no obvious way to reproduce the issue, the team will ask you for those steps and mark the issue as `needs-repro`. Bugs with the `needs-repro` tag will not be addressed until they are reproduced. +- If the team is able to reproduce the issue, it will be marked `needs-fix`, as well as possibly other tags (such as `critical`), and the issue will be left to be implemented by someone. + +### Suggesting Enhancements + +This section guides you through submitting an enhancement suggestion for mergo, **including completely new features and minor improvements to existing functionality**. Following these guidelines will help maintainers and the community to understand your suggestion and find related suggestions. + + +#### Before Submitting an Enhancement + +- Make sure that you are using the latest version. +- Read the [documentation]() carefully and find out if the functionality is already covered, maybe by an individual configuration. +- Perform a [search](https://github.com/imdario/mergo/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. +- Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the project's developers of the merits of this feature. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing an add-on/plugin library. + + +#### How Do I Submit a Good Enhancement Suggestion? + +Enhancement suggestions are tracked as [GitHub issues](https://github.com/imdario/mergo/issues). + +- Use a **clear and descriptive title** for the issue to identify the suggestion. +- Provide a **step-by-step description of the suggested enhancement** in as many details as possible. +- **Describe the current behavior** and **explain which behavior you expected to see instead** and why. At this point you can also tell which alternatives do not work for you. +- You may want to **include screenshots and animated GIFs** which help you demonstrate the steps or point out the part which the suggestion is related to. You can use [this tool](https://www.cockos.com/licecap/) to record GIFs on macOS and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux. +- **Explain why this enhancement would be useful** to most mergo users. You may also want to point out the other projects that solved it better and which could serve as inspiration. + + +## Attribution +This guide is based on the **contributing-gen**. [Make your own](https://github.com/bttger/contributing-gen)! diff --git a/vendor/github.com/imdario/mergo/README.md b/vendor/github.com/imdario/mergo/README.md index 7e6f7aeee..4f0287498 100644 --- a/vendor/github.com/imdario/mergo/README.md +++ b/vendor/github.com/imdario/mergo/README.md @@ -1,6 +1,5 @@ # Mergo - [![GoDoc][3]][4] [![GitHub release][5]][6] [![GoCard][7]][8] @@ -9,6 +8,7 @@ [![Sourcegraph][11]][12] [![FOSSA Status][13]][14] [![Become my sponsor][15]][16] +[![Tidelift][17]][18] [1]: https://travis-ci.org/imdario/mergo.png [2]: https://travis-ci.org/imdario/mergo @@ -26,6 +26,8 @@ [14]: https://app.fossa.io/projects/git%2Bgithub.com%2Fimdario%2Fmergo?ref=badge_shield [15]: https://img.shields.io/github/sponsors/imdario [16]: https://github.com/sponsors/imdario +[17]: https://tidelift.com/badges/package/go/github.com%2Fimdario%2Fmergo +[18]: https://tidelift.com/subscription/pkg/go-github.com-imdario-mergo A helper to merge structs and maps in Golang. Useful for configuration default values, avoiding messy if-statements. @@ -55,7 +57,6 @@ If Mergo is useful to you, consider buying me a coffee, a beer, or making a mont ### Mergo in the wild -- [cli/cli](https://github.com/cli/cli) - [moby/moby](https://github.com/moby/moby) - [kubernetes/kubernetes](https://github.com/kubernetes/kubernetes) - [vmware/dispatch](https://github.com/vmware/dispatch) diff --git a/vendor/github.com/imdario/mergo/SECURITY.md b/vendor/github.com/imdario/mergo/SECURITY.md new file mode 100644 index 000000000..a5de61f77 --- /dev/null +++ b/vendor/github.com/imdario/mergo/SECURITY.md @@ -0,0 +1,14 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| 0.3.x | :white_check_mark: | +| < 0.3 | :x: | + +## Security contact information + +To report a security vulnerability, please use the +[Tidelift security contact](https://tidelift.com/security). +Tidelift will coordinate the fix and disclosure. diff --git a/vendor/github.com/imdario/mergo/map.go b/vendor/github.com/imdario/mergo/map.go index a13a7ee46..b50d5c2a4 100644 --- a/vendor/github.com/imdario/mergo/map.go +++ b/vendor/github.com/imdario/mergo/map.go @@ -44,7 +44,7 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, conf } } // Remember, remember... - visited[h] = &visit{addr, typ, seen} + visited[h] = &visit{typ, seen, addr} } zeroValue := reflect.Value{} switch dst.Kind() { @@ -58,7 +58,7 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, conf } fieldName := field.Name fieldName = changeInitialCase(fieldName, unicode.ToLower) - if v, ok := dstMap[fieldName]; !ok || (isEmptyValue(reflect.ValueOf(v)) || overwrite) { + if v, ok := dstMap[fieldName]; !ok || (isEmptyValue(reflect.ValueOf(v), !config.ShouldNotDereference) || overwrite) { dstMap[fieldName] = src.Field(i).Interface() } } @@ -142,7 +142,7 @@ func MapWithOverwrite(dst, src interface{}, opts ...func(*Config)) error { func _map(dst, src interface{}, opts ...func(*Config)) error { if dst != nil && reflect.ValueOf(dst).Kind() != reflect.Ptr { - return ErrNonPointerAgument + return ErrNonPointerArgument } var ( vDst, vSrc reflect.Value diff --git a/vendor/github.com/imdario/mergo/merge.go b/vendor/github.com/imdario/mergo/merge.go index 8b4e2f47a..0ef9b2138 100644 --- a/vendor/github.com/imdario/mergo/merge.go +++ b/vendor/github.com/imdario/mergo/merge.go @@ -38,10 +38,11 @@ func isExportedComponent(field *reflect.StructField) bool { } type Config struct { + Transformers Transformers Overwrite bool + ShouldNotDereference bool AppendSlice bool TypeCheck bool - Transformers Transformers overwriteWithEmptyValue bool overwriteSliceWithEmptyValue bool sliceDeepCopy bool @@ -76,7 +77,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co } } // Remember, remember... - visited[h] = &visit{addr, typ, seen} + visited[h] = &visit{typ, seen, addr} } if config.Transformers != nil && !isReflectNil(dst) && dst.IsValid() { @@ -95,7 +96,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co } } } else { - if dst.CanSet() && (isReflectNil(dst) || overwrite) && (!isEmptyValue(src) || overwriteWithEmptySrc) { + if dst.CanSet() && (isReflectNil(dst) || overwrite) && (!isEmptyValue(src, !config.ShouldNotDereference) || overwriteWithEmptySrc) { dst.Set(src) } } @@ -110,7 +111,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co } if src.Kind() != reflect.Map { - if overwrite { + if overwrite && dst.CanSet() { dst.Set(src) } return @@ -162,7 +163,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co dstSlice = reflect.ValueOf(dstElement.Interface()) } - if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice && !sliceDeepCopy { + if (!isEmptyValue(src, !config.ShouldNotDereference) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst, !config.ShouldNotDereference)) && !config.AppendSlice && !sliceDeepCopy { if typeCheck && srcSlice.Type() != dstSlice.Type() { return fmt.Errorf("cannot override two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type()) } @@ -194,22 +195,38 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co dst.SetMapIndex(key, dstSlice) } } - if dstElement.IsValid() && !isEmptyValue(dstElement) && (reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map || reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Slice) { - continue + + if dstElement.IsValid() && !isEmptyValue(dstElement, !config.ShouldNotDereference) { + if reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Slice { + continue + } + if reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map && reflect.TypeOf(dstElement.Interface()).Kind() == reflect.Map { + continue + } } - if srcElement.IsValid() && ((srcElement.Kind() != reflect.Ptr && overwrite) || !dstElement.IsValid() || isEmptyValue(dstElement)) { + if srcElement.IsValid() && ((srcElement.Kind() != reflect.Ptr && overwrite) || !dstElement.IsValid() || isEmptyValue(dstElement, !config.ShouldNotDereference)) { if dst.IsNil() { dst.Set(reflect.MakeMap(dst.Type())) } dst.SetMapIndex(key, srcElement) } } + + // Ensure that all keys in dst are deleted if they are not in src. + if overwriteWithEmptySrc { + for _, key := range dst.MapKeys() { + srcElement := src.MapIndex(key) + if !srcElement.IsValid() { + dst.SetMapIndex(key, reflect.Value{}) + } + } + } case reflect.Slice: if !dst.CanSet() { break } - if (!isEmptyValue(src) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice && !sliceDeepCopy { + if (!isEmptyValue(src, !config.ShouldNotDereference) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst, !config.ShouldNotDereference)) && !config.AppendSlice && !sliceDeepCopy { dst.Set(src) } else if config.AppendSlice { if src.Type() != dst.Type() { @@ -244,12 +261,18 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co if src.Kind() != reflect.Interface { if dst.IsNil() || (src.Kind() != reflect.Ptr && overwrite) { - if dst.CanSet() && (overwrite || isEmptyValue(dst)) { + if dst.CanSet() && (overwrite || isEmptyValue(dst, !config.ShouldNotDereference)) { dst.Set(src) } } else if src.Kind() == reflect.Ptr { - if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil { - return + if !config.ShouldNotDereference { + if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil { + return + } + } else { + if overwriteWithEmptySrc || (overwrite && !src.IsNil()) || dst.IsNil() { + dst.Set(src) + } } } else if dst.Elem().Type() == src.Type() { if err = deepMerge(dst.Elem(), src, visited, depth+1, config); err != nil { @@ -262,7 +285,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co } if dst.IsNil() || overwrite { - if dst.CanSet() && (overwrite || isEmptyValue(dst)) { + if dst.CanSet() && (overwrite || isEmptyValue(dst, !config.ShouldNotDereference)) { dst.Set(src) } break @@ -275,7 +298,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co break } default: - mustSet := (isEmptyValue(dst) || overwrite) && (!isEmptyValue(src) || overwriteWithEmptySrc) + mustSet := (isEmptyValue(dst, !config.ShouldNotDereference) || overwrite) && (!isEmptyValue(src, !config.ShouldNotDereference) || overwriteWithEmptySrc) if mustSet { if dst.CanSet() { dst.Set(src) @@ -326,6 +349,12 @@ func WithOverrideEmptySlice(config *Config) { config.overwriteSliceWithEmptyValue = true } +// WithoutDereference prevents dereferencing pointers when evaluating whether they are empty +// (i.e. a non-nil pointer is never considered empty). +func WithoutDereference(config *Config) { + config.ShouldNotDereference = true +} + // WithAppendSlice will make merge append slices instead of overwriting it. func WithAppendSlice(config *Config) { config.AppendSlice = true @@ -344,7 +373,7 @@ func WithSliceDeepCopy(config *Config) { func merge(dst, src interface{}, opts ...func(*Config)) error { if dst != nil && reflect.ValueOf(dst).Kind() != reflect.Ptr { - return ErrNonPointerAgument + return ErrNonPointerArgument } var ( vDst, vSrc reflect.Value diff --git a/vendor/github.com/imdario/mergo/mergo.go b/vendor/github.com/imdario/mergo/mergo.go index 9fe362d47..0a721e2d8 100644 --- a/vendor/github.com/imdario/mergo/mergo.go +++ b/vendor/github.com/imdario/mergo/mergo.go @@ -20,7 +20,7 @@ var ( ErrNotSupported = errors.New("only structs, maps, and slices are supported") ErrExpectedMapAsDestination = errors.New("dst was expected to be a map") ErrExpectedStructAsDestination = errors.New("dst was expected to be a struct") - ErrNonPointerAgument = errors.New("dst must be a pointer") + ErrNonPointerArgument = errors.New("dst must be a pointer") ) // During deepMerge, must keep track of checks that are @@ -28,13 +28,13 @@ var ( // checks in progress are true when it reencounters them. // Visited are stored in a map indexed by 17 * a1 + a2; type visit struct { - ptr uintptr typ reflect.Type next *visit + ptr uintptr } // From src/pkg/encoding/json/encode.go. -func isEmptyValue(v reflect.Value) bool { +func isEmptyValue(v reflect.Value, shouldDereference bool) bool { switch v.Kind() { case reflect.Array, reflect.Map, reflect.Slice, reflect.String: return v.Len() == 0 @@ -50,7 +50,10 @@ func isEmptyValue(v reflect.Value) bool { if v.IsNil() { return true } - return isEmptyValue(v.Elem()) + if shouldDereference { + return isEmptyValue(v.Elem(), shouldDereference) + } + return false case reflect.Func: return v.IsNil() case reflect.Invalid: diff --git a/vendor/github.com/mattn/go-isatty/isatty_bsd.go b/vendor/github.com/mattn/go-isatty/isatty_bsd.go index 39bbcf00f..d569c0c94 100644 --- a/vendor/github.com/mattn/go-isatty/isatty_bsd.go +++ b/vendor/github.com/mattn/go-isatty/isatty_bsd.go @@ -1,5 +1,5 @@ -//go:build (darwin || freebsd || openbsd || netbsd || dragonfly) && !appengine -// +build darwin freebsd openbsd netbsd dragonfly +//go:build (darwin || freebsd || openbsd || netbsd || dragonfly || hurd) && !appengine +// +build darwin freebsd openbsd netbsd dragonfly hurd // +build !appengine package isatty diff --git a/vendor/github.com/xo/terminfo/caps.go b/vendor/github.com/xo/terminfo/caps.go index 9674aaa7b..e5e1d41f1 100644 --- a/vendor/github.com/xo/terminfo/caps.go +++ b/vendor/github.com/xo/terminfo/caps.go @@ -1,7 +1,5 @@ package terminfo -//go:generate go run gen.go - // BoolCapName returns the bool capability name. func BoolCapName(i int) string { return boolCapNames[2*i] diff --git a/vendor/github.com/xo/terminfo/capvals.go b/vendor/github.com/xo/terminfo/capvals.go index 8528740ea..0c2274e3a 100644 --- a/vendor/github.com/xo/terminfo/capvals.go +++ b/vendor/github.com/xo/terminfo/capvals.go @@ -1,138 +1,94 @@ package terminfo // Code generated by gen.go. DO NOT EDIT. - // Bool capabilities. const ( // The AutoLeftMargin [auto_left_margin, bw] bool capability indicates cub1 wraps from column 0 to last column. AutoLeftMargin = iota - // The AutoRightMargin [auto_right_margin, am] bool capability indicates terminal has automatic margins. AutoRightMargin - // The NoEscCtlc [no_esc_ctlc, xsb] bool capability indicates beehive (f1=escape, f2=ctrl C). NoEscCtlc - // The CeolStandoutGlitch [ceol_standout_glitch, xhp] bool capability indicates standout not erased by overwriting (hp). CeolStandoutGlitch - // The EatNewlineGlitch [eat_newline_glitch, xenl] bool capability indicates newline ignored after 80 cols (concept). EatNewlineGlitch - // The EraseOverstrike [erase_overstrike, eo] bool capability indicates can erase overstrikes with a blank. EraseOverstrike - // The GenericType [generic_type, gn] bool capability indicates generic line type. GenericType - // The HardCopy [hard_copy, hc] bool capability indicates hardcopy terminal. HardCopy - // The HasMetaKey [has_meta_key, km] bool capability indicates Has a meta key (i.e., sets 8th-bit). HasMetaKey - // The HasStatusLine [has_status_line, hs] bool capability indicates has extra status line. HasStatusLine - // The InsertNullGlitch [insert_null_glitch, in] bool capability indicates insert mode distinguishes nulls. InsertNullGlitch - // The MemoryAbove [memory_above, da] bool capability indicates display may be retained above the screen. MemoryAbove - // The MemoryBelow [memory_below, db] bool capability indicates display may be retained below the screen. MemoryBelow - // The MoveInsertMode [move_insert_mode, mir] bool capability indicates safe to move while in insert mode. MoveInsertMode - // The MoveStandoutMode [move_standout_mode, msgr] bool capability indicates safe to move while in standout mode. MoveStandoutMode - // The OverStrike [over_strike, os] bool capability indicates terminal can overstrike. OverStrike - // The StatusLineEscOk [status_line_esc_ok, eslok] bool capability indicates escape can be used on the status line. StatusLineEscOk - // The DestTabsMagicSmso [dest_tabs_magic_smso, xt] bool capability indicates tabs destructive, magic so char (t1061). DestTabsMagicSmso - // The TildeGlitch [tilde_glitch, hz] bool capability indicates cannot print ~'s (Hazeltine). TildeGlitch - // The TransparentUnderline [transparent_underline, ul] bool capability indicates underline character overstrikes. TransparentUnderline - // The XonXoff [xon_xoff, xon] bool capability indicates terminal uses xon/xoff handshaking. XonXoff - // The NeedsXonXoff [needs_xon_xoff, nxon] bool capability indicates padding will not work, xon/xoff required. NeedsXonXoff - // The PrtrSilent [prtr_silent, mc5i] bool capability indicates printer will not echo on screen. PrtrSilent - // The HardCursor [hard_cursor, chts] bool capability indicates cursor is hard to see. HardCursor - // The NonRevRmcup [non_rev_rmcup, nrrmc] bool capability indicates smcup does not reverse rmcup. NonRevRmcup - // The NoPadChar [no_pad_char, npc] bool capability indicates pad character does not exist. NoPadChar - // The NonDestScrollRegion [non_dest_scroll_region, ndscr] bool capability indicates scrolling region is non-destructive. NonDestScrollRegion - // The CanChange [can_change, ccc] bool capability indicates terminal can re-define existing colors. CanChange - // The BackColorErase [back_color_erase, bce] bool capability indicates screen erased with background color. BackColorErase - // The HueLightnessSaturation [hue_lightness_saturation, hls] bool capability indicates terminal uses only HLS color notation (Tektronix). HueLightnessSaturation - // The ColAddrGlitch [col_addr_glitch, xhpa] bool capability indicates only positive motion for hpa/mhpa caps. ColAddrGlitch - // The CrCancelsMicroMode [cr_cancels_micro_mode, crxm] bool capability indicates using cr turns off micro mode. CrCancelsMicroMode - // The HasPrintWheel [has_print_wheel, daisy] bool capability indicates printer needs operator to change character set. HasPrintWheel - // The RowAddrGlitch [row_addr_glitch, xvpa] bool capability indicates only positive motion for vpa/mvpa caps. RowAddrGlitch - // The SemiAutoRightMargin [semi_auto_right_margin, sam] bool capability indicates printing in last column causes cr. SemiAutoRightMargin - // The CpiChangesRes [cpi_changes_res, cpix] bool capability indicates changing character pitch changes resolution. CpiChangesRes - // The LpiChangesRes [lpi_changes_res, lpix] bool capability indicates changing line pitch changes resolution. LpiChangesRes - // The BackspacesWithBs [backspaces_with_bs, OTbs] bool capability indicates uses ^H to move left. BackspacesWithBs - // The CrtNoScrolling [crt_no_scrolling, OTns] bool capability indicates crt cannot scroll. CrtNoScrolling - // The NoCorrectlyWorkingCr [no_correctly_working_cr, OTnc] bool capability indicates no way to go to start of line. NoCorrectlyWorkingCr - // The GnuHasMetaKey [gnu_has_meta_key, OTMT] bool capability indicates has meta key. GnuHasMetaKey - // The LinefeedIsNewline [linefeed_is_newline, OTNL] bool capability indicates move down with \n. LinefeedIsNewline - // The HasHardwareTabs [has_hardware_tabs, OTpt] bool capability indicates has 8-char tabs invoked with ^I. HasHardwareTabs - // The ReturnDoesClrEol [return_does_clr_eol, OTxr] bool capability indicates return clears the line. ReturnDoesClrEol ) @@ -141,118 +97,80 @@ const ( const ( // The Columns [columns, cols] num capability is number of columns in a line. Columns = iota - // The InitTabs [init_tabs, it] num capability is tabs initially every # spaces. InitTabs - // The Lines [lines, lines] num capability is number of lines on screen or page. Lines - // The LinesOfMemory [lines_of_memory, lm] num capability is lines of memory if > line. 0 means varies. LinesOfMemory - // The MagicCookieGlitch [magic_cookie_glitch, xmc] num capability is number of blank characters left by smso or rmso. MagicCookieGlitch - // The PaddingBaudRate [padding_baud_rate, pb] num capability is lowest baud rate where padding needed. PaddingBaudRate - // The VirtualTerminal [virtual_terminal, vt] num capability is virtual terminal number (CB/unix). VirtualTerminal - // The WidthStatusLine [width_status_line, wsl] num capability is number of columns in status line. WidthStatusLine - // The NumLabels [num_labels, nlab] num capability is number of labels on screen. NumLabels - // The LabelHeight [label_height, lh] num capability is rows in each label. LabelHeight - // The LabelWidth [label_width, lw] num capability is columns in each label. LabelWidth - // The MaxAttributes [max_attributes, ma] num capability is maximum combined attributes terminal can handle. MaxAttributes - // The MaximumWindows [maximum_windows, wnum] num capability is maximum number of definable windows. MaximumWindows - // The MaxColors [max_colors, colors] num capability is maximum number of colors on screen. MaxColors - // The MaxPairs [max_pairs, pairs] num capability is maximum number of color-pairs on the screen. MaxPairs - // The NoColorVideo [no_color_video, ncv] num capability is video attributes that cannot be used with colors. NoColorVideo - // The BufferCapacity [buffer_capacity, bufsz] num capability is numbers of bytes buffered before printing. BufferCapacity - // The DotVertSpacing [dot_vert_spacing, spinv] num capability is spacing of pins vertically in pins per inch. DotVertSpacing - // The DotHorzSpacing [dot_horz_spacing, spinh] num capability is spacing of dots horizontally in dots per inch. DotHorzSpacing - // The MaxMicroAddress [max_micro_address, maddr] num capability is maximum value in micro_..._address. MaxMicroAddress - // The MaxMicroJump [max_micro_jump, mjump] num capability is maximum value in parm_..._micro. MaxMicroJump - // The MicroColSize [micro_col_size, mcs] num capability is character step size when in micro mode. MicroColSize - // The MicroLineSize [micro_line_size, mls] num capability is line step size when in micro mode. MicroLineSize - // The NumberOfPins [number_of_pins, npins] num capability is numbers of pins in print-head. NumberOfPins - // The OutputResChar [output_res_char, orc] num capability is horizontal resolution in units per line. OutputResChar - // The OutputResLine [output_res_line, orl] num capability is vertical resolution in units per line. OutputResLine - // The OutputResHorzInch [output_res_horz_inch, orhi] num capability is horizontal resolution in units per inch. OutputResHorzInch - // The OutputResVertInch [output_res_vert_inch, orvi] num capability is vertical resolution in units per inch. OutputResVertInch - // The PrintRate [print_rate, cps] num capability is print rate in characters per second. PrintRate - // The WideCharSize [wide_char_size, widcs] num capability is character step size when in double wide mode. WideCharSize - // The Buttons [buttons, btns] num capability is number of buttons on mouse. Buttons - // The BitImageEntwining [bit_image_entwining, bitwin] num capability is number of passes for each bit-image row. BitImageEntwining - // The BitImageType [bit_image_type, bitype] num capability is type of bit-image device. BitImageType - // The MagicCookieGlitchUl [magic_cookie_glitch_ul, OTug] num capability is number of blanks left by ul. MagicCookieGlitchUl - // The CarriageReturnDelay [carriage_return_delay, OTdC] num capability is pad needed for CR. CarriageReturnDelay - // The NewLineDelay [new_line_delay, OTdN] num capability is pad needed for LF. NewLineDelay - // The BackspaceDelay [backspace_delay, OTdB] num capability is padding required for ^H. BackspaceDelay - // The HorizontalTabDelay [horizontal_tab_delay, OTdT] num capability is padding required for ^I. HorizontalTabDelay - // The NumberOfFunctionKeys [number_of_function_keys, OTkn] num capability is count of function keys. NumberOfFunctionKeys ) @@ -261,1254 +179,838 @@ const ( const ( // The BackTab [back_tab, cbt] string capability is the back tab (P). BackTab = iota - // The Bell [bell, bel] string capability is the audible signal (bell) (P). Bell - // The CarriageReturn [carriage_return, cr] string capability is the carriage return (P*) (P*). CarriageReturn - // The ChangeScrollRegion [change_scroll_region, csr] string capability is the change region to line #1 to line #2 (P). ChangeScrollRegion - // The ClearAllTabs [clear_all_tabs, tbc] string capability is the clear all tab stops (P). ClearAllTabs - // The ClearScreen [clear_screen, clear] string capability is the clear screen and home cursor (P*). ClearScreen - // The ClrEol [clr_eol, el] string capability is the clear to end of line (P). ClrEol - // The ClrEos [clr_eos, ed] string capability is the clear to end of screen (P*). ClrEos - // The ColumnAddress [column_address, hpa] string capability is the horizontal position #1, absolute (P). ColumnAddress - // The CommandCharacter [command_character, cmdch] string capability is the terminal settable cmd character in prototype !?. CommandCharacter - // The CursorAddress [cursor_address, cup] string capability is the move to row #1 columns #2. CursorAddress - // The CursorDown [cursor_down, cud1] string capability is the down one line. CursorDown - // The CursorHome [cursor_home, home] string capability is the home cursor (if no cup). CursorHome - // The CursorInvisible [cursor_invisible, civis] string capability is the make cursor invisible. CursorInvisible - // The CursorLeft [cursor_left, cub1] string capability is the move left one space. CursorLeft - // The CursorMemAddress [cursor_mem_address, mrcup] string capability is the memory relative cursor addressing, move to row #1 columns #2. CursorMemAddress - // The CursorNormal [cursor_normal, cnorm] string capability is the make cursor appear normal (undo civis/cvvis). CursorNormal - // The CursorRight [cursor_right, cuf1] string capability is the non-destructive space (move right one space). CursorRight - // The CursorToLl [cursor_to_ll, ll] string capability is the last line, first column (if no cup). CursorToLl - // The CursorUp [cursor_up, cuu1] string capability is the up one line. CursorUp - // The CursorVisible [cursor_visible, cvvis] string capability is the make cursor very visible. CursorVisible - // The DeleteCharacter [delete_character, dch1] string capability is the delete character (P*). DeleteCharacter - // The DeleteLine [delete_line, dl1] string capability is the delete line (P*). DeleteLine - // The DisStatusLine [dis_status_line, dsl] string capability is the disable status line. DisStatusLine - // The DownHalfLine [down_half_line, hd] string capability is the half a line down. DownHalfLine - // The EnterAltCharsetMode [enter_alt_charset_mode, smacs] string capability is the start alternate character set (P). EnterAltCharsetMode - // The EnterBlinkMode [enter_blink_mode, blink] string capability is the turn on blinking. EnterBlinkMode - // The EnterBoldMode [enter_bold_mode, bold] string capability is the turn on bold (extra bright) mode. EnterBoldMode - // The EnterCaMode [enter_ca_mode, smcup] string capability is the string to start programs using cup. EnterCaMode - // The EnterDeleteMode [enter_delete_mode, smdc] string capability is the enter delete mode. EnterDeleteMode - // The EnterDimMode [enter_dim_mode, dim] string capability is the turn on half-bright mode. EnterDimMode - // The EnterInsertMode [enter_insert_mode, smir] string capability is the enter insert mode. EnterInsertMode - // The EnterSecureMode [enter_secure_mode, invis] string capability is the turn on blank mode (characters invisible). EnterSecureMode - // The EnterProtectedMode [enter_protected_mode, prot] string capability is the turn on protected mode. EnterProtectedMode - // The EnterReverseMode [enter_reverse_mode, rev] string capability is the turn on reverse video mode. EnterReverseMode - // The EnterStandoutMode [enter_standout_mode, smso] string capability is the begin standout mode. EnterStandoutMode - // The EnterUnderlineMode [enter_underline_mode, smul] string capability is the begin underline mode. EnterUnderlineMode - // The EraseChars [erase_chars, ech] string capability is the erase #1 characters (P). EraseChars - // The ExitAltCharsetMode [exit_alt_charset_mode, rmacs] string capability is the end alternate character set (P). ExitAltCharsetMode - // The ExitAttributeMode [exit_attribute_mode, sgr0] string capability is the turn off all attributes. ExitAttributeMode - // The ExitCaMode [exit_ca_mode, rmcup] string capability is the strings to end programs using cup. ExitCaMode - // The ExitDeleteMode [exit_delete_mode, rmdc] string capability is the end delete mode. ExitDeleteMode - // The ExitInsertMode [exit_insert_mode, rmir] string capability is the exit insert mode. ExitInsertMode - // The ExitStandoutMode [exit_standout_mode, rmso] string capability is the exit standout mode. ExitStandoutMode - // The ExitUnderlineMode [exit_underline_mode, rmul] string capability is the exit underline mode. ExitUnderlineMode - // The FlashScreen [flash_screen, flash] string capability is the visible bell (may not move cursor). FlashScreen - // The FormFeed [form_feed, ff] string capability is the hardcopy terminal page eject (P*). FormFeed - // The FromStatusLine [from_status_line, fsl] string capability is the return from status line. FromStatusLine - // The Init1string [init_1string, is1] string capability is the initialization string. Init1string - // The Init2string [init_2string, is2] string capability is the initialization string. Init2string - // The Init3string [init_3string, is3] string capability is the initialization string. Init3string - // The InitFile [init_file, if] string capability is the name of initialization file. InitFile - // The InsertCharacter [insert_character, ich1] string capability is the insert character (P). InsertCharacter - // The InsertLine [insert_line, il1] string capability is the insert line (P*). InsertLine - // The InsertPadding [insert_padding, ip] string capability is the insert padding after inserted character. InsertPadding - // The KeyBackspace [key_backspace, kbs] string capability is the backspace key. KeyBackspace - // The KeyCatab [key_catab, ktbc] string capability is the clear-all-tabs key. KeyCatab - // The KeyClear [key_clear, kclr] string capability is the clear-screen or erase key. KeyClear - // The KeyCtab [key_ctab, kctab] string capability is the clear-tab key. KeyCtab - // The KeyDc [key_dc, kdch1] string capability is the delete-character key. KeyDc - // The KeyDl [key_dl, kdl1] string capability is the delete-line key. KeyDl - // The KeyDown [key_down, kcud1] string capability is the down-arrow key. KeyDown - // The KeyEic [key_eic, krmir] string capability is the sent by rmir or smir in insert mode. KeyEic - // The KeyEol [key_eol, kel] string capability is the clear-to-end-of-line key. KeyEol - // The KeyEos [key_eos, ked] string capability is the clear-to-end-of-screen key. KeyEos - // The KeyF0 [key_f0, kf0] string capability is the F0 function key. KeyF0 - // The KeyF1 [key_f1, kf1] string capability is the F1 function key. KeyF1 - // The KeyF10 [key_f10, kf10] string capability is the F10 function key. KeyF10 - // The KeyF2 [key_f2, kf2] string capability is the F2 function key. KeyF2 - // The KeyF3 [key_f3, kf3] string capability is the F3 function key. KeyF3 - // The KeyF4 [key_f4, kf4] string capability is the F4 function key. KeyF4 - // The KeyF5 [key_f5, kf5] string capability is the F5 function key. KeyF5 - // The KeyF6 [key_f6, kf6] string capability is the F6 function key. KeyF6 - // The KeyF7 [key_f7, kf7] string capability is the F7 function key. KeyF7 - // The KeyF8 [key_f8, kf8] string capability is the F8 function key. KeyF8 - // The KeyF9 [key_f9, kf9] string capability is the F9 function key. KeyF9 - // The KeyHome [key_home, khome] string capability is the home key. KeyHome - // The KeyIc [key_ic, kich1] string capability is the insert-character key. KeyIc - // The KeyIl [key_il, kil1] string capability is the insert-line key. KeyIl - // The KeyLeft [key_left, kcub1] string capability is the left-arrow key. KeyLeft - // The KeyLl [key_ll, kll] string capability is the lower-left key (home down). KeyLl - // The KeyNpage [key_npage, knp] string capability is the next-page key. KeyNpage - // The KeyPpage [key_ppage, kpp] string capability is the previous-page key. KeyPpage - // The KeyRight [key_right, kcuf1] string capability is the right-arrow key. KeyRight - // The KeySf [key_sf, kind] string capability is the scroll-forward key. KeySf - // The KeySr [key_sr, kri] string capability is the scroll-backward key. KeySr - // The KeyStab [key_stab, khts] string capability is the set-tab key. KeyStab - // The KeyUp [key_up, kcuu1] string capability is the up-arrow key. KeyUp - // The KeypadLocal [keypad_local, rmkx] string capability is the leave 'keyboard_transmit' mode. KeypadLocal - // The KeypadXmit [keypad_xmit, smkx] string capability is the enter 'keyboard_transmit' mode. KeypadXmit - // The LabF0 [lab_f0, lf0] string capability is the label on function key f0 if not f0. LabF0 - // The LabF1 [lab_f1, lf1] string capability is the label on function key f1 if not f1. LabF1 - // The LabF10 [lab_f10, lf10] string capability is the label on function key f10 if not f10. LabF10 - // The LabF2 [lab_f2, lf2] string capability is the label on function key f2 if not f2. LabF2 - // The LabF3 [lab_f3, lf3] string capability is the label on function key f3 if not f3. LabF3 - // The LabF4 [lab_f4, lf4] string capability is the label on function key f4 if not f4. LabF4 - // The LabF5 [lab_f5, lf5] string capability is the label on function key f5 if not f5. LabF5 - // The LabF6 [lab_f6, lf6] string capability is the label on function key f6 if not f6. LabF6 - // The LabF7 [lab_f7, lf7] string capability is the label on function key f7 if not f7. LabF7 - // The LabF8 [lab_f8, lf8] string capability is the label on function key f8 if not f8. LabF8 - // The LabF9 [lab_f9, lf9] string capability is the label on function key f9 if not f9. LabF9 - // The MetaOff [meta_off, rmm] string capability is the turn off meta mode. MetaOff - // The MetaOn [meta_on, smm] string capability is the turn on meta mode (8th-bit on). MetaOn - // The Newline [newline, nel] string capability is the newline (behave like cr followed by lf). Newline - // The PadChar [pad_char, pad] string capability is the padding char (instead of null). PadChar - // The ParmDch [parm_dch, dch] string capability is the delete #1 characters (P*). ParmDch - // The ParmDeleteLine [parm_delete_line, dl] string capability is the delete #1 lines (P*). ParmDeleteLine - // The ParmDownCursor [parm_down_cursor, cud] string capability is the down #1 lines (P*). ParmDownCursor - // The ParmIch [parm_ich, ich] string capability is the insert #1 characters (P*). ParmIch - // The ParmIndex [parm_index, indn] string capability is the scroll forward #1 lines (P). ParmIndex - // The ParmInsertLine [parm_insert_line, il] string capability is the insert #1 lines (P*). ParmInsertLine - // The ParmLeftCursor [parm_left_cursor, cub] string capability is the move #1 characters to the left (P). ParmLeftCursor - // The ParmRightCursor [parm_right_cursor, cuf] string capability is the move #1 characters to the right (P*). ParmRightCursor - // The ParmRindex [parm_rindex, rin] string capability is the scroll back #1 lines (P). ParmRindex - // The ParmUpCursor [parm_up_cursor, cuu] string capability is the up #1 lines (P*). ParmUpCursor - // The PkeyKey [pkey_key, pfkey] string capability is the program function key #1 to type string #2. PkeyKey - // The PkeyLocal [pkey_local, pfloc] string capability is the program function key #1 to execute string #2. PkeyLocal - // The PkeyXmit [pkey_xmit, pfx] string capability is the program function key #1 to transmit string #2. PkeyXmit - // The PrintScreen [print_screen, mc0] string capability is the print contents of screen. PrintScreen - // The PrtrOff [prtr_off, mc4] string capability is the turn off printer. PrtrOff - // The PrtrOn [prtr_on, mc5] string capability is the turn on printer. PrtrOn - // The RepeatChar [repeat_char, rep] string capability is the repeat char #1 #2 times (P*). RepeatChar - // The Reset1string [reset_1string, rs1] string capability is the reset string. Reset1string - // The Reset2string [reset_2string, rs2] string capability is the reset string. Reset2string - // The Reset3string [reset_3string, rs3] string capability is the reset string. Reset3string - // The ResetFile [reset_file, rf] string capability is the name of reset file. ResetFile - // The RestoreCursor [restore_cursor, rc] string capability is the restore cursor to position of last save_cursor. RestoreCursor - // The RowAddress [row_address, vpa] string capability is the vertical position #1 absolute (P). RowAddress - // The SaveCursor [save_cursor, sc] string capability is the save current cursor position (P). SaveCursor - // The ScrollForward [scroll_forward, ind] string capability is the scroll text up (P). ScrollForward - // The ScrollReverse [scroll_reverse, ri] string capability is the scroll text down (P). ScrollReverse - // The SetAttributes [set_attributes, sgr] string capability is the define video attributes #1-#9 (PG9). SetAttributes - // The SetTab [set_tab, hts] string capability is the set a tab in every row, current columns. SetTab - // The SetWindow [set_window, wind] string capability is the current window is lines #1-#2 cols #3-#4. SetWindow - // The Tab [tab, ht] string capability is the tab to next 8-space hardware tab stop. Tab - // The ToStatusLine [to_status_line, tsl] string capability is the move to status line, column #1. ToStatusLine - // The UnderlineChar [underline_char, uc] string capability is the underline char and move past it. UnderlineChar - // The UpHalfLine [up_half_line, hu] string capability is the half a line up. UpHalfLine - // The InitProg [init_prog, iprog] string capability is the path name of program for initialization. InitProg - // The KeyA1 [key_a1, ka1] string capability is the upper left of keypad. KeyA1 - // The KeyA3 [key_a3, ka3] string capability is the upper right of keypad. KeyA3 - // The KeyB2 [key_b2, kb2] string capability is the center of keypad. KeyB2 - // The KeyC1 [key_c1, kc1] string capability is the lower left of keypad. KeyC1 - // The KeyC3 [key_c3, kc3] string capability is the lower right of keypad. KeyC3 - // The PrtrNon [prtr_non, mc5p] string capability is the turn on printer for #1 bytes. PrtrNon - // The CharPadding [char_padding, rmp] string capability is the like ip but when in insert mode. CharPadding - // The AcsChars [acs_chars, acsc] string capability is the graphics charset pairs, based on vt100. AcsChars - // The PlabNorm [plab_norm, pln] string capability is the program label #1 to show string #2. PlabNorm - // The KeyBtab [key_btab, kcbt] string capability is the back-tab key. KeyBtab - // The EnterXonMode [enter_xon_mode, smxon] string capability is the turn on xon/xoff handshaking. EnterXonMode - // The ExitXonMode [exit_xon_mode, rmxon] string capability is the turn off xon/xoff handshaking. ExitXonMode - // The EnterAmMode [enter_am_mode, smam] string capability is the turn on automatic margins. EnterAmMode - // The ExitAmMode [exit_am_mode, rmam] string capability is the turn off automatic margins. ExitAmMode - // The XonCharacter [xon_character, xonc] string capability is the XON character. XonCharacter - // The XoffCharacter [xoff_character, xoffc] string capability is the XOFF character. XoffCharacter - // The EnaAcs [ena_acs, enacs] string capability is the enable alternate char set. EnaAcs - // The LabelOn [label_on, smln] string capability is the turn on soft labels. LabelOn - // The LabelOff [label_off, rmln] string capability is the turn off soft labels. LabelOff - // The KeyBeg [key_beg, kbeg] string capability is the begin key. KeyBeg - // The KeyCancel [key_cancel, kcan] string capability is the cancel key. KeyCancel - // The KeyClose [key_close, kclo] string capability is the close key. KeyClose - // The KeyCommand [key_command, kcmd] string capability is the command key. KeyCommand - // The KeyCopy [key_copy, kcpy] string capability is the copy key. KeyCopy - // The KeyCreate [key_create, kcrt] string capability is the create key. KeyCreate - // The KeyEnd [key_end, kend] string capability is the end key. KeyEnd - // The KeyEnter [key_enter, kent] string capability is the enter/send key. KeyEnter - // The KeyExit [key_exit, kext] string capability is the exit key. KeyExit - // The KeyFind [key_find, kfnd] string capability is the find key. KeyFind - // The KeyHelp [key_help, khlp] string capability is the help key. KeyHelp - // The KeyMark [key_mark, kmrk] string capability is the mark key. KeyMark - // The KeyMessage [key_message, kmsg] string capability is the message key. KeyMessage - // The KeyMove [key_move, kmov] string capability is the move key. KeyMove - // The KeyNext [key_next, knxt] string capability is the next key. KeyNext - // The KeyOpen [key_open, kopn] string capability is the open key. KeyOpen - // The KeyOptions [key_options, kopt] string capability is the options key. KeyOptions - // The KeyPrevious [key_previous, kprv] string capability is the previous key. KeyPrevious - // The KeyPrint [key_print, kprt] string capability is the print key. KeyPrint - // The KeyRedo [key_redo, krdo] string capability is the redo key. KeyRedo - // The KeyReference [key_reference, kref] string capability is the reference key. KeyReference - // The KeyRefresh [key_refresh, krfr] string capability is the refresh key. KeyRefresh - // The KeyReplace [key_replace, krpl] string capability is the replace key. KeyReplace - // The KeyRestart [key_restart, krst] string capability is the restart key. KeyRestart - // The KeyResume [key_resume, kres] string capability is the resume key. KeyResume - // The KeySave [key_save, ksav] string capability is the save key. KeySave - // The KeySuspend [key_suspend, kspd] string capability is the suspend key. KeySuspend - // The KeyUndo [key_undo, kund] string capability is the undo key. KeyUndo - // The KeySbeg [key_sbeg, kBEG] string capability is the shifted begin key. KeySbeg - // The KeyScancel [key_scancel, kCAN] string capability is the shifted cancel key. KeyScancel - // The KeyScommand [key_scommand, kCMD] string capability is the shifted command key. KeyScommand - // The KeyScopy [key_scopy, kCPY] string capability is the shifted copy key. KeyScopy - // The KeyScreate [key_screate, kCRT] string capability is the shifted create key. KeyScreate - // The KeySdc [key_sdc, kDC] string capability is the shifted delete-character key. KeySdc - // The KeySdl [key_sdl, kDL] string capability is the shifted delete-line key. KeySdl - // The KeySelect [key_select, kslt] string capability is the select key. KeySelect - // The KeySend [key_send, kEND] string capability is the shifted end key. KeySend - // The KeySeol [key_seol, kEOL] string capability is the shifted clear-to-end-of-line key. KeySeol - // The KeySexit [key_sexit, kEXT] string capability is the shifted exit key. KeySexit - // The KeySfind [key_sfind, kFND] string capability is the shifted find key. KeySfind - // The KeyShelp [key_shelp, kHLP] string capability is the shifted help key. KeyShelp - // The KeyShome [key_shome, kHOM] string capability is the shifted home key. KeyShome - // The KeySic [key_sic, kIC] string capability is the shifted insert-character key. KeySic - // The KeySleft [key_sleft, kLFT] string capability is the shifted left-arrow key. KeySleft - // The KeySmessage [key_smessage, kMSG] string capability is the shifted message key. KeySmessage - // The KeySmove [key_smove, kMOV] string capability is the shifted move key. KeySmove - // The KeySnext [key_snext, kNXT] string capability is the shifted next key. KeySnext - // The KeySoptions [key_soptions, kOPT] string capability is the shifted options key. KeySoptions - // The KeySprevious [key_sprevious, kPRV] string capability is the shifted previous key. KeySprevious - // The KeySprint [key_sprint, kPRT] string capability is the shifted print key. KeySprint - // The KeySredo [key_sredo, kRDO] string capability is the shifted redo key. KeySredo - // The KeySreplace [key_sreplace, kRPL] string capability is the shifted replace key. KeySreplace - // The KeySright [key_sright, kRIT] string capability is the shifted right-arrow key. KeySright - // The KeySrsume [key_srsume, kRES] string capability is the shifted resume key. KeySrsume - // The KeySsave [key_ssave, kSAV] string capability is the shifted save key. KeySsave - // The KeySsuspend [key_ssuspend, kSPD] string capability is the shifted suspend key. KeySsuspend - // The KeySundo [key_sundo, kUND] string capability is the shifted undo key. KeySundo - // The ReqForInput [req_for_input, rfi] string capability is the send next input char (for ptys). ReqForInput - // The KeyF11 [key_f11, kf11] string capability is the F11 function key. KeyF11 - // The KeyF12 [key_f12, kf12] string capability is the F12 function key. KeyF12 - // The KeyF13 [key_f13, kf13] string capability is the F13 function key. KeyF13 - // The KeyF14 [key_f14, kf14] string capability is the F14 function key. KeyF14 - // The KeyF15 [key_f15, kf15] string capability is the F15 function key. KeyF15 - // The KeyF16 [key_f16, kf16] string capability is the F16 function key. KeyF16 - // The KeyF17 [key_f17, kf17] string capability is the F17 function key. KeyF17 - // The KeyF18 [key_f18, kf18] string capability is the F18 function key. KeyF18 - // The KeyF19 [key_f19, kf19] string capability is the F19 function key. KeyF19 - // The KeyF20 [key_f20, kf20] string capability is the F20 function key. KeyF20 - // The KeyF21 [key_f21, kf21] string capability is the F21 function key. KeyF21 - // The KeyF22 [key_f22, kf22] string capability is the F22 function key. KeyF22 - // The KeyF23 [key_f23, kf23] string capability is the F23 function key. KeyF23 - // The KeyF24 [key_f24, kf24] string capability is the F24 function key. KeyF24 - // The KeyF25 [key_f25, kf25] string capability is the F25 function key. KeyF25 - // The KeyF26 [key_f26, kf26] string capability is the F26 function key. KeyF26 - // The KeyF27 [key_f27, kf27] string capability is the F27 function key. KeyF27 - // The KeyF28 [key_f28, kf28] string capability is the F28 function key. KeyF28 - // The KeyF29 [key_f29, kf29] string capability is the F29 function key. KeyF29 - // The KeyF30 [key_f30, kf30] string capability is the F30 function key. KeyF30 - // The KeyF31 [key_f31, kf31] string capability is the F31 function key. KeyF31 - // The KeyF32 [key_f32, kf32] string capability is the F32 function key. KeyF32 - // The KeyF33 [key_f33, kf33] string capability is the F33 function key. KeyF33 - // The KeyF34 [key_f34, kf34] string capability is the F34 function key. KeyF34 - // The KeyF35 [key_f35, kf35] string capability is the F35 function key. KeyF35 - // The KeyF36 [key_f36, kf36] string capability is the F36 function key. KeyF36 - // The KeyF37 [key_f37, kf37] string capability is the F37 function key. KeyF37 - // The KeyF38 [key_f38, kf38] string capability is the F38 function key. KeyF38 - // The KeyF39 [key_f39, kf39] string capability is the F39 function key. KeyF39 - // The KeyF40 [key_f40, kf40] string capability is the F40 function key. KeyF40 - // The KeyF41 [key_f41, kf41] string capability is the F41 function key. KeyF41 - // The KeyF42 [key_f42, kf42] string capability is the F42 function key. KeyF42 - // The KeyF43 [key_f43, kf43] string capability is the F43 function key. KeyF43 - // The KeyF44 [key_f44, kf44] string capability is the F44 function key. KeyF44 - // The KeyF45 [key_f45, kf45] string capability is the F45 function key. KeyF45 - // The KeyF46 [key_f46, kf46] string capability is the F46 function key. KeyF46 - // The KeyF47 [key_f47, kf47] string capability is the F47 function key. KeyF47 - // The KeyF48 [key_f48, kf48] string capability is the F48 function key. KeyF48 - // The KeyF49 [key_f49, kf49] string capability is the F49 function key. KeyF49 - // The KeyF50 [key_f50, kf50] string capability is the F50 function key. KeyF50 - // The KeyF51 [key_f51, kf51] string capability is the F51 function key. KeyF51 - // The KeyF52 [key_f52, kf52] string capability is the F52 function key. KeyF52 - // The KeyF53 [key_f53, kf53] string capability is the F53 function key. KeyF53 - // The KeyF54 [key_f54, kf54] string capability is the F54 function key. KeyF54 - // The KeyF55 [key_f55, kf55] string capability is the F55 function key. KeyF55 - // The KeyF56 [key_f56, kf56] string capability is the F56 function key. KeyF56 - // The KeyF57 [key_f57, kf57] string capability is the F57 function key. KeyF57 - // The KeyF58 [key_f58, kf58] string capability is the F58 function key. KeyF58 - // The KeyF59 [key_f59, kf59] string capability is the F59 function key. KeyF59 - // The KeyF60 [key_f60, kf60] string capability is the F60 function key. KeyF60 - // The KeyF61 [key_f61, kf61] string capability is the F61 function key. KeyF61 - // The KeyF62 [key_f62, kf62] string capability is the F62 function key. KeyF62 - // The KeyF63 [key_f63, kf63] string capability is the F63 function key. KeyF63 - // The ClrBol [clr_bol, el1] string capability is the Clear to beginning of line. ClrBol - // The ClearMargins [clear_margins, mgc] string capability is the clear right and left soft margins. ClearMargins - - // The SetLeftMargin [set_left_margin, smgl] string capability is the set left soft margin at current column. See smgl. (ML is not in BSD termcap). + // The SetLeftMargin [set_left_margin, smgl] string capability is the set left soft margin at current column. (ML is not in BSD termcap). SetLeftMargin - // The SetRightMargin [set_right_margin, smgr] string capability is the set right soft margin at current column. SetRightMargin - // The LabelFormat [label_format, fln] string capability is the label format. LabelFormat - // The SetClock [set_clock, sclk] string capability is the set clock, #1 hrs #2 mins #3 secs. SetClock - // The DisplayClock [display_clock, dclk] string capability is the display clock. DisplayClock - // The RemoveClock [remove_clock, rmclk] string capability is the remove clock. RemoveClock - // The CreateWindow [create_window, cwin] string capability is the define a window #1 from #2,#3 to #4,#5. CreateWindow - // The GotoWindow [goto_window, wingo] string capability is the go to window #1. GotoWindow - // The Hangup [hangup, hup] string capability is the hang-up phone. Hangup - // The DialPhone [dial_phone, dial] string capability is the dial number #1. DialPhone - // The QuickDial [quick_dial, qdial] string capability is the dial number #1 without checking. QuickDial - // The Tone [tone, tone] string capability is the select touch tone dialing. Tone - // The Pulse [pulse, pulse] string capability is the select pulse dialing. Pulse - // The FlashHook [flash_hook, hook] string capability is the flash switch hook. FlashHook - // The FixedPause [fixed_pause, pause] string capability is the pause for 2-3 seconds. FixedPause - // The WaitTone [wait_tone, wait] string capability is the wait for dial-tone. WaitTone - // The User0 [user0, u0] string capability is the User string #0. User0 - // The User1 [user1, u1] string capability is the User string #1. User1 - // The User2 [user2, u2] string capability is the User string #2. User2 - // The User3 [user3, u3] string capability is the User string #3. User3 - // The User4 [user4, u4] string capability is the User string #4. User4 - // The User5 [user5, u5] string capability is the User string #5. User5 - // The User6 [user6, u6] string capability is the User string #6. User6 - // The User7 [user7, u7] string capability is the User string #7. User7 - // The User8 [user8, u8] string capability is the User string #8. User8 - // The User9 [user9, u9] string capability is the User string #9. User9 - // The OrigPair [orig_pair, op] string capability is the Set default pair to its original value. OrigPair - // The OrigColors [orig_colors, oc] string capability is the Set all color pairs to the original ones. OrigColors - // The InitializeColor [initialize_color, initc] string capability is the initialize color #1 to (#2,#3,#4). InitializeColor - // The InitializePair [initialize_pair, initp] string capability is the Initialize color pair #1 to fg=(#2,#3,#4), bg=(#5,#6,#7). InitializePair - // The SetColorPair [set_color_pair, scp] string capability is the Set current color pair to #1. SetColorPair - // The SetForeground [set_foreground, setf] string capability is the Set foreground color #1. SetForeground - // The SetBackground [set_background, setb] string capability is the Set background color #1. SetBackground - // The ChangeCharPitch [change_char_pitch, cpi] string capability is the Change number of characters per inch to #1. ChangeCharPitch - // The ChangeLinePitch [change_line_pitch, lpi] string capability is the Change number of lines per inch to #1. ChangeLinePitch - // The ChangeResHorz [change_res_horz, chr] string capability is the Change horizontal resolution to #1. ChangeResHorz - // The ChangeResVert [change_res_vert, cvr] string capability is the Change vertical resolution to #1. ChangeResVert - // The DefineChar [define_char, defc] string capability is the Define a character #1, #2 dots wide, descender #3. DefineChar - // The EnterDoublewideMode [enter_doublewide_mode, swidm] string capability is the Enter double-wide mode. EnterDoublewideMode - // The EnterDraftQuality [enter_draft_quality, sdrfq] string capability is the Enter draft-quality mode. EnterDraftQuality - // The EnterItalicsMode [enter_italics_mode, sitm] string capability is the Enter italic mode. EnterItalicsMode - // The EnterLeftwardMode [enter_leftward_mode, slm] string capability is the Start leftward carriage motion. EnterLeftwardMode - // The EnterMicroMode [enter_micro_mode, smicm] string capability is the Start micro-motion mode. EnterMicroMode - // The EnterNearLetterQuality [enter_near_letter_quality, snlq] string capability is the Enter NLQ mode. EnterNearLetterQuality - // The EnterNormalQuality [enter_normal_quality, snrmq] string capability is the Enter normal-quality mode. EnterNormalQuality - // The EnterShadowMode [enter_shadow_mode, sshm] string capability is the Enter shadow-print mode. EnterShadowMode - // The EnterSubscriptMode [enter_subscript_mode, ssubm] string capability is the Enter subscript mode. EnterSubscriptMode - // The EnterSuperscriptMode [enter_superscript_mode, ssupm] string capability is the Enter superscript mode. EnterSuperscriptMode - // The EnterUpwardMode [enter_upward_mode, sum] string capability is the Start upward carriage motion. EnterUpwardMode - // The ExitDoublewideMode [exit_doublewide_mode, rwidm] string capability is the End double-wide mode. ExitDoublewideMode - // The ExitItalicsMode [exit_italics_mode, ritm] string capability is the End italic mode. ExitItalicsMode - // The ExitLeftwardMode [exit_leftward_mode, rlm] string capability is the End left-motion mode. ExitLeftwardMode - // The ExitMicroMode [exit_micro_mode, rmicm] string capability is the End micro-motion mode. ExitMicroMode - // The ExitShadowMode [exit_shadow_mode, rshm] string capability is the End shadow-print mode. ExitShadowMode - // The ExitSubscriptMode [exit_subscript_mode, rsubm] string capability is the End subscript mode. ExitSubscriptMode - // The ExitSuperscriptMode [exit_superscript_mode, rsupm] string capability is the End superscript mode. ExitSuperscriptMode - // The ExitUpwardMode [exit_upward_mode, rum] string capability is the End reverse character motion. ExitUpwardMode - // The MicroColumnAddress [micro_column_address, mhpa] string capability is the Like column_address in micro mode. MicroColumnAddress - // The MicroDown [micro_down, mcud1] string capability is the Like cursor_down in micro mode. MicroDown - // The MicroLeft [micro_left, mcub1] string capability is the Like cursor_left in micro mode. MicroLeft - // The MicroRight [micro_right, mcuf1] string capability is the Like cursor_right in micro mode. MicroRight - // The MicroRowAddress [micro_row_address, mvpa] string capability is the Like row_address #1 in micro mode. MicroRowAddress - // The MicroUp [micro_up, mcuu1] string capability is the Like cursor_up in micro mode. MicroUp - // The OrderOfPins [order_of_pins, porder] string capability is the Match software bits to print-head pins. OrderOfPins - // The ParmDownMicro [parm_down_micro, mcud] string capability is the Like parm_down_cursor in micro mode. ParmDownMicro - // The ParmLeftMicro [parm_left_micro, mcub] string capability is the Like parm_left_cursor in micro mode. ParmLeftMicro - // The ParmRightMicro [parm_right_micro, mcuf] string capability is the Like parm_right_cursor in micro mode. ParmRightMicro - // The ParmUpMicro [parm_up_micro, mcuu] string capability is the Like parm_up_cursor in micro mode. ParmUpMicro - // The SelectCharSet [select_char_set, scs] string capability is the Select character set, #1. SelectCharSet - // The SetBottomMargin [set_bottom_margin, smgb] string capability is the Set bottom margin at current line. SetBottomMargin - // The SetBottomMarginParm [set_bottom_margin_parm, smgbp] string capability is the Set bottom margin at line #1 or (if smgtp is not given) #2 lines from bottom. SetBottomMarginParm - // The SetLeftMarginParm [set_left_margin_parm, smglp] string capability is the Set left (right) margin at column #1. SetLeftMarginParm - // The SetRightMarginParm [set_right_margin_parm, smgrp] string capability is the Set right margin at column #1. SetRightMarginParm - // The SetTopMargin [set_top_margin, smgt] string capability is the Set top margin at current line. SetTopMargin - // The SetTopMarginParm [set_top_margin_parm, smgtp] string capability is the Set top (bottom) margin at row #1. SetTopMarginParm - // The StartBitImage [start_bit_image, sbim] string capability is the Start printing bit image graphics. StartBitImage - // The StartCharSetDef [start_char_set_def, scsd] string capability is the Start character set definition #1, with #2 characters in the set. StartCharSetDef - // The StopBitImage [stop_bit_image, rbim] string capability is the Stop printing bit image graphics. StopBitImage - // The StopCharSetDef [stop_char_set_def, rcsd] string capability is the End definition of character set #1. StopCharSetDef - // The SubscriptCharacters [subscript_characters, subcs] string capability is the List of subscriptable characters. SubscriptCharacters - // The SuperscriptCharacters [superscript_characters, supcs] string capability is the List of superscriptable characters. SuperscriptCharacters - // The TheseCauseCr [these_cause_cr, docr] string capability is the Printing any of these characters causes CR. TheseCauseCr - // The ZeroMotion [zero_motion, zerom] string capability is the No motion for subsequent character. ZeroMotion - // The CharSetNames [char_set_names, csnm] string capability is the Produce #1'th item from list of character set names. CharSetNames - // The KeyMouse [key_mouse, kmous] string capability is the Mouse event has occurred. KeyMouse - // The MouseInfo [mouse_info, minfo] string capability is the Mouse status information. MouseInfo - // The ReqMousePos [req_mouse_pos, reqmp] string capability is the Request mouse position. ReqMousePos - // The GetMouse [get_mouse, getm] string capability is the Curses should get button events, parameter #1 not documented. GetMouse - // The SetAForeground [set_a_foreground, setaf] string capability is the Set foreground color to #1, using ANSI escape. SetAForeground - // The SetABackground [set_a_background, setab] string capability is the Set background color to #1, using ANSI escape. SetABackground - // The PkeyPlab [pkey_plab, pfxl] string capability is the Program function key #1 to type string #2 and show string #3. PkeyPlab - // The DeviceType [device_type, devt] string capability is the Indicate language/codeset support. DeviceType - // The CodeSetInit [code_set_init, csin] string capability is the Init sequence for multiple codesets. CodeSetInit - // The Set0DesSeq [set0_des_seq, s0ds] string capability is the Shift to codeset 0 (EUC set 0, ASCII). Set0DesSeq - // The Set1DesSeq [set1_des_seq, s1ds] string capability is the Shift to codeset 1. Set1DesSeq - // The Set2DesSeq [set2_des_seq, s2ds] string capability is the Shift to codeset 2. Set2DesSeq - // The Set3DesSeq [set3_des_seq, s3ds] string capability is the Shift to codeset 3. Set3DesSeq - // The SetLrMargin [set_lr_margin, smglr] string capability is the Set both left and right margins to #1, #2. (ML is not in BSD termcap). SetLrMargin - // The SetTbMargin [set_tb_margin, smgtb] string capability is the Sets both top and bottom margins to #1, #2. SetTbMargin - // The BitImageRepeat [bit_image_repeat, birep] string capability is the Repeat bit image cell #1 #2 times. BitImageRepeat - // The BitImageNewline [bit_image_newline, binel] string capability is the Move to next row of the bit image. BitImageNewline - // The BitImageCarriageReturn [bit_image_carriage_return, bicr] string capability is the Move to beginning of same row. BitImageCarriageReturn - // The ColorNames [color_names, colornm] string capability is the Give name for color #1. ColorNames - // The DefineBitImageRegion [define_bit_image_region, defbi] string capability is the Define rectangular bit image region. DefineBitImageRegion - // The EndBitImageRegion [end_bit_image_region, endbi] string capability is the End a bit-image region. EndBitImageRegion - // The SetColorBand [set_color_band, setcolor] string capability is the Change to ribbon color #1. SetColorBand - // The SetPageLength [set_page_length, slines] string capability is the Set page length to #1 lines. SetPageLength - // The DisplayPcChar [display_pc_char, dispc] string capability is the Display PC character #1. DisplayPcChar - // The EnterPcCharsetMode [enter_pc_charset_mode, smpch] string capability is the Enter PC character display mode. EnterPcCharsetMode - // The ExitPcCharsetMode [exit_pc_charset_mode, rmpch] string capability is the Exit PC character display mode. ExitPcCharsetMode - // The EnterScancodeMode [enter_scancode_mode, smsc] string capability is the Enter PC scancode mode. EnterScancodeMode - // The ExitScancodeMode [exit_scancode_mode, rmsc] string capability is the Exit PC scancode mode. ExitScancodeMode - // The PcTermOptions [pc_term_options, pctrm] string capability is the PC terminal options. PcTermOptions - // The ScancodeEscape [scancode_escape, scesc] string capability is the Escape for scancode emulation. ScancodeEscape - // The AltScancodeEsc [alt_scancode_esc, scesa] string capability is the Alternate escape for scancode emulation. AltScancodeEsc - // The EnterHorizontalHlMode [enter_horizontal_hl_mode, ehhlm] string capability is the Enter horizontal highlight mode. EnterHorizontalHlMode - // The EnterLeftHlMode [enter_left_hl_mode, elhlm] string capability is the Enter left highlight mode. EnterLeftHlMode - // The EnterLowHlMode [enter_low_hl_mode, elohlm] string capability is the Enter low highlight mode. EnterLowHlMode - // The EnterRightHlMode [enter_right_hl_mode, erhlm] string capability is the Enter right highlight mode. EnterRightHlMode - // The EnterTopHlMode [enter_top_hl_mode, ethlm] string capability is the Enter top highlight mode. EnterTopHlMode - // The EnterVerticalHlMode [enter_vertical_hl_mode, evhlm] string capability is the Enter vertical highlight mode. EnterVerticalHlMode - // The SetAAttributes [set_a_attributes, sgr1] string capability is the Define second set of video attributes #1-#6. SetAAttributes - // The SetPglenInch [set_pglen_inch, slength] string capability is the Set page length to #1 hundredth of an inch (some implementations use sL for termcap). SetPglenInch - // The TermcapInit2 [termcap_init2, OTi2] string capability is the secondary initialization string. TermcapInit2 - // The TermcapReset [termcap_reset, OTrs] string capability is the terminal reset string. TermcapReset - // The LinefeedIfNotLf [linefeed_if_not_lf, OTnl] string capability is the use to move down. LinefeedIfNotLf - // The BackspaceIfNotBs [backspace_if_not_bs, OTbc] string capability is the move left, if not ^H. BackspaceIfNotBs - // The OtherNonFunctionKeys [other_non_function_keys, OTko] string capability is the list of self-mapped keycaps. OtherNonFunctionKeys - // The ArrowKeyMap [arrow_key_map, OTma] string capability is the map motion-keys for vi version 2. ArrowKeyMap - // The AcsUlcorner [acs_ulcorner, OTG2] string capability is the single upper left. AcsUlcorner - // The AcsLlcorner [acs_llcorner, OTG3] string capability is the single lower left. AcsLlcorner - // The AcsUrcorner [acs_urcorner, OTG1] string capability is the single upper right. AcsUrcorner - // The AcsLrcorner [acs_lrcorner, OTG4] string capability is the single lower right. AcsLrcorner - // The AcsLtee [acs_ltee, OTGR] string capability is the tee pointing right. AcsLtee - // The AcsRtee [acs_rtee, OTGL] string capability is the tee pointing left. AcsRtee - // The AcsBtee [acs_btee, OTGU] string capability is the tee pointing up. AcsBtee - // The AcsTtee [acs_ttee, OTGD] string capability is the tee pointing down. AcsTtee - // The AcsHline [acs_hline, OTGH] string capability is the single horizontal line. AcsHline - // The AcsVline [acs_vline, OTGV] string capability is the single vertical line. AcsVline - // The AcsPlus [acs_plus, OTGC] string capability is the single intersection. AcsPlus - // The MemoryLock [memory_lock, meml] string capability is the lock memory above cursor. MemoryLock - // The MemoryUnlock [memory_unlock, memu] string capability is the unlock memory. MemoryUnlock - // The BoxChars1 [box_chars_1, box1] string capability is the box characters primary set. BoxChars1 ) - const ( // CapCountBool is the count of bool capabilities. CapCountBool = ReturnDoesClrEol + 1 - // CapCountNum is the count of num capabilities. CapCountNum = NumberOfFunctionKeys + 1 - // CapCountString is the count of string capabilities. CapCountString = BoxChars1 + 1 ) diff --git a/vendor/github.com/xo/terminfo/color.go b/vendor/github.com/xo/terminfo/color.go index 453c29c24..76c439fc9 100644 --- a/vendor/github.com/xo/terminfo/color.go +++ b/vendor/github.com/xo/terminfo/color.go @@ -70,14 +70,12 @@ func ColorLevelFromEnv() (ColorLevel, error) { } return ColorLevelHundreds, nil } - // otherwise determine from TERM's max_colors capability if term := os.Getenv("TERM"); term != "" { ti, err := Load(term) if err != nil { return ColorLevelNone, err } - v, ok := ti.Nums[MaxColors] switch { case !ok || v <= 16: @@ -86,6 +84,5 @@ func ColorLevelFromEnv() (ColorLevel, error) { return ColorLevelHundreds, nil } } - return ColorLevelBasic, nil } diff --git a/vendor/github.com/xo/terminfo/util.go b/vendor/github.com/xo/terminfo/dec.go similarity index 92% rename from vendor/github.com/xo/terminfo/util.go rename to vendor/github.com/xo/terminfo/dec.go index 56f47e811..dacc88e38 100644 --- a/vendor/github.com/xo/terminfo/util.go +++ b/vendor/github.com/xo/terminfo/dec.go @@ -7,12 +7,11 @@ import ( const ( // maxFileLength is the max file length. maxFileLength = 4096 - // magic is the file magic for terminfo files. - magic = 0432 - - // magicExtended is the file magic for terminfo files with the extended number format. - magicExtended = 01036 + magic = 0o432 + // magicExtended is the file magic for terminfo files with the extended + // number format. + magicExtended = 0o1036 ) // header fields. @@ -99,12 +98,12 @@ func readStrings(idx []int, buf []byte, n int) (map[int][]byte, int, error) { type decoder struct { buf []byte pos int - len int + n int } // readBytes reads the next n bytes of buf, incrementing pos by n. func (d *decoder) readBytes(n int) ([]byte, error) { - if d.len < d.pos+n { + if d.n < d.pos+n { return nil, ErrUnexpectedFileEnd } n, d.pos = d.pos, d.pos+n @@ -115,15 +114,12 @@ func (d *decoder) readBytes(n int) ([]byte, error) { func (d *decoder) readInts(n, w int) ([]int, error) { w /= 8 l := n * w - buf, err := d.readBytes(l) if err != nil { return nil, err } - // align d.pos += d.pos % 2 - z := make([]int, n) for i, j := 0, 0; i < l; i, j = i+w, j+1 { switch w { @@ -135,7 +131,6 @@ func (d *decoder) readInts(n, w int) ([]int, error) { z[j] = int(buf[i+3])<<24 | int(buf[i+2])<<16 | int(buf[i+1])<<8 | int(buf[i]) } } - return z, nil } @@ -145,7 +140,6 @@ func (d *decoder) readBools(n int) (map[int]bool, map[int]bool, error) { if err != nil { return nil, nil, err } - // process bools, boolsM := make(map[int]bool), make(map[int]bool) for i, b := range buf { @@ -154,7 +148,6 @@ func (d *decoder) readBools(n int) (map[int]bool, map[int]bool, error) { boolsM[i] = true } } - return bools, boolsM, nil } @@ -164,7 +157,6 @@ func (d *decoder) readNums(n, w int) (map[int]int, map[int]bool, error) { if err != nil { return nil, nil, err } - // process nums, numsM := make(map[int]int), make(map[int]bool) for i := 0; i < n; i++ { @@ -173,7 +165,6 @@ func (d *decoder) readNums(n, w int) (map[int]int, map[int]bool, error) { numsM[i] = true } } - return nums, numsM, nil } @@ -184,16 +175,13 @@ func (d *decoder) readStringTable(n, sz int) ([][]byte, []int, error) { if err != nil { return nil, nil, err } - // read string data table data, err := d.readBytes(sz) if err != nil { return nil, nil, err } - // align d.pos += d.pos % 2 - // process s := make([][]byte, n) var m []int @@ -209,7 +197,6 @@ func (d *decoder) readStringTable(n, sz int) ([][]byte, []int, error) { } } } - return s, m, nil } @@ -220,7 +207,6 @@ func (d *decoder) readStrings(n, sz int) (map[int][]byte, map[int]bool, error) { if err != nil { return nil, nil, err } - strs := make(map[int][]byte) for k, v := range s { if k == AcsChars { @@ -228,39 +214,32 @@ func (d *decoder) readStrings(n, sz int) (map[int][]byte, map[int]bool, error) { } strs[k] = v } - strsM := make(map[int]bool, len(m)) for _, k := range m { strsM[k] = true } - return strs, strsM, nil } // canonicalizeAscChars reorders chars to be unique, in order. // -// see repair_ascc in ncurses-6.0/progs/dump_entry.c +// see repair_ascc in ncurses-6.3/progs/dump_entry.c func canonicalizeAscChars(z []byte) []byte { - var c chars + var c []byte enc := make(map[byte]byte, len(z)/2) for i := 0; i < len(z); i += 2 { if _, ok := enc[z[i]]; !ok { a, b := z[i], z[i+1] - //log.Printf(">>> a: %d %c, b: %d %c", a, a, b, b) + // log.Printf(">>> a: %d %c, b: %d %c", a, a, b, b) c, enc[a] = append(c, b), b } } - sort.Sort(c) - + sort.Slice(c, func(i, j int) bool { + return c[i] < c[j] + }) r := make([]byte, 2*len(c)) for i := 0; i < len(c); i++ { r[i*2], r[i*2+1] = c[i], enc[c[i]] } return r } - -type chars []byte - -func (c chars) Len() int { return len(c) } -func (c chars) Swap(i, j int) { c[i], c[j] = c[j], c[i] } -func (c chars) Less(i, j int) bool { return c[i] < c[j] } diff --git a/vendor/github.com/xo/terminfo/load.go b/vendor/github.com/xo/terminfo/load.go index 9b0b94286..d7cd266cb 100644 --- a/vendor/github.com/xo/terminfo/load.go +++ b/vendor/github.com/xo/terminfo/load.go @@ -23,34 +23,27 @@ func Load(name string) (*Terminfo, error) { if name == "" { return nil, ErrEmptyTermName } - termCache.RLock() ti, ok := termCache.db[name] termCache.RUnlock() - if ok { return ti, nil } - var checkDirs []string - // check $TERMINFO if dir := os.Getenv("TERMINFO"); dir != "" { checkDirs = append(checkDirs, dir) } - // check $HOME/.terminfo u, err := user.Current() if err != nil { return nil, err } checkDirs = append(checkDirs, path.Join(u.HomeDir, ".terminfo")) - // check $TERMINFO_DIRS if dirs := os.Getenv("TERMINFO_DIRS"); dirs != "" { checkDirs = append(checkDirs, strings.Split(dirs, ":")...) } - // check fallback directories checkDirs = append(checkDirs, "/etc/terminfo", "/lib/terminfo", "/usr/share/terminfo") for _, dir := range checkDirs { @@ -61,7 +54,6 @@ func Load(name string) (*Terminfo, error) { return ti, nil } } - return nil, ErrDatabaseDirectoryNotFound } diff --git a/vendor/github.com/xo/terminfo/param.go b/vendor/github.com/xo/terminfo/param.go index e6b8a1bc0..ed4cb86b6 100644 --- a/vendor/github.com/xo/terminfo/param.go +++ b/vendor/github.com/xo/terminfo/param.go @@ -13,25 +13,18 @@ import ( type parametizer struct { // z is the string to parameterize z []byte - // pos is the current position in s. pos int - // nest is the current nest level. nest int - // s is the variable stack. s stack - // skipElse keeps the state of skipping else. skipElse bool - // buf is the result buffer. buf *bytes.Buffer - // params are the parameters to interpolate. params [9]interface{} - // vars are dynamic variables. vars [26]interface{} } @@ -54,19 +47,15 @@ var parametizerPool = sync.Pool{ func newParametizer(z []byte) *parametizer { p := parametizerPool.Get().(*parametizer) p.z = z - return p } // reset resets the parametizer. func (p *parametizer) reset() { p.pos, p.nest = 0, 0 - p.s.reset() p.buf.Reset() - p.params, p.vars = [9]interface{}{}, [26]interface{}{} - parametizerPool.Put(p) } @@ -106,13 +95,11 @@ func (p *parametizer) scanTextFn() stateFn { p.writeFrom(ppos) return nil } - if ch == '%' { p.writeFrom(ppos) p.pos++ return p.scanCodeFn } - p.pos++ } } @@ -122,11 +109,9 @@ func (p *parametizer) scanCodeFn() stateFn { if err != nil { return nil } - switch ch { case '%': p.buf.WriteByte('%') - case ':': // this character is used to avoid interpreting "%-" and "%+" as operators. // the next character is where the format really begins. @@ -136,71 +121,52 @@ func (p *parametizer) scanCodeFn() stateFn { return nil } return p.scanFormatFn - case '#', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.': return p.scanFormatFn - case 'o': p.buf.WriteString(strconv.FormatInt(int64(p.s.popInt()), 8)) - case 'd': p.buf.WriteString(strconv.Itoa(p.s.popInt())) - case 'x': p.buf.WriteString(strconv.FormatInt(int64(p.s.popInt()), 16)) - case 'X': p.buf.WriteString(strings.ToUpper(strconv.FormatInt(int64(p.s.popInt()), 16))) - case 's': p.buf.WriteString(p.s.popString()) - case 'c': p.buf.WriteByte(p.s.popByte()) - case 'p': p.pos++ return p.pushParamFn - case 'P': p.pos++ return p.setDsVarFn - case 'g': p.pos++ return p.getDsVarFn - case '\'': p.pos++ ch, err = p.peek() if err != nil { return nil } - p.s.push(ch) - // skip the '\'' p.pos++ - case '{': p.pos++ return p.pushIntfn - case 'l': p.s.push(len(p.s.popString())) - case '+': bi, ai := p.s.popInt(), p.s.popInt() p.s.push(ai + bi) - case '-': bi, ai := p.s.popInt(), p.s.popInt() p.s.push(ai - bi) - case '*': bi, ai := p.s.popInt(), p.s.popInt() p.s.push(ai * bi) - case '/': bi, ai := p.s.popInt(), p.s.popInt() if bi != 0 { @@ -208,7 +174,6 @@ func (p *parametizer) scanCodeFn() stateFn { } else { p.s.push(0) } - case 'm': bi, ai := p.s.popInt(), p.s.popInt() if bi != 0 { @@ -216,101 +181,77 @@ func (p *parametizer) scanCodeFn() stateFn { } else { p.s.push(0) } - case '&': bi, ai := p.s.popInt(), p.s.popInt() p.s.push(ai & bi) - case '|': bi, ai := p.s.popInt(), p.s.popInt() p.s.push(ai | bi) - case '^': bi, ai := p.s.popInt(), p.s.popInt() p.s.push(ai ^ bi) - case '=': bi, ai := p.s.popInt(), p.s.popInt() p.s.push(ai == bi) - case '>': bi, ai := p.s.popInt(), p.s.popInt() p.s.push(ai > bi) - case '<': bi, ai := p.s.popInt(), p.s.popInt() p.s.push(ai < bi) - case 'A': bi, ai := p.s.popBool(), p.s.popBool() p.s.push(ai && bi) - case 'O': bi, ai := p.s.popBool(), p.s.popBool() p.s.push(ai || bi) - case '!': p.s.push(!p.s.popBool()) - case '~': p.s.push(^p.s.popInt()) - case 'i': for i := range p.params[:2] { if n, ok := p.params[i].(int); ok { p.params[i] = n + 1 } } - case '?', ';': - case 't': return p.scanThenFn - case 'e': p.skipElse = true return p.skipTextFn } - p.pos++ - return p.scanTextFn } func (p *parametizer) scanFormatFn() stateFn { // the character was already read, so no need to check the error. ch, _ := p.peek() - // 6 should be the maximum length of a format string, for example "%:-9.9d". f := []byte{'%', ch, 0, 0, 0, 0} - var err error - for { p.pos++ ch, err = p.peek() if err != nil { return nil } - f = append(f, ch) switch ch { case 'o', 'd', 'x', 'X': fmt.Fprintf(p.buf, string(f), p.s.popInt()) break - case 's': fmt.Fprintf(p.buf, string(f), p.s.popString()) break - case 'c': fmt.Fprintf(p.buf, string(f), p.s.popByte()) break } } - p.pos++ - return p.scanTextFn } @@ -319,16 +260,13 @@ func (p *parametizer) pushParamFn() stateFn { if err != nil { return nil } - if ai := int(ch - '1'); ai >= 0 && ai < len(p.params) { p.s.push(p.params[ai]) } else { p.s.push(0) } - // skip the '}' p.pos++ - return p.scanTextFn } @@ -337,7 +275,6 @@ func (p *parametizer) setDsVarFn() stateFn { if err != nil { return nil } - if ch >= 'A' && ch <= 'Z' { staticVars.Lock() staticVars.vars[int(ch-'A')] = p.s.pop() @@ -345,7 +282,6 @@ func (p *parametizer) setDsVarFn() stateFn { } else if ch >= 'a' && ch <= 'z' { p.vars[int(ch-'a')] = p.s.pop() } - p.pos++ return p.scanTextFn } @@ -355,20 +291,16 @@ func (p *parametizer) getDsVarFn() stateFn { if err != nil { return nil } - var a byte if ch >= 'A' && ch <= 'Z' { a = 'A' } else if ch >= 'a' && ch <= 'z' { a = 'a' } - staticVars.Lock() p.s.push(staticVars.vars[int(ch-a)]) staticVars.Unlock() - p.pos++ - return p.scanTextFn } @@ -379,26 +311,21 @@ func (p *parametizer) pushIntfn() stateFn { if err != nil { return nil } - p.pos++ if ch < '0' || ch > '9' { p.s.push(ai) return p.scanTextFn } - ai = (ai * 10) + int(ch-'0') } } func (p *parametizer) scanThenFn() stateFn { p.pos++ - if p.s.popBool() { return p.scanTextFn } - p.skipElse = false - return p.skipTextFn } @@ -408,17 +335,14 @@ func (p *parametizer) skipTextFn() stateFn { if err != nil { return nil } - p.pos++ if ch == '%' { break } } - if p.skipElse { return p.skipElseFn } - return p.skipThenFn } @@ -427,7 +351,6 @@ func (p *parametizer) skipThenFn() stateFn { if err != nil { return nil } - p.pos++ switch ch { case ';': @@ -435,16 +358,13 @@ func (p *parametizer) skipThenFn() stateFn { return p.scanTextFn } p.nest-- - case '?': p.nest++ - case 'e': if p.nest == 0 { return p.scanTextFn } } - return p.skipTextFn } @@ -453,7 +373,6 @@ func (p *parametizer) skipElseFn() stateFn { if err != nil { return nil } - p.pos++ switch ch { case ';': @@ -461,11 +380,9 @@ func (p *parametizer) skipElseFn() stateFn { return p.scanTextFn } p.nest-- - case '?': p.nest++ } - return p.skipTextFn } @@ -473,13 +390,11 @@ func (p *parametizer) skipElseFn() stateFn { func Printf(z []byte, params ...interface{}) string { p := newParametizer(z) defer p.reset() - // make sure we always have 9 parameters -- makes it easier // later to skip checks and its faster for i := 0; i < len(p.params) && i < len(params); i++ { p.params[i] = params[i] } - return p.exec() } diff --git a/vendor/github.com/xo/terminfo/terminfo.go b/vendor/github.com/xo/terminfo/terminfo.go index 8ebbf9599..69e3b6064 100644 --- a/vendor/github.com/xo/terminfo/terminfo.go +++ b/vendor/github.com/xo/terminfo/terminfo.go @@ -1,6 +1,8 @@ // Package terminfo implements reading terminfo files in pure go. package terminfo +//go:generate go run gen.go + import ( "io" "io/ioutil" @@ -20,34 +22,24 @@ func (err Error) Error() string { const ( // ErrInvalidFileSize is the invalid file size error. ErrInvalidFileSize Error = "invalid file size" - // ErrUnexpectedFileEnd is the unexpected file end error. ErrUnexpectedFileEnd Error = "unexpected file end" - // ErrInvalidStringTable is the invalid string table error. ErrInvalidStringTable Error = "invalid string table" - // ErrInvalidMagic is the invalid magic error. ErrInvalidMagic Error = "invalid magic" - // ErrInvalidHeader is the invalid header error. ErrInvalidHeader Error = "invalid header" - // ErrInvalidNames is the invalid names error. ErrInvalidNames Error = "invalid names" - // ErrInvalidExtendedHeader is the invalid extended header error. ErrInvalidExtendedHeader Error = "invalid extended header" - // ErrEmptyTermName is the empty term name error. ErrEmptyTermName Error = "empty term name" - // ErrDatabaseDirectoryNotFound is the database directory not found error. ErrDatabaseDirectoryNotFound Error = "database directory not found" - // ErrFileNotFound is the file not found error. ErrFileNotFound Error = "file not found" - // ErrInvalidTermProgramVersion is the invalid TERM_PROGRAM_VERSION error. ErrInvalidTermProgramVersion Error = "invalid TERM_PROGRAM_VERSION" ) @@ -56,43 +48,30 @@ const ( type Terminfo struct { // File is the original source file. File string - // Names are the provided cap names. Names []string - // Bools are the bool capabilities. Bools map[int]bool - // BoolsM are the missing bool capabilities. BoolsM map[int]bool - // Nums are the num capabilities. Nums map[int]int - // NumsM are the missing num capabilities. NumsM map[int]bool - // Strings are the string capabilities. Strings map[int][]byte - // StringsM are the missing string capabilities. StringsM map[int]bool - // ExtBools are the extended bool capabilities. ExtBools map[int]bool - // ExtBoolsNames is the map of extended bool capabilities to their index. ExtBoolNames map[int][]byte - // ExtNums are the extended num capabilities. ExtNums map[int]int - // ExtNumsNames is the map of extended num capabilities to their index. ExtNumNames map[int][]byte - // ExtStrings are the extended string capabilities. ExtStrings map[int][]byte - // ExtStringsNames is the map of extended string capabilities to their index. ExtStringNames map[int][]byte } @@ -100,75 +79,63 @@ type Terminfo struct { // Decode decodes the terminfo data contained in buf. func Decode(buf []byte) (*Terminfo, error) { var err error - // check max file length if len(buf) >= maxFileLength { return nil, ErrInvalidFileSize } - d := &decoder{ buf: buf, - len: len(buf), + n: len(buf), } - // read header h, err := d.readInts(6, 16) if err != nil { return nil, err } - var numWidth int - // check magic - if h[fieldMagic] == magic { + switch { + case h[fieldMagic] == magic: numWidth = 16 - } else if h[fieldMagic] == magicExtended { + case h[fieldMagic] == magicExtended: numWidth = 32 - } else { + default: return nil, ErrInvalidMagic } - // check header if hasInvalidCaps(h) { return nil, ErrInvalidHeader } - // check remaining length - if d.len-d.pos < capLength(h) { + if d.n-d.pos < capLength(h) { return nil, ErrUnexpectedFileEnd } - // read names names, err := d.readBytes(h[fieldNameSize]) if err != nil { return nil, err } - // check name is terminated properly i := findNull(names, 0) if i == -1 { return nil, ErrInvalidNames } names = names[:i] - // read bool caps bools, boolsM, err := d.readBools(h[fieldBoolCount]) if err != nil { return nil, err } - // read num caps nums, numsM, err := d.readNums(h[fieldNumCount], numWidth) if err != nil { return nil, err } - // read string caps strs, strsM, err := d.readStrings(h[fieldStringCount], h[fieldTableSize]) if err != nil { return nil, err } - ti := &Terminfo{ Names: strings.Split(string(names), "|"), Bools: bools, @@ -178,57 +145,47 @@ func Decode(buf []byte) (*Terminfo, error) { Strings: strs, StringsM: strsM, } - // at the end of file, so no extended caps - if d.pos >= d.len { + if d.pos >= d.n { return ti, nil } - // decode extended header eh, err := d.readInts(5, 16) if err != nil { return nil, err } - // check extended offset field if hasInvalidExtOffset(eh) { return nil, ErrInvalidExtendedHeader } - // check extended cap lengths - if d.len-d.pos != extCapLength(eh, numWidth) { + if d.n-d.pos != extCapLength(eh, numWidth) { return nil, ErrInvalidExtendedHeader } - // read extended bool caps ti.ExtBools, _, err = d.readBools(eh[fieldExtBoolCount]) if err != nil { return nil, err } - // read extended num caps ti.ExtNums, _, err = d.readNums(eh[fieldExtNumCount], numWidth) if err != nil { return nil, err } - // read extended string data table indexes extIndexes, err := d.readInts(eh[fieldExtOffsetCount], 16) if err != nil { return nil, err } - // read string data table extData, err := d.readBytes(eh[fieldExtTableSize]) if err != nil { return nil, err } - // precautionary check that exactly at end of file - if d.pos != d.len { + if d.pos != d.n { return nil, ErrUnexpectedFileEnd } - var last int // read extended string caps ti.ExtStrings, last, err = readStrings(extIndexes, extData, eh[fieldExtStringCount]) @@ -236,28 +193,24 @@ func Decode(buf []byte) (*Terminfo, error) { return nil, err } extIndexes, extData = extIndexes[eh[fieldExtStringCount]:], extData[last:] - // read extended bool names ti.ExtBoolNames, _, err = readStrings(extIndexes, extData, eh[fieldExtBoolCount]) if err != nil { return nil, err } extIndexes = extIndexes[eh[fieldExtBoolCount]:] - // read extended num names ti.ExtNumNames, _, err = readStrings(extIndexes, extData, eh[fieldExtNumCount]) if err != nil { return nil, err } extIndexes = extIndexes[eh[fieldExtNumCount]:] - // read extended string names ti.ExtStringNames, _, err = readStrings(extIndexes, extData, eh[fieldExtStringCount]) if err != nil { return nil, err } - //extIndexes = extIndexes[eh[fieldExtStringCount]:] - + // extIndexes = extIndexes[eh[fieldExtStringCount]:] return ti, nil } @@ -279,23 +232,19 @@ func Open(dir, name string) (*Terminfo, error) { if buf == nil { return nil, ErrFileNotFound } - // decode ti, err := Decode(buf) if err != nil { return nil, err } - // save original file name ti.File = filename - // add to cache termCache.Lock() for _, n := range ti.Names { termCache.db[n] = ti } termCache.Unlock() - return ti, nil } @@ -441,7 +390,6 @@ func (ti *Terminfo) Fprintf(w io.Writer, i int, v ...interface{}) { // them for this terminal. func (ti *Terminfo) Colorf(fg, bg int, str string) string { maxColors := int(ti.Nums[MaxColors]) - // map bright colors to lower versions if the color table only holds 8. if maxColors == 8 { if fg > 7 && fg < 16 { @@ -451,7 +399,6 @@ func (ti *Terminfo) Colorf(fg, bg int, str string) string { bg -= 8 } } - var s string if maxColors > fg && fg >= 0 { s += ti.Printf(SetAForeground, fg) @@ -480,20 +427,17 @@ func (ti *Terminfo) Goto(row, col int) string { // most strings don't need padding, which is good news! return io.WriteString(w, s) } - end := strings.Index(s, ">") if end == -1 { // unterminated... just emit bytes unadulterated. return io.WriteString(w, "$<"+s) } - var c int c, err = io.WriteString(w, s[:start]) if err != nil { return n + c, err } n += c - s = s[start+2:] val := s[:end] s = s[end+1:] @@ -518,13 +462,11 @@ func (ti *Terminfo) Goto(row, col int) string { break } } - z, pad := ((baud/8)/unit)*ms, ti.Strings[PadChar] b := make([]byte, len(pad)*z) for bp := copy(b, pad); bp < len(b); bp *= 2 { copy(b[bp:], b[:bp]) } - if (!ti.Bools[XonXoff] && baud > int(ti.Nums[PaddingBaudRate])) || mandatory { c, err = w.Write(b) if err != nil { @@ -533,6 +475,5 @@ func (ti *Terminfo) Goto(row, col int) string { n += c } } - return n, nil }*/ diff --git a/vendor/golang.org/x/xerrors/LICENSE b/vendor/golang.org/x/xerrors/LICENSE new file mode 100644 index 000000000..e4a47e17f --- /dev/null +++ b/vendor/golang.org/x/xerrors/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2019 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/xerrors/PATENTS b/vendor/golang.org/x/xerrors/PATENTS new file mode 100644 index 000000000..733099041 --- /dev/null +++ b/vendor/golang.org/x/xerrors/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/xerrors/README b/vendor/golang.org/x/xerrors/README new file mode 100644 index 000000000..aac7867a5 --- /dev/null +++ b/vendor/golang.org/x/xerrors/README @@ -0,0 +1,2 @@ +This repository holds the transition packages for the new Go 1.13 error values. +See golang.org/design/29934-error-values. diff --git a/vendor/golang.org/x/xerrors/adaptor.go b/vendor/golang.org/x/xerrors/adaptor.go new file mode 100644 index 000000000..4317f2483 --- /dev/null +++ b/vendor/golang.org/x/xerrors/adaptor.go @@ -0,0 +1,193 @@ +// Copyright 2018 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 xerrors + +import ( + "bytes" + "fmt" + "io" + "reflect" + "strconv" +) + +// FormatError calls the FormatError method of f with an errors.Printer +// configured according to s and verb, and writes the result to s. +func FormatError(f Formatter, s fmt.State, verb rune) { + // Assuming this function is only called from the Format method, and given + // that FormatError takes precedence over Format, it cannot be called from + // any package that supports errors.Formatter. It is therefore safe to + // disregard that State may be a specific printer implementation and use one + // of our choice instead. + + // limitations: does not support printing error as Go struct. + + var ( + sep = " " // separator before next error + p = &state{State: s} + direct = true + ) + + var err error = f + + switch verb { + // Note that this switch must match the preference order + // for ordinary string printing (%#v before %+v, and so on). + + case 'v': + if s.Flag('#') { + if stringer, ok := err.(fmt.GoStringer); ok { + io.WriteString(&p.buf, stringer.GoString()) + goto exit + } + // proceed as if it were %v + } else if s.Flag('+') { + p.printDetail = true + sep = "\n - " + } + case 's': + case 'q', 'x', 'X': + // Use an intermediate buffer in the rare cases that precision, + // truncation, or one of the alternative verbs (q, x, and X) are + // specified. + direct = false + + default: + p.buf.WriteString("%!") + p.buf.WriteRune(verb) + p.buf.WriteByte('(') + switch { + case err != nil: + p.buf.WriteString(reflect.TypeOf(f).String()) + default: + p.buf.WriteString("") + } + p.buf.WriteByte(')') + io.Copy(s, &p.buf) + return + } + +loop: + for { + switch v := err.(type) { + case Formatter: + err = v.FormatError((*printer)(p)) + case fmt.Formatter: + v.Format(p, 'v') + break loop + default: + io.WriteString(&p.buf, v.Error()) + break loop + } + if err == nil { + break + } + if p.needColon || !p.printDetail { + p.buf.WriteByte(':') + p.needColon = false + } + p.buf.WriteString(sep) + p.inDetail = false + p.needNewline = false + } + +exit: + width, okW := s.Width() + prec, okP := s.Precision() + + if !direct || (okW && width > 0) || okP { + // Construct format string from State s. + format := []byte{'%'} + if s.Flag('-') { + format = append(format, '-') + } + if s.Flag('+') { + format = append(format, '+') + } + if s.Flag(' ') { + format = append(format, ' ') + } + if okW { + format = strconv.AppendInt(format, int64(width), 10) + } + if okP { + format = append(format, '.') + format = strconv.AppendInt(format, int64(prec), 10) + } + format = append(format, string(verb)...) + fmt.Fprintf(s, string(format), p.buf.String()) + } else { + io.Copy(s, &p.buf) + } +} + +var detailSep = []byte("\n ") + +// state tracks error printing state. It implements fmt.State. +type state struct { + fmt.State + buf bytes.Buffer + + printDetail bool + inDetail bool + needColon bool + needNewline bool +} + +func (s *state) Write(b []byte) (n int, err error) { + if s.printDetail { + if len(b) == 0 { + return 0, nil + } + if s.inDetail && s.needColon { + s.needNewline = true + if b[0] == '\n' { + b = b[1:] + } + } + k := 0 + for i, c := range b { + if s.needNewline { + if s.inDetail && s.needColon { + s.buf.WriteByte(':') + s.needColon = false + } + s.buf.Write(detailSep) + s.needNewline = false + } + if c == '\n' { + s.buf.Write(b[k:i]) + k = i + 1 + s.needNewline = true + } + } + s.buf.Write(b[k:]) + if !s.inDetail { + s.needColon = true + } + } else if !s.inDetail { + s.buf.Write(b) + } + return len(b), nil +} + +// printer wraps a state to implement an xerrors.Printer. +type printer state + +func (s *printer) Print(args ...interface{}) { + if !s.inDetail || s.printDetail { + fmt.Fprint((*state)(s), args...) + } +} + +func (s *printer) Printf(format string, args ...interface{}) { + if !s.inDetail || s.printDetail { + fmt.Fprintf((*state)(s), format, args...) + } +} + +func (s *printer) Detail() bool { + s.inDetail = true + return s.printDetail +} diff --git a/vendor/golang.org/x/xerrors/codereview.cfg b/vendor/golang.org/x/xerrors/codereview.cfg new file mode 100644 index 000000000..3f8b14b64 --- /dev/null +++ b/vendor/golang.org/x/xerrors/codereview.cfg @@ -0,0 +1 @@ +issuerepo: golang/go diff --git a/vendor/golang.org/x/xerrors/doc.go b/vendor/golang.org/x/xerrors/doc.go new file mode 100644 index 000000000..2ef99f5a8 --- /dev/null +++ b/vendor/golang.org/x/xerrors/doc.go @@ -0,0 +1,23 @@ +// Copyright 2019 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 xerrors implements functions to manipulate errors. +// +// This package is based on the Go 2 proposal for error values: +// +// https://golang.org/design/29934-error-values +// +// These functions were incorporated into the standard library's errors package +// in Go 1.13: +// - Is +// - As +// - Unwrap +// +// Also, Errorf's %w verb was incorporated into fmt.Errorf. +// +// Use this package to get equivalent behavior in all supported Go versions. +// +// No other features of this package were included in Go 1.13, and at present +// there are no plans to include any of them. +package xerrors // import "golang.org/x/xerrors" diff --git a/vendor/golang.org/x/xerrors/errors.go b/vendor/golang.org/x/xerrors/errors.go new file mode 100644 index 000000000..e88d3772d --- /dev/null +++ b/vendor/golang.org/x/xerrors/errors.go @@ -0,0 +1,33 @@ +// Copyright 2011 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 xerrors + +import "fmt" + +// errorString is a trivial implementation of error. +type errorString struct { + s string + frame Frame +} + +// New returns an error that formats as the given text. +// +// The returned error contains a Frame set to the caller's location and +// implements Formatter to show this information when printed with details. +func New(text string) error { + return &errorString{text, Caller(1)} +} + +func (e *errorString) Error() string { + return e.s +} + +func (e *errorString) Format(s fmt.State, v rune) { FormatError(e, s, v) } + +func (e *errorString) FormatError(p Printer) (next error) { + p.Print(e.s) + e.frame.Format(p) + return nil +} diff --git a/vendor/golang.org/x/xerrors/fmt.go b/vendor/golang.org/x/xerrors/fmt.go new file mode 100644 index 000000000..27a5d70bd --- /dev/null +++ b/vendor/golang.org/x/xerrors/fmt.go @@ -0,0 +1,190 @@ +// Copyright 2018 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 xerrors + +import ( + "fmt" + "strings" + "unicode" + "unicode/utf8" + + "golang.org/x/xerrors/internal" +) + +const percentBangString = "%!" + +// Errorf formats according to a format specifier and returns the string as a +// value that satisfies error. +// +// The returned error includes the file and line number of the caller when +// formatted with additional detail enabled. If the last argument is an error +// the returned error's Format method will return it if the format string ends +// with ": %s", ": %v", or ": %w". If the last argument is an error and the +// format string ends with ": %w", the returned error implements an Unwrap +// method returning it. +// +// If the format specifier includes a %w verb with an error operand in a +// position other than at the end, the returned error will still implement an +// Unwrap method returning the operand, but the error's Format method will not +// return the wrapped error. +// +// It is invalid to include more than one %w verb or to supply it with an +// operand that does not implement the error interface. The %w verb is otherwise +// a synonym for %v. +// +// Note that as of Go 1.13, the fmt.Errorf function will do error formatting, +// but it will not capture a stack backtrace. +func Errorf(format string, a ...interface{}) error { + format = formatPlusW(format) + // Support a ": %[wsv]" suffix, which works well with xerrors.Formatter. + wrap := strings.HasSuffix(format, ": %w") + idx, format2, ok := parsePercentW(format) + percentWElsewhere := !wrap && idx >= 0 + if !percentWElsewhere && (wrap || strings.HasSuffix(format, ": %s") || strings.HasSuffix(format, ": %v")) { + err := errorAt(a, len(a)-1) + if err == nil { + return &noWrapError{fmt.Sprintf(format, a...), nil, Caller(1)} + } + // TODO: this is not entirely correct. The error value could be + // printed elsewhere in format if it mixes numbered with unnumbered + // substitutions. With relatively small changes to doPrintf we can + // have it optionally ignore extra arguments and pass the argument + // list in its entirety. + msg := fmt.Sprintf(format[:len(format)-len(": %s")], a[:len(a)-1]...) + frame := Frame{} + if internal.EnableTrace { + frame = Caller(1) + } + if wrap { + return &wrapError{msg, err, frame} + } + return &noWrapError{msg, err, frame} + } + // Support %w anywhere. + // TODO: don't repeat the wrapped error's message when %w occurs in the middle. + msg := fmt.Sprintf(format2, a...) + if idx < 0 { + return &noWrapError{msg, nil, Caller(1)} + } + err := errorAt(a, idx) + if !ok || err == nil { + // Too many %ws or argument of %w is not an error. Approximate the Go + // 1.13 fmt.Errorf message. + return &noWrapError{fmt.Sprintf("%sw(%s)", percentBangString, msg), nil, Caller(1)} + } + frame := Frame{} + if internal.EnableTrace { + frame = Caller(1) + } + return &wrapError{msg, err, frame} +} + +func errorAt(args []interface{}, i int) error { + if i < 0 || i >= len(args) { + return nil + } + err, ok := args[i].(error) + if !ok { + return nil + } + return err +} + +// formatPlusW is used to avoid the vet check that will barf at %w. +func formatPlusW(s string) string { + return s +} + +// Return the index of the only %w in format, or -1 if none. +// Also return a rewritten format string with %w replaced by %v, and +// false if there is more than one %w. +// TODO: handle "%[N]w". +func parsePercentW(format string) (idx int, newFormat string, ok bool) { + // Loosely copied from golang.org/x/tools/go/analysis/passes/printf/printf.go. + idx = -1 + ok = true + n := 0 + sz := 0 + var isW bool + for i := 0; i < len(format); i += sz { + if format[i] != '%' { + sz = 1 + continue + } + // "%%" is not a format directive. + if i+1 < len(format) && format[i+1] == '%' { + sz = 2 + continue + } + sz, isW = parsePrintfVerb(format[i:]) + if isW { + if idx >= 0 { + ok = false + } else { + idx = n + } + // "Replace" the last character, the 'w', with a 'v'. + p := i + sz - 1 + format = format[:p] + "v" + format[p+1:] + } + n++ + } + return idx, format, ok +} + +// Parse the printf verb starting with a % at s[0]. +// Return how many bytes it occupies and whether the verb is 'w'. +func parsePrintfVerb(s string) (int, bool) { + // Assume only that the directive is a sequence of non-letters followed by a single letter. + sz := 0 + var r rune + for i := 1; i < len(s); i += sz { + r, sz = utf8.DecodeRuneInString(s[i:]) + if unicode.IsLetter(r) { + return i + sz, r == 'w' + } + } + return len(s), false +} + +type noWrapError struct { + msg string + err error + frame Frame +} + +func (e *noWrapError) Error() string { + return fmt.Sprint(e) +} + +func (e *noWrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) } + +func (e *noWrapError) FormatError(p Printer) (next error) { + p.Print(e.msg) + e.frame.Format(p) + return e.err +} + +type wrapError struct { + msg string + err error + frame Frame +} + +func (e *wrapError) Error() string { + return fmt.Sprint(e) +} + +func (e *wrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) } + +func (e *wrapError) FormatError(p Printer) (next error) { + p.Print(e.msg) + e.frame.Format(p) + return e.err +} + +func (e *wrapError) Unwrap() error { + return e.err +} diff --git a/vendor/golang.org/x/xerrors/format.go b/vendor/golang.org/x/xerrors/format.go new file mode 100644 index 000000000..1bc9c26b9 --- /dev/null +++ b/vendor/golang.org/x/xerrors/format.go @@ -0,0 +1,34 @@ +// Copyright 2018 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 xerrors + +// A Formatter formats error messages. +type Formatter interface { + error + + // FormatError prints the receiver's first error and returns the next error in + // the error chain, if any. + FormatError(p Printer) (next error) +} + +// A Printer formats error messages. +// +// The most common implementation of Printer is the one provided by package fmt +// during Printf (as of Go 1.13). Localization packages such as golang.org/x/text/message +// typically provide their own implementations. +type Printer interface { + // Print appends args to the message output. + Print(args ...interface{}) + + // Printf writes a formatted string. + Printf(format string, args ...interface{}) + + // Detail reports whether error detail is requested. + // After the first call to Detail, all text written to the Printer + // is formatted as additional detail, or ignored when + // detail has not been requested. + // If Detail returns false, the caller can avoid printing the detail at all. + Detail() bool +} diff --git a/vendor/golang.org/x/xerrors/frame.go b/vendor/golang.org/x/xerrors/frame.go new file mode 100644 index 000000000..0de628ec5 --- /dev/null +++ b/vendor/golang.org/x/xerrors/frame.go @@ -0,0 +1,56 @@ +// Copyright 2018 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 xerrors + +import ( + "runtime" +) + +// A Frame contains part of a call stack. +type Frame struct { + // Make room for three PCs: the one we were asked for, what it called, + // and possibly a PC for skipPleaseUseCallersFrames. See: + // https://go.googlesource.com/go/+/032678e0fb/src/runtime/extern.go#169 + frames [3]uintptr +} + +// Caller returns a Frame that describes a frame on the caller's stack. +// The argument skip is the number of frames to skip over. +// Caller(0) returns the frame for the caller of Caller. +func Caller(skip int) Frame { + var s Frame + runtime.Callers(skip+1, s.frames[:]) + return s +} + +// location reports the file, line, and function of a frame. +// +// The returned function may be "" even if file and line are not. +func (f Frame) location() (function, file string, line int) { + frames := runtime.CallersFrames(f.frames[:]) + if _, ok := frames.Next(); !ok { + return "", "", 0 + } + fr, ok := frames.Next() + if !ok { + return "", "", 0 + } + return fr.Function, fr.File, fr.Line +} + +// Format prints the stack as error detail. +// It should be called from an error's Format implementation +// after printing any other error detail. +func (f Frame) Format(p Printer) { + if p.Detail() { + function, file, line := f.location() + if function != "" { + p.Printf("%s\n ", function) + } + if file != "" { + p.Printf("%s:%d\n", file, line) + } + } +} diff --git a/vendor/golang.org/x/xerrors/internal/internal.go b/vendor/golang.org/x/xerrors/internal/internal.go new file mode 100644 index 000000000..89f4eca5d --- /dev/null +++ b/vendor/golang.org/x/xerrors/internal/internal.go @@ -0,0 +1,8 @@ +// Copyright 2018 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 internal + +// EnableTrace indicates whether stack information should be recorded in errors. +var EnableTrace = true diff --git a/vendor/golang.org/x/xerrors/wrap.go b/vendor/golang.org/x/xerrors/wrap.go new file mode 100644 index 000000000..9842758ca --- /dev/null +++ b/vendor/golang.org/x/xerrors/wrap.go @@ -0,0 +1,112 @@ +// Copyright 2018 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 xerrors + +import ( + "reflect" +) + +// A Wrapper provides context around another error. +type Wrapper interface { + // Unwrap returns the next error in the error chain. + // If there is no next error, Unwrap returns nil. + Unwrap() error +} + +// Opaque returns an error with the same error formatting as err +// but that does not match err and cannot be unwrapped. +func Opaque(err error) error { + return noWrapper{err} +} + +type noWrapper struct { + error +} + +func (e noWrapper) FormatError(p Printer) (next error) { + if f, ok := e.error.(Formatter); ok { + return f.FormatError(p) + } + p.Print(e.error) + return nil +} + +// Unwrap returns the result of calling the Unwrap method on err, if err implements +// Unwrap. Otherwise, Unwrap returns nil. +// +// Deprecated: As of Go 1.13, use errors.Unwrap instead. +func Unwrap(err error) error { + u, ok := err.(Wrapper) + if !ok { + return nil + } + return u.Unwrap() +} + +// Is reports whether any error in err's chain matches target. +// +// An error is considered to match a target if it is equal to that target or if +// it implements a method Is(error) bool such that Is(target) returns true. +// +// Deprecated: As of Go 1.13, use errors.Is instead. +func Is(err, target error) bool { + if target == nil { + return err == target + } + + isComparable := reflect.TypeOf(target).Comparable() + for { + if isComparable && err == target { + return true + } + if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) { + return true + } + // TODO: consider supporing target.Is(err). This would allow + // user-definable predicates, but also may allow for coping with sloppy + // APIs, thereby making it easier to get away with them. + if err = Unwrap(err); err == nil { + return false + } + } +} + +// As finds the first error in err's chain that matches the type to which target +// points, and if so, sets the target to its value and returns true. An error +// matches a type if it is assignable to the target type, or if it has a method +// As(interface{}) bool such that As(target) returns true. As will panic if target +// is not a non-nil pointer to a type which implements error or is of interface type. +// +// The As method should set the target to its value and return true if err +// matches the type to which target points. +// +// Deprecated: As of Go 1.13, use errors.As instead. +func As(err error, target interface{}) bool { + if target == nil { + panic("errors: target cannot be nil") + } + val := reflect.ValueOf(target) + typ := val.Type() + if typ.Kind() != reflect.Ptr || val.IsNil() { + panic("errors: target must be a non-nil pointer") + } + if e := typ.Elem(); e.Kind() != reflect.Interface && !e.Implements(errorType) { + panic("errors: *target must be interface or implement error") + } + targetType := typ.Elem() + for err != nil { + if reflect.TypeOf(err).AssignableTo(targetType) { + val.Elem().Set(reflect.ValueOf(err)) + return true + } + if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) { + return true + } + err = Unwrap(err) + } + return false +} + +var errorType = reflect.TypeOf((*error)(nil)).Elem() diff --git a/vendor/modules.txt b/vendor/modules.txt index f483caad5..3be7021b0 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -4,7 +4,7 @@ contrib.go.opencensus.io/exporter/prometheus # github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 ## explicit github.com/Azure/go-ntlmssp -# github.com/BurntSushi/toml v1.2.1 +# github.com/BurntSushi/toml v1.3.2 ## explicit; go 1.16 github.com/BurntSushi/toml github.com/BurntSushi/toml/internal @@ -753,8 +753,8 @@ github.com/emvi/iso-639-1 # github.com/evanphx/json-patch/v5 v5.5.0 ## explicit; go 1.12 github.com/evanphx/json-patch/v5 -# github.com/fatih/color v1.13.0 -## explicit; go 1.13 +# github.com/fatih/color v1.14.1 +## explicit; go 1.17 github.com/fatih/color # github.com/felixge/httpsnoop v1.0.3 ## explicit; go 1.13 @@ -951,6 +951,8 @@ github.com/go-sql-driver/mysql # github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 ## explicit; go 1.13 github.com/go-task/slim-sprig +# github.com/go-test/deep v1.1.0 +## explicit; go 1.16 # github.com/gobwas/glob v0.2.3 ## explicit github.com/gobwas/glob @@ -974,6 +976,16 @@ github.com/gobwas/pool/pbytes ## explicit github.com/gobwas/ws github.com/gobwas/ws/wsutil +# github.com/goccy/go-yaml v1.11.0 +## explicit; go 1.18 +github.com/goccy/go-yaml +github.com/goccy/go-yaml/ast +github.com/goccy/go-yaml/internal/errors +github.com/goccy/go-yaml/lexer +github.com/goccy/go-yaml/parser +github.com/goccy/go-yaml/printer +github.com/goccy/go-yaml/scanner +github.com/goccy/go-yaml/token # github.com/godbus/dbus/v5 v5.0.6 ## explicit; go 1.12 github.com/godbus/dbus/v5 @@ -1040,22 +1052,23 @@ github.com/google/pprof/profile # github.com/google/uuid v1.3.0 ## explicit github.com/google/uuid -# github.com/gookit/color v1.5.2 -## explicit; go 1.16 +# github.com/gookit/color v1.5.3 +## explicit; go 1.18 github.com/gookit/color -# github.com/gookit/config/v2 v2.1.8 -## explicit; go 1.16 +# github.com/gookit/config/v2 v2.2.2 +## explicit; go 1.18 github.com/gookit/config/v2 github.com/gookit/config/v2/yaml -# github.com/gookit/goutil v0.6.0 +# github.com/gookit/goutil v0.6.9 ## explicit; go 1.18 github.com/gookit/goutil github.com/gookit/goutil/arrutil +github.com/gookit/goutil/basefn +github.com/gookit/goutil/byteutil 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/fsutil github.com/gookit/goutil/internal/comfunc github.com/gookit/goutil/jsonutil @@ -1066,9 +1079,9 @@ github.com/gookit/goutil/stdio github.com/gookit/goutil/stdutil github.com/gookit/goutil/structs github.com/gookit/goutil/strutil +github.com/gookit/goutil/strutil/textutil github.com/gookit/goutil/sysutil github.com/gookit/goutil/sysutil/cmdr -github.com/gookit/goutil/sysutil/process # github.com/gorilla/handlers v1.5.1 ## explicit; go 1.14 github.com/gorilla/handlers @@ -1127,7 +1140,7 @@ github.com/huandu/xstrings # github.com/iancoleman/strcase v0.2.0 ## explicit; go 1.16 github.com/iancoleman/strcase -# github.com/imdario/mergo v0.3.13 +# github.com/imdario/mergo v0.3.15 ## explicit; go 1.13 github.com/imdario/mergo # github.com/inconshreveable/mousetrap v1.1.0 @@ -1229,7 +1242,7 @@ github.com/mattermost/xml-roundtrip-validator # github.com/mattn/go-colorable v0.1.13 ## explicit; go 1.15 github.com/mattn/go-colorable -# github.com/mattn/go-isatty v0.0.16 +# github.com/mattn/go-isatty v0.0.17 ## explicit; go 1.15 github.com/mattn/go-isatty # github.com/mattn/go-runewidth v0.0.13 @@ -1667,8 +1680,8 @@ github.com/xeipuuv/gojsonreference # github.com/xhit/go-simple-mail/v2 v2.13.0 ## explicit; go 1.13 github.com/xhit/go-simple-mail/v2 -# github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 -## explicit; go 1.15 +# github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e +## explicit; go 1.19 github.com/xo/terminfo # github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 ## explicit @@ -1950,7 +1963,7 @@ golang.org/x/net/trace ## explicit; go 1.17 golang.org/x/oauth2 golang.org/x/oauth2/internal -# golang.org/x/sync v0.1.0 +# golang.org/x/sync v0.2.0 ## explicit golang.org/x/sync/errgroup golang.org/x/sync/semaphore @@ -2017,6 +2030,10 @@ golang.org/x/tools/internal/pkgbits golang.org/x/tools/internal/tokeninternal golang.org/x/tools/internal/typeparams golang.org/x/tools/internal/typesinternal +# golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 +## explicit; go 1.17 +golang.org/x/xerrors +golang.org/x/xerrors/internal # google.golang.org/appengine v1.6.7 ## explicit; go 1.11 google.golang.org/appengine/internal