diff --git a/go.mod b/go.mod
index 1f2f8ec85..51b643052 100644
--- a/go.mod
+++ b/go.mod
@@ -1,8 +1,6 @@
module github.com/go-graphite/carbonapi
-go 1.21.0
-
-toolchain go1.22.1
+go 1.23.0
require (
github.com/JaderDias/movingmedian v0.0.0-20220813210630-d8c6b6de8835
@@ -32,8 +30,8 @@ require (
github.com/natefinch/atomic v1.0.1
github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529
github.com/satori/go.uuid v1.2.0
- github.com/spf13/viper v1.20.1
- github.com/stretchr/testify v1.10.0
+ github.com/spf13/viper v1.21.0
+ github.com/stretchr/testify v1.11.1
github.com/tebeka/strftime v0.1.5
github.com/tevino/abool v1.2.0
github.com/tinylib/msgp v1.1.9
@@ -49,26 +47,27 @@ require (
github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
- github.com/fsnotify/fsnotify v1.8.0 // indirect
- github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
+ github.com/fsnotify/fsnotify v1.9.0 // indirect
+ github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/flatbuffers v24.3.25+incompatible // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/openhistogram/circonusllhist v0.4.0 // indirect
- github.com/pelletier/go-toml/v2 v2.2.3 // indirect
+ github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/philhofer/fwd v1.1.2 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
- github.com/sagikazarmark/locafero v0.7.0 // indirect
- github.com/sourcegraph/conc v0.3.0 // indirect
- github.com/spf13/afero v1.12.0 // indirect
- github.com/spf13/cast v1.7.1 // indirect
- github.com/spf13/pflag v1.0.6 // indirect
+ github.com/sagikazarmark/locafero v0.11.0 // indirect
+ github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
+ github.com/spf13/afero v1.15.0 // indirect
+ github.com/spf13/cast v1.10.0 // indirect
+ github.com/spf13/pflag v1.0.10 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/yuin/gopher-lua v1.1.1 // indirect
go.uber.org/multierr v1.11.0 // indirect
+ go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
- golang.org/x/sync v0.10.0 // indirect
- golang.org/x/text v0.21.0 // indirect
+ golang.org/x/sync v0.16.0 // indirect
+ golang.org/x/text v0.28.0 // indirect
google.golang.org/protobuf v1.36.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/go.sum b/go.sum
index 5416b3da0..e96841b09 100644
--- a/go.sum
+++ b/go.sum
@@ -50,16 +50,16 @@ github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHqu
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
-github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
-github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
+github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
+github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-errors/errors v1.1.1 h1:ljK/pL5ltg3qoN+OtN6yCv9HWSfMwxSx90GJCZQxYNg=
github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
github.com/go-graphite/gocairo v0.1.0 h1:VH624JTY1a6DsBHcM0knN73jJSUS/LwpqVEyXyBZRH4=
github.com/go-graphite/gocairo v0.1.0/go.mod h1:6yOCt3ZqO6z4l6zZd5jM5Cj89B1kDK999KRYp6QO4Vs=
github.com/go-graphite/protocol v1.0.0 h1:Fqb0mkVVtfMrn6vw6Ntm3raf3gVVZCOVdZu4JosW5qE=
github.com/go-graphite/protocol v1.0.0/go.mod h1:eonkg/0UGhJUYu+PshOg1NzWSUcXskr/yHeQXJHJr8Y=
-github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
-github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
+github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
+github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -136,8 +136,8 @@ github.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0
github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM=
github.com/openhistogram/circonusllhist v0.4.0 h1:t77KqrahIG/iuJqTBNDBwyHu1dkvbCg30amo/TB4gKM=
github.com/openhistogram/circonusllhist v0.4.0/go.mod h1:PfeYJ/RW2+Jfv3wTz0upbY2TRour/LLqIm2K2Kw5zg0=
-github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
-github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
+github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
+github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
github.com/pierrec/lz4 v2.6.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
@@ -152,28 +152,28 @@ github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZV
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529 h1:18kd+8ZUlt/ARXhljq+14TwAoKa61q6dX8jtwOf6DH8=
github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529/go.mod h1:qe5TWALJ8/a1Lqznoc5BDHpYX/8HU60Hm2AwRmqzxqA=
-github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
-github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
+github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
+github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
-github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
-github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
-github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
-github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
-github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
-github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
-github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
-github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
-github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
+github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
+github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
+github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
+github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
+github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
+github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
+github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
+github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
+github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
-github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tebeka/strftime v0.1.5 h1:1NQKN1NiQgkqd/2moD6ySP/5CoZQsKa1d3ZhJ44Jpmg=
@@ -202,6 +202,8 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
+go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -234,8 +236,8 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
-golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
+golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -250,8 +252,8 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
-golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
+golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
+golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
diff --git a/vendor/github.com/fsnotify/fsnotify/.cirrus.yml b/vendor/github.com/fsnotify/fsnotify/.cirrus.yml
index f4e7dbf37..7f257e99a 100644
--- a/vendor/github.com/fsnotify/fsnotify/.cirrus.yml
+++ b/vendor/github.com/fsnotify/fsnotify/.cirrus.yml
@@ -1,7 +1,7 @@
freebsd_task:
name: 'FreeBSD'
freebsd_instance:
- image_family: freebsd-14-1
+ image_family: freebsd-14-2
install_script:
- pkg update -f
- pkg install -y go
diff --git a/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md b/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
index fa854785d..6468d2cf4 100644
--- a/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
+++ b/vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
@@ -1,6 +1,39 @@
# Changelog
-1.8.0 2023-10-31
+1.9.0 2024-04-04
+----------------
+
+### Changes and fixes
+
+- all: make BufferedWatcher buffered again ([#657])
+
+- inotify: fix race when adding/removing watches while a watched path is being
+ deleted ([#678], [#686])
+
+- inotify: don't send empty event if a watched path is unmounted ([#655])
+
+- inotify: don't register duplicate watches when watching both a symlink and its
+ target; previously that would get "half-added" and removing the second would
+ panic ([#679])
+
+- kqueue: fix watching relative symlinks ([#681])
+
+- kqueue: correctly mark pre-existing entries when watching a link to a dir on
+ kqueue ([#682])
+
+- illumos: don't send error if changed file is deleted while processing the
+ event ([#678])
+
+
+[#657]: https://github.com/fsnotify/fsnotify/pull/657
+[#678]: https://github.com/fsnotify/fsnotify/pull/678
+[#686]: https://github.com/fsnotify/fsnotify/pull/686
+[#655]: https://github.com/fsnotify/fsnotify/pull/655
+[#681]: https://github.com/fsnotify/fsnotify/pull/681
+[#679]: https://github.com/fsnotify/fsnotify/pull/679
+[#682]: https://github.com/fsnotify/fsnotify/pull/682
+
+1.8.0 2024-10-31
----------------
### Additions
diff --git a/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md b/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
index e4ac2a2ff..4cc40fa59 100644
--- a/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
+++ b/vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
@@ -77,6 +77,7 @@ End-of-line escapes with `\` are not supported.
debug [yes/no] # Enable/disable FSNOTIFY_DEBUG (tests are run in
parallel by default, so -parallel=1 is probably a good
idea).
+ print [any strings] # Print text to stdout; for debugging.
touch path
mkdir [-p] dir
diff --git a/vendor/github.com/fsnotify/fsnotify/README.md b/vendor/github.com/fsnotify/fsnotify/README.md
index e480733d1..1f4eb583d 100644
--- a/vendor/github.com/fsnotify/fsnotify/README.md
+++ b/vendor/github.com/fsnotify/fsnotify/README.md
@@ -15,7 +15,6 @@ Platform support:
| ReadDirectoryChangesW | Windows | Supported |
| FEN | illumos | Supported |
| fanotify | Linux 5.9+ | [Not yet](https://github.com/fsnotify/fsnotify/issues/114) |
-| AHAFS | AIX | [aix branch]; experimental due to lack of maintainer and test environment |
| FSEvents | macOS | [Needs support in x/sys/unix][fsevents] |
| USN Journals | Windows | [Needs support in x/sys/windows][usn] |
| Polling | *All* | [Not yet](https://github.com/fsnotify/fsnotify/issues/9) |
@@ -25,7 +24,6 @@ untested.
[fsevents]: https://github.com/fsnotify/fsnotify/issues/11#issuecomment-1279133120
[usn]: https://github.com/fsnotify/fsnotify/issues/53#issuecomment-1279829847
-[aix branch]: https://github.com/fsnotify/fsnotify/issues/353#issuecomment-1284590129
Usage
-----
diff --git a/vendor/github.com/fsnotify/fsnotify/backend_fen.go b/vendor/github.com/fsnotify/fsnotify/backend_fen.go
index c349c326c..57fc69284 100644
--- a/vendor/github.com/fsnotify/fsnotify/backend_fen.go
+++ b/vendor/github.com/fsnotify/fsnotify/backend_fen.go
@@ -9,6 +9,7 @@ package fsnotify
import (
"errors"
"fmt"
+ "io/fs"
"os"
"path/filepath"
"sync"
@@ -19,27 +20,25 @@ import (
)
type fen struct {
+ *shared
Events chan Event
Errors chan error
mu sync.Mutex
port *unix.EventPort
- done chan struct{} // Channel for sending a "quit message" to the reader goroutine
dirs map[string]Op // Explicitly watched directories
watches map[string]Op // Explicitly watched non-directories
}
-func newBackend(ev chan Event, errs chan error) (backend, error) {
- return newBufferedBackend(0, ev, errs)
-}
+var defaultBufferSize = 0
-func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error) {
+func newBackend(ev chan Event, errs chan error) (backend, error) {
w := &fen{
+ shared: newShared(ev, errs),
Events: ev,
Errors: errs,
dirs: make(map[string]Op),
watches: make(map[string]Op),
- done: make(chan struct{}),
}
var err error
@@ -52,49 +51,10 @@ func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error
return w, nil
}
-// sendEvent attempts to send an event to the user, returning true if the event
-// was put in the channel successfully and false if the watcher has been closed.
-func (w *fen) sendEvent(name string, op Op) (sent bool) {
- select {
- case <-w.done:
- return false
- case w.Events <- Event{Name: name, Op: op}:
- return true
- }
-}
-
-// sendError attempts to send an error to the user, returning true if the error
-// was put in the channel successfully and false if the watcher has been closed.
-func (w *fen) sendError(err error) (sent bool) {
- if err == nil {
- return true
- }
- select {
- case <-w.done:
- return false
- case w.Errors <- err:
- return true
- }
-}
-
-func (w *fen) isClosed() bool {
- select {
- case <-w.done:
- return true
- default:
- return false
- }
-}
-
func (w *fen) Close() error {
- // Take the lock used by associateFile to prevent lingering events from
- // being processed after the close
- w.mu.Lock()
- defer w.mu.Unlock()
- if w.isClosed() {
+ if w.shared.close() {
return nil
}
- close(w.done)
return w.port.Close()
}
@@ -209,7 +169,7 @@ func (w *fen) readEvents() {
return
}
// There was an error not caused by calling w.Close()
- if !w.sendError(err) {
+ if !w.sendError(fmt.Errorf("port.Get: %w", err)) {
return
}
}
@@ -277,13 +237,13 @@ func (w *fen) handleEvent(event *unix.PortEvent) error {
isWatched := watchedDir || watchedPath
if events&unix.FILE_DELETE != 0 {
- if !w.sendEvent(path, Remove) {
+ if !w.sendEvent(Event{Name: path, Op: Remove}) {
return nil
}
reRegister = false
}
if events&unix.FILE_RENAME_FROM != 0 {
- if !w.sendEvent(path, Rename) {
+ if !w.sendEvent(Event{Name: path, Op: Rename}) {
return nil
}
// Don't keep watching the new file name
@@ -297,7 +257,7 @@ func (w *fen) handleEvent(event *unix.PortEvent) error {
// inotify reports a Remove event in this case, so we simulate this
// here.
- if !w.sendEvent(path, Remove) {
+ if !w.sendEvent(Event{Name: path, Op: Remove}) {
return nil
}
// Don't keep watching the file that was removed
@@ -331,7 +291,7 @@ func (w *fen) handleEvent(event *unix.PortEvent) error {
// get here, the sudirectory is already gone. Clearly we were watching
// this path but now it is gone. Let's tell the user that it was
// removed.
- if !w.sendEvent(path, Remove) {
+ if !w.sendEvent(Event{Name: path, Op: Remove}) {
return nil
}
// Suppress extra write events on removed directories; they are not
@@ -346,7 +306,7 @@ func (w *fen) handleEvent(event *unix.PortEvent) error {
if err != nil {
// The symlink still exists, but the target is gone. Report the
// Remove similar to above.
- if !w.sendEvent(path, Remove) {
+ if !w.sendEvent(Event{Name: path, Op: Remove}) {
return nil
}
// Don't return the error
@@ -359,7 +319,7 @@ func (w *fen) handleEvent(event *unix.PortEvent) error {
return err
}
} else {
- if !w.sendEvent(path, Write) {
+ if !w.sendEvent(Event{Name: path, Op: Write}) {
return nil
}
}
@@ -367,7 +327,7 @@ func (w *fen) handleEvent(event *unix.PortEvent) error {
if events&unix.FILE_ATTRIB != 0 && stat != nil {
// Only send Chmod if perms changed
if stat.Mode().Perm() != fmode.Perm() {
- if !w.sendEvent(path, Chmod) {
+ if !w.sendEvent(Event{Name: path, Op: Chmod}) {
return nil
}
}
@@ -376,17 +336,27 @@ func (w *fen) handleEvent(event *unix.PortEvent) error {
if stat != nil {
// If we get here, it means we've hit an event above that requires us to
// continue watching the file or directory
- return w.associateFile(path, stat, isWatched)
+ err := w.associateFile(path, stat, isWatched)
+ if errors.Is(err, fs.ErrNotExist) {
+ // Path may have been removed since the stat.
+ err = nil
+ }
+ return err
}
return nil
}
+// The directory was modified, so we must find unwatched entities and watch
+// them. If something was removed from the directory, nothing will happen, as
+// everything else should still be watched.
func (w *fen) updateDirectory(path string) error {
- // The directory was modified, so we must find unwatched entities and watch
- // them. If something was removed from the directory, nothing will happen,
- // as everything else should still be watched.
files, err := os.ReadDir(path)
if err != nil {
+ // Directory no longer exists: probably just deleted since we got the
+ // event.
+ if errors.Is(err, fs.ErrNotExist) {
+ return nil
+ }
return err
}
@@ -401,10 +371,15 @@ func (w *fen) updateDirectory(path string) error {
return err
}
err = w.associateFile(path, finfo, false)
+ if errors.Is(err, fs.ErrNotExist) {
+ // File may have disappeared between getting the dir listing and
+ // adding the port: that's okay to ignore.
+ continue
+ }
if !w.sendError(err) {
return nil
}
- if !w.sendEvent(path, Create) {
+ if !w.sendEvent(Event{Name: path, Op: Create}) {
return nil
}
}
@@ -430,7 +405,7 @@ func (w *fen) associateFile(path string, stat os.FileInfo, follow bool) error {
// has fired but we haven't processed it yet.
err := w.port.DissociatePath(path)
if err != nil && !errors.Is(err, unix.ENOENT) {
- return err
+ return fmt.Errorf("port.DissociatePath(%q): %w", path, err)
}
}
@@ -446,14 +421,22 @@ func (w *fen) associateFile(path string, stat os.FileInfo, follow bool) error {
if true {
events |= unix.FILE_ATTRIB
}
- return w.port.AssociatePath(path, stat, events, stat.Mode())
+ err := w.port.AssociatePath(path, stat, events, stat.Mode())
+ if err != nil {
+ return fmt.Errorf("port.AssociatePath(%q): %w", path, err)
+ }
+ return nil
}
func (w *fen) dissociateFile(path string, stat os.FileInfo, unused bool) error {
if !w.port.PathIsWatched(path) {
return nil
}
- return w.port.DissociatePath(path)
+ err := w.port.DissociatePath(path)
+ if err != nil {
+ return fmt.Errorf("port.DissociatePath(%q): %w", path, err)
+ }
+ return nil
}
func (w *fen) WatchList() []string {
diff --git a/vendor/github.com/fsnotify/fsnotify/backend_inotify.go b/vendor/github.com/fsnotify/fsnotify/backend_inotify.go
index 36c311694..a36cb89d7 100644
--- a/vendor/github.com/fsnotify/fsnotify/backend_inotify.go
+++ b/vendor/github.com/fsnotify/fsnotify/backend_inotify.go
@@ -19,6 +19,7 @@ import (
)
type inotify struct {
+ *shared
Events chan Event
Errors chan error
@@ -27,8 +28,6 @@ type inotify struct {
fd int
inotifyFile *os.File
watches *watches
- done chan struct{} // Channel for sending a "quit message" to the reader goroutine
- doneMu sync.Mutex
doneResp chan struct{} // Channel to respond to Close
// Store rename cookies in an array, with the index wrapping to 0. Almost
@@ -52,7 +51,6 @@ type inotify struct {
type (
watches struct {
- mu sync.RWMutex
wd map[uint32]*watch // wd → watch
path map[string]uint32 // pathname → wd
}
@@ -75,34 +73,13 @@ func newWatches() *watches {
}
}
-func (w *watches) len() int {
- w.mu.RLock()
- defer w.mu.RUnlock()
- return len(w.wd)
-}
-
-func (w *watches) add(ww *watch) {
- w.mu.Lock()
- defer w.mu.Unlock()
- w.wd[ww.wd] = ww
- w.path[ww.path] = ww.wd
-}
-
-func (w *watches) remove(wd uint32) {
- w.mu.Lock()
- defer w.mu.Unlock()
- watch := w.wd[wd] // Could have had Remove() called. See #616.
- if watch == nil {
- return
- }
- delete(w.path, watch.path)
- delete(w.wd, wd)
-}
+func (w *watches) byPath(path string) *watch { return w.wd[w.path[path]] }
+func (w *watches) byWd(wd uint32) *watch { return w.wd[wd] }
+func (w *watches) len() int { return len(w.wd) }
+func (w *watches) add(ww *watch) { w.wd[ww.wd] = ww; w.path[ww.path] = ww.wd }
+func (w *watches) remove(watch *watch) { delete(w.path, watch.path); delete(w.wd, watch.wd) }
func (w *watches) removePath(path string) ([]uint32, error) {
- w.mu.Lock()
- defer w.mu.Unlock()
-
path, recurse := recursivePath(path)
wd, ok := w.path[path]
if !ok {
@@ -123,7 +100,7 @@ func (w *watches) removePath(path string) ([]uint32, error) {
wds := make([]uint32, 0, 8)
wds = append(wds, wd)
for p, rwd := range w.path {
- if filepath.HasPrefix(p, path) {
+ if strings.HasPrefix(p, path) {
delete(w.path, p)
delete(w.wd, rwd)
wds = append(wds, rwd)
@@ -132,22 +109,7 @@ func (w *watches) removePath(path string) ([]uint32, error) {
return wds, nil
}
-func (w *watches) byPath(path string) *watch {
- w.mu.RLock()
- defer w.mu.RUnlock()
- return w.wd[w.path[path]]
-}
-
-func (w *watches) byWd(wd uint32) *watch {
- w.mu.RLock()
- defer w.mu.RUnlock()
- return w.wd[wd]
-}
-
func (w *watches) updatePath(path string, f func(*watch) (*watch, error)) error {
- w.mu.Lock()
- defer w.mu.Unlock()
-
var existing *watch
wd, ok := w.path[path]
if ok {
@@ -170,11 +132,9 @@ func (w *watches) updatePath(path string, f func(*watch) (*watch, error)) error
return nil
}
-func newBackend(ev chan Event, errs chan error) (backend, error) {
- return newBufferedBackend(0, ev, errs)
-}
+var defaultBufferSize = 0
-func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error) {
+func newBackend(ev chan Event, errs chan error) (backend, error) {
// Need to set nonblocking mode for SetDeadline to work, otherwise blocking
// I/O operations won't terminate on close.
fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK)
@@ -183,12 +143,12 @@ func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error
}
w := &inotify{
+ shared: newShared(ev, errs),
Events: ev,
Errors: errs,
fd: fd,
inotifyFile: os.NewFile(uintptr(fd), ""),
watches: newWatches(),
- done: make(chan struct{}),
doneResp: make(chan struct{}),
}
@@ -196,46 +156,10 @@ func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error
return w, nil
}
-// Returns true if the event was sent, or false if watcher is closed.
-func (w *inotify) sendEvent(e Event) bool {
- select {
- case <-w.done:
- return false
- case w.Events <- e:
- return true
- }
-}
-
-// Returns true if the error was sent, or false if watcher is closed.
-func (w *inotify) sendError(err error) bool {
- if err == nil {
- return true
- }
- select {
- case <-w.done:
- return false
- case w.Errors <- err:
- return true
- }
-}
-
-func (w *inotify) isClosed() bool {
- select {
- case <-w.done:
- return true
- default:
- return false
- }
-}
-
func (w *inotify) Close() error {
- w.doneMu.Lock()
- if w.isClosed() {
- w.doneMu.Unlock()
+ if w.shared.close() {
return nil
}
- close(w.done)
- w.doneMu.Unlock()
// Causes any blocking reads to return with an error, provided the file
// still supports deadline operations.
@@ -244,9 +168,7 @@ func (w *inotify) Close() error {
return err
}
- // Wait for goroutine to close
- <-w.doneResp
-
+ <-w.doneResp // Wait for readEvents() to finish.
return nil
}
@@ -266,6 +188,43 @@ func (w *inotify) AddWith(path string, opts ...addOpt) error {
return fmt.Errorf("%w: %s", xErrUnsupported, with.op)
}
+ add := func(path string, with withOpts, recurse bool) error {
+ var flags uint32
+ if with.noFollow {
+ flags |= unix.IN_DONT_FOLLOW
+ }
+ if with.op.Has(Create) {
+ flags |= unix.IN_CREATE
+ }
+ if with.op.Has(Write) {
+ flags |= unix.IN_MODIFY
+ }
+ if with.op.Has(Remove) {
+ flags |= unix.IN_DELETE | unix.IN_DELETE_SELF
+ }
+ if with.op.Has(Rename) {
+ flags |= unix.IN_MOVED_TO | unix.IN_MOVED_FROM | unix.IN_MOVE_SELF
+ }
+ if with.op.Has(Chmod) {
+ flags |= unix.IN_ATTRIB
+ }
+ if with.op.Has(xUnportableOpen) {
+ flags |= unix.IN_OPEN
+ }
+ if with.op.Has(xUnportableRead) {
+ flags |= unix.IN_ACCESS
+ }
+ if with.op.Has(xUnportableCloseWrite) {
+ flags |= unix.IN_CLOSE_WRITE
+ }
+ if with.op.Has(xUnportableCloseRead) {
+ flags |= unix.IN_CLOSE_NOWRITE
+ }
+ return w.register(path, flags, recurse)
+ }
+
+ w.mu.Lock()
+ defer w.mu.Unlock()
path, recurse := recursivePath(path)
if recurse {
return filepath.WalkDir(path, func(root string, d fs.DirEntry, err error) error {
@@ -289,46 +248,11 @@ func (w *inotify) AddWith(path string, opts ...addOpt) error {
w.sendEvent(Event{Name: root, Op: Create})
}
- return w.add(root, with, true)
+ return add(root, with, true)
})
}
- return w.add(path, with, false)
-}
-
-func (w *inotify) add(path string, with withOpts, recurse bool) error {
- var flags uint32
- if with.noFollow {
- flags |= unix.IN_DONT_FOLLOW
- }
- if with.op.Has(Create) {
- flags |= unix.IN_CREATE
- }
- if with.op.Has(Write) {
- flags |= unix.IN_MODIFY
- }
- if with.op.Has(Remove) {
- flags |= unix.IN_DELETE | unix.IN_DELETE_SELF
- }
- if with.op.Has(Rename) {
- flags |= unix.IN_MOVED_TO | unix.IN_MOVED_FROM | unix.IN_MOVE_SELF
- }
- if with.op.Has(Chmod) {
- flags |= unix.IN_ATTRIB
- }
- if with.op.Has(xUnportableOpen) {
- flags |= unix.IN_OPEN
- }
- if with.op.Has(xUnportableRead) {
- flags |= unix.IN_ACCESS
- }
- if with.op.Has(xUnportableCloseWrite) {
- flags |= unix.IN_CLOSE_WRITE
- }
- if with.op.Has(xUnportableCloseRead) {
- flags |= unix.IN_CLOSE_NOWRITE
- }
- return w.register(path, flags, recurse)
+ return add(path, with, false)
}
func (w *inotify) register(path string, flags uint32, recurse bool) error {
@@ -342,6 +266,10 @@ func (w *inotify) register(path string, flags uint32, recurse bool) error {
return nil, err
}
+ if e, ok := w.watches.wd[uint32(wd)]; ok {
+ return e, nil
+ }
+
if existing == nil {
return &watch{
wd: uint32(wd),
@@ -365,6 +293,9 @@ func (w *inotify) Remove(name string) error {
fmt.Fprintf(os.Stderr, "FSNOTIFY_DEBUG: %s Remove(%q)\n",
time.Now().Format("15:04:05.000000000"), name)
}
+
+ w.mu.Lock()
+ defer w.mu.Unlock()
return w.remove(filepath.Clean(name))
}
@@ -399,13 +330,12 @@ func (w *inotify) WatchList() []string {
return nil
}
+ w.mu.Lock()
+ defer w.mu.Unlock()
entries := make([]string, 0, w.watches.len())
- w.watches.mu.RLock()
for pathname := range w.watches.path {
entries = append(entries, pathname)
}
- w.watches.mu.RUnlock()
-
return entries
}
@@ -418,21 +348,17 @@ func (w *inotify) readEvents() {
close(w.Events)
}()
- var (
- buf [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
- errno error // Syscall errno
- )
+ var buf [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
for {
- // See if we have been closed.
if w.isClosed() {
return
}
n, err := w.inotifyFile.Read(buf[:])
- switch {
- case errors.Unwrap(err) == os.ErrClosed:
- return
- case err != nil:
+ if err != nil {
+ if errors.Is(err, os.ErrClosed) {
+ return
+ }
if !w.sendError(err) {
return
}
@@ -440,13 +366,9 @@ func (w *inotify) readEvents() {
}
if n < unix.SizeofInotifyEvent {
- var err error
+ err := errors.New("notify: short read in readEvents()") // Read was too short.
if n == 0 {
err = io.EOF // If EOF is received. This should really never happen.
- } else if n < 0 {
- err = errno // If an error occurred while reading.
- } else {
- err = errors.New("notify: short read in readEvents()") // Read was too short.
}
if !w.sendError(err) {
return
@@ -454,132 +376,135 @@ func (w *inotify) readEvents() {
continue
}
- // We don't know how many events we just read into the buffer
- // While the offset points to at least one whole event...
+ // We don't know how many events we just read into the buffer While the
+ // offset points to at least one whole event.
var offset uint32
for offset <= uint32(n-unix.SizeofInotifyEvent) {
- var (
- // Point "raw" to the event in the buffer
- raw = (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset]))
- mask = uint32(raw.Mask)
- nameLen = uint32(raw.Len)
- // Move to the next event in the buffer
- next = func() { offset += unix.SizeofInotifyEvent + nameLen }
- )
-
- if mask&unix.IN_Q_OVERFLOW != 0 {
+ // Point to the event in the buffer.
+ inEvent := (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset]))
+
+ if inEvent.Mask&unix.IN_Q_OVERFLOW != 0 {
if !w.sendError(ErrEventOverflow) {
return
}
}
- /// If the event happened to the watched directory or the watched
- /// file, the kernel doesn't append the filename to the event, but
- /// we would like to always fill the the "Name" field with a valid
- /// filename. We retrieve the path of the watch from the "paths"
- /// map.
- watch := w.watches.byWd(uint32(raw.Wd))
- /// Can be nil if Remove() was called in another goroutine for this
- /// path inbetween reading the events from the kernel and reading
- /// the internal state. Not much we can do about it, so just skip.
- /// See #616.
- if watch == nil {
- next()
- continue
+ ev, ok := w.handleEvent(inEvent, &buf, offset)
+ if !ok {
+ return
}
-
- name := watch.path
- if nameLen > 0 {
- /// Point "bytes" at the first byte of the filename
- bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent]))[:nameLen:nameLen]
- /// The filename is padded with NULL bytes. TrimRight() gets rid of those.
- name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
+ if !w.sendEvent(ev) {
+ return
}
- if debug {
- internal.Debug(name, raw.Mask, raw.Cookie)
- }
+ // Move to the next event in the buffer
+ offset += unix.SizeofInotifyEvent + inEvent.Len
+ }
+ }
+}
- if mask&unix.IN_IGNORED != 0 { //&& event.Op != 0
- next()
- continue
- }
+func (w *inotify) handleEvent(inEvent *unix.InotifyEvent, buf *[65536]byte, offset uint32) (Event, bool) {
+ w.mu.Lock()
+ defer w.mu.Unlock()
- // inotify will automatically remove the watch on deletes; just need
- // to clean our state here.
- if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
- w.watches.remove(watch.wd)
- }
+ /// If the event happened to the watched directory or the watched file, the
+ /// kernel doesn't append the filename to the event, but we would like to
+ /// always fill the the "Name" field with a valid filename. We retrieve the
+ /// path of the watch from the "paths" map.
+ ///
+ /// Can be nil if Remove() was called in another goroutine for this path
+ /// inbetween reading the events from the kernel and reading the internal
+ /// state. Not much we can do about it, so just skip. See #616.
+ watch := w.watches.byWd(uint32(inEvent.Wd))
+ if watch == nil {
+ return Event{}, true
+ }
- // We can't really update the state when a watched path is moved;
- // only IN_MOVE_SELF is sent and not IN_MOVED_{FROM,TO}. So remove
- // the watch.
- if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF {
- if watch.recurse {
- next() // Do nothing
- continue
- }
+ var (
+ name = watch.path
+ nameLen = uint32(inEvent.Len)
+ )
+ if nameLen > 0 {
+ /// Point "bytes" at the first byte of the filename
+ bb := *buf
+ bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&bb[offset+unix.SizeofInotifyEvent]))[:nameLen:nameLen]
+ /// The filename is padded with NULL bytes. TrimRight() gets rid of those.
+ name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\x00")
+ }
- err := w.remove(watch.path)
- if err != nil && !errors.Is(err, ErrNonExistentWatch) {
- if !w.sendError(err) {
- return
- }
- }
+ if debug {
+ internal.Debug(name, inEvent.Mask, inEvent.Cookie)
+ }
+
+ if inEvent.Mask&unix.IN_IGNORED != 0 || inEvent.Mask&unix.IN_UNMOUNT != 0 {
+ w.watches.remove(watch)
+ return Event{}, true
+ }
+
+ // inotify will automatically remove the watch on deletes; just need
+ // to clean our state here.
+ if inEvent.Mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
+ w.watches.remove(watch)
+ }
+
+ // We can't really update the state when a watched path is moved; only
+ // IN_MOVE_SELF is sent and not IN_MOVED_{FROM,TO}. So remove the watch.
+ if inEvent.Mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF {
+ if watch.recurse { // Do nothing
+ return Event{}, true
+ }
+
+ err := w.remove(watch.path)
+ if err != nil && !errors.Is(err, ErrNonExistentWatch) {
+ if !w.sendError(err) {
+ return Event{}, false
}
+ }
+ }
- /// Skip if we're watching both this path and the parent; the parent
- /// will already send a delete so no need to do it twice.
- if mask&unix.IN_DELETE_SELF != 0 {
- if _, ok := w.watches.path[filepath.Dir(watch.path)]; ok {
- next()
- continue
- }
+ /// Skip if we're watching both this path and the parent; the parent will
+ /// already send a delete so no need to do it twice.
+ if inEvent.Mask&unix.IN_DELETE_SELF != 0 {
+ _, ok := w.watches.path[filepath.Dir(watch.path)]
+ if ok {
+ return Event{}, true
+ }
+ }
+
+ ev := w.newEvent(name, inEvent.Mask, inEvent.Cookie)
+ // Need to update watch path for recurse.
+ if watch.recurse {
+ isDir := inEvent.Mask&unix.IN_ISDIR == unix.IN_ISDIR
+ /// New directory created: set up watch on it.
+ if isDir && ev.Has(Create) {
+ err := w.register(ev.Name, watch.flags, true)
+ if !w.sendError(err) {
+ return Event{}, false
}
- ev := w.newEvent(name, mask, raw.Cookie)
- // Need to update watch path for recurse.
- if watch.recurse {
- isDir := mask&unix.IN_ISDIR == unix.IN_ISDIR
- /// New directory created: set up watch on it.
- if isDir && ev.Has(Create) {
- err := w.register(ev.Name, watch.flags, true)
- if !w.sendError(err) {
- return
+ // This was a directory rename, so we need to update all the
+ // children.
+ //
+ // TODO: this is of course pretty slow; we should use a better data
+ // structure for storing all of this, e.g. store children in the
+ // watch. I have some code for this in my kqueue refactor we can use
+ // in the future. For now I'm okay with this as it's not publicly
+ // available. Correctness first, performance second.
+ if ev.renamedFrom != "" {
+ for k, ww := range w.watches.wd {
+ if k == watch.wd || ww.path == ev.Name {
+ continue
}
-
- // This was a directory rename, so we need to update all
- // the children.
- //
- // TODO: this is of course pretty slow; we should use a
- // better data structure for storing all of this, e.g. store
- // children in the watch. I have some code for this in my
- // kqueue refactor we can use in the future. For now I'm
- // okay with this as it's not publicly available.
- // Correctness first, performance second.
- if ev.renamedFrom != "" {
- w.watches.mu.Lock()
- for k, ww := range w.watches.wd {
- if k == watch.wd || ww.path == ev.Name {
- continue
- }
- if strings.HasPrefix(ww.path, ev.renamedFrom) {
- ww.path = strings.Replace(ww.path, ev.renamedFrom, ev.Name, 1)
- w.watches.wd[k] = ww
- }
- }
- w.watches.mu.Unlock()
+ if strings.HasPrefix(ww.path, ev.renamedFrom) {
+ ww.path = strings.Replace(ww.path, ev.renamedFrom, ev.Name, 1)
+ w.watches.wd[k] = ww
}
}
}
-
- /// Send the events that are not ignored on the events channel
- if !w.sendEvent(ev) {
- return
- }
- next()
}
}
+
+ return ev, true
}
func (w *inotify) isRecursive(path string) bool {
@@ -650,8 +575,8 @@ func (w *inotify) xSupports(op Op) bool {
}
func (w *inotify) state() {
- w.watches.mu.Lock()
- defer w.watches.mu.Unlock()
+ w.mu.Lock()
+ defer w.mu.Unlock()
for wd, ww := range w.watches.wd {
fmt.Fprintf(os.Stderr, "%4d: recurse=%t %q\n", wd, ww.recurse, ww.path)
}
diff --git a/vendor/github.com/fsnotify/fsnotify/backend_kqueue.go b/vendor/github.com/fsnotify/fsnotify/backend_kqueue.go
index d8de5ab76..340aeec06 100644
--- a/vendor/github.com/fsnotify/fsnotify/backend_kqueue.go
+++ b/vendor/github.com/fsnotify/fsnotify/backend_kqueue.go
@@ -16,14 +16,13 @@ import (
)
type kqueue struct {
+ *shared
Events chan Event
Errors chan error
kq int // File descriptor (as returned by the kqueue() syscall).
closepipe [2]int // Pipe used for closing kq.
watches *watches
- done chan struct{}
- doneMu sync.Mutex
}
type (
@@ -132,14 +131,18 @@ func (w *watches) byPath(path string) (watch, bool) {
return info, ok
}
-func (w *watches) updateDirFlags(path string, flags uint32) {
+func (w *watches) updateDirFlags(path string, flags uint32) bool {
w.mu.Lock()
defer w.mu.Unlock()
- fd := w.path[path]
+ fd, ok := w.path[path]
+ if !ok { // Already deleted: don't re-set it here.
+ return false
+ }
info := w.wd[fd]
info.dirFlags = flags
w.wd[fd] = info
+ return true
}
func (w *watches) remove(fd int, path string) bool {
@@ -179,22 +182,20 @@ func (w *watches) seenBefore(path string) bool {
return ok
}
-func newBackend(ev chan Event, errs chan error) (backend, error) {
- return newBufferedBackend(0, ev, errs)
-}
+var defaultBufferSize = 0
-func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error) {
+func newBackend(ev chan Event, errs chan error) (backend, error) {
kq, closepipe, err := newKqueue()
if err != nil {
return nil, err
}
w := &kqueue{
+ shared: newShared(ev, errs),
Events: ev,
Errors: errs,
kq: kq,
closepipe: closepipe,
- done: make(chan struct{}),
watches: newWatches(),
}
@@ -210,7 +211,7 @@ func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error
// all.
func newKqueue() (kq int, closepipe [2]int, err error) {
kq, err = unix.Kqueue()
- if kq == -1 {
+ if err != nil {
return kq, closepipe, err
}
@@ -239,54 +240,17 @@ func newKqueue() (kq int, closepipe [2]int, err error) {
return kq, closepipe, nil
}
-// Returns true if the event was sent, or false if watcher is closed.
-func (w *kqueue) sendEvent(e Event) bool {
- select {
- case <-w.done:
- return false
- case w.Events <- e:
- return true
- }
-}
-
-// Returns true if the error was sent, or false if watcher is closed.
-func (w *kqueue) sendError(err error) bool {
- if err == nil {
- return true
- }
- select {
- case <-w.done:
- return false
- case w.Errors <- err:
- return true
- }
-}
-
-func (w *kqueue) isClosed() bool {
- select {
- case <-w.done:
- return true
- default:
- return false
- }
-}
-
func (w *kqueue) Close() error {
- w.doneMu.Lock()
- if w.isClosed() {
- w.doneMu.Unlock()
+ if w.shared.close() {
return nil
}
- close(w.done)
- w.doneMu.Unlock()
pathsToRemove := w.watches.listPaths(false)
for _, name := range pathsToRemove {
w.Remove(name)
}
- // Send "quit" message to the reader goroutine.
- unix.Close(w.closepipe[1])
+ unix.Close(w.closepipe[1]) // Send "quit" message to readEvents
return nil
}
@@ -303,7 +267,7 @@ func (w *kqueue) AddWith(name string, opts ...addOpt) error {
return fmt.Errorf("%w: %s", xErrUnsupported, with.op)
}
- _, err := w.addWatch(name, noteAllEvents)
+ _, err := w.addWatch(name, noteAllEvents, false)
if err != nil {
return err
}
@@ -366,7 +330,7 @@ const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | un
// described in kevent(2).
//
// Returns the real path to the file which was added, with symlinks resolved.
-func (w *kqueue) addWatch(name string, flags uint32) (string, error) {
+func (w *kqueue) addWatch(name string, flags uint32, listDir bool) (string, error) {
if w.isClosed() {
return "", ErrClosed
}
@@ -385,15 +349,15 @@ func (w *kqueue) addWatch(name string, flags uint32) (string, error) {
return "", nil
}
- // Follow symlinks.
- if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
+ // Follow symlinks, but only for paths added with Add(), and not paths
+ // we're adding from internalWatch from a listdir.
+ if !listDir && fi.Mode()&os.ModeSymlink == os.ModeSymlink {
link, err := os.Readlink(name)
if err != nil {
- // Return nil because Linux can add unresolvable symlinks to the
- // watch list without problems, so maintain consistency with
- // that. There will be no file events for broken symlinks.
- // TODO: more specific check; returns os.PathError; ENOENT?
- return "", nil
+ return "", err
+ }
+ if !filepath.IsAbs(link) {
+ link = filepath.Join(filepath.Dir(name), link)
}
_, alreadyWatching = w.watches.byPath(link)
@@ -408,7 +372,7 @@ func (w *kqueue) addWatch(name string, flags uint32) (string, error) {
name = link
fi, err = os.Lstat(name)
if err != nil {
- return "", nil
+ return "", err
}
}
@@ -422,7 +386,6 @@ func (w *kqueue) addWatch(name string, flags uint32) (string, error) {
if errors.Is(err, unix.EINTR) {
continue
}
-
return "", err
}
@@ -444,10 +407,16 @@ func (w *kqueue) addWatch(name string, flags uint32) (string, error) {
if info.isDir {
watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE &&
(!alreadyWatching || (info.dirFlags&unix.NOTE_WRITE) != unix.NOTE_WRITE)
- w.watches.updateDirFlags(name, flags)
+ if !w.watches.updateDirFlags(name, flags) {
+ return "", nil
+ }
if watchDir {
- if err := w.watchDirectoryFiles(name); err != nil {
+ d := name
+ if info.linkName != "" {
+ d = info.linkName
+ }
+ if err := w.watchDirectoryFiles(d); err != nil {
return "", err
}
}
@@ -644,19 +613,22 @@ func (w *kqueue) dirChange(dir string) error {
if errors.Is(err, os.ErrNotExist) {
return nil
}
- return fmt.Errorf("fsnotify.dirChange: %w", err)
+ return fmt.Errorf("fsnotify.dirChange %q: %w", dir, err)
}
for _, f := range files {
fi, err := f.Info()
if err != nil {
+ if errors.Is(err, os.ErrNotExist) {
+ return nil
+ }
return fmt.Errorf("fsnotify.dirChange: %w", err)
}
err = w.sendCreateIfNew(filepath.Join(dir, fi.Name()), fi)
if err != nil {
// Don't need to send an error if this file isn't readable.
- if errors.Is(err, unix.EACCES) || errors.Is(err, unix.EPERM) {
+ if errors.Is(err, unix.EACCES) || errors.Is(err, unix.EPERM) || errors.Is(err, os.ErrNotExist) {
return nil
}
return fmt.Errorf("fsnotify.dirChange: %w", err)
@@ -688,11 +660,11 @@ func (w *kqueue) internalWatch(name string, fi os.FileInfo) (string, error) {
// mimic Linux providing delete events for subdirectories, but preserve
// the flags used if currently watching subdirectory
info, _ := w.watches.byPath(name)
- return w.addWatch(name, info.dirFlags|unix.NOTE_DELETE|unix.NOTE_RENAME)
+ return w.addWatch(name, info.dirFlags|unix.NOTE_DELETE|unix.NOTE_RENAME, true)
}
- // watch file to mimic Linux inotify
- return w.addWatch(name, noteAllEvents)
+ // Watch file to mimic Linux inotify.
+ return w.addWatch(name, noteAllEvents, true)
}
// Register events with the queue.
@@ -722,9 +694,9 @@ func (w *kqueue) read(events []unix.Kevent_t) ([]unix.Kevent_t, error) {
}
func (w *kqueue) xSupports(op Op) bool {
- if runtime.GOOS == "freebsd" {
- //return true // Supports everything.
- }
+ //if runtime.GOOS == "freebsd" {
+ // return true // Supports everything.
+ //}
if op.Has(xUnportableOpen) || op.Has(xUnportableRead) ||
op.Has(xUnportableCloseWrite) || op.Has(xUnportableCloseRead) {
return false
diff --git a/vendor/github.com/fsnotify/fsnotify/backend_other.go b/vendor/github.com/fsnotify/fsnotify/backend_other.go
index 5eb5dbc66..b8c0ad722 100644
--- a/vendor/github.com/fsnotify/fsnotify/backend_other.go
+++ b/vendor/github.com/fsnotify/fsnotify/backend_other.go
@@ -9,12 +9,11 @@ type other struct {
Errors chan error
}
+var defaultBufferSize = 0
+
func newBackend(ev chan Event, errs chan error) (backend, error) {
return nil, errors.New("fsnotify not supported on the current platform")
}
-func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error) {
- return newBackend(ev, errs)
-}
func (w *other) Close() error { return nil }
func (w *other) WatchList() []string { return nil }
func (w *other) Add(name string) error { return nil }
diff --git a/vendor/github.com/fsnotify/fsnotify/backend_windows.go b/vendor/github.com/fsnotify/fsnotify/backend_windows.go
index c54a63083..3433642d6 100644
--- a/vendor/github.com/fsnotify/fsnotify/backend_windows.go
+++ b/vendor/github.com/fsnotify/fsnotify/backend_windows.go
@@ -28,18 +28,16 @@ type readDirChangesW struct {
port windows.Handle // Handle to completion port
input chan *input // Inputs to the reader are sent on this channel
- quit chan chan<- error
+ done chan chan<- error
mu sync.Mutex // Protects access to watches, closed
watches watchMap // Map of watches (key: i-number)
closed bool // Set to true when Close() is first called
}
-func newBackend(ev chan Event, errs chan error) (backend, error) {
- return newBufferedBackend(50, ev, errs)
-}
+var defaultBufferSize = 50
-func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error) {
+func newBackend(ev chan Event, errs chan error) (backend, error) {
port, err := windows.CreateIoCompletionPort(windows.InvalidHandle, 0, 0, 0)
if err != nil {
return nil, os.NewSyscallError("CreateIoCompletionPort", err)
@@ -50,7 +48,7 @@ func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error
port: port,
watches: make(watchMap),
input: make(chan *input, 1),
- quit: make(chan chan<- error, 1),
+ done: make(chan chan<- error, 1),
}
go w.readEvents()
return w, nil
@@ -70,8 +68,8 @@ func (w *readDirChangesW) sendEvent(name, renamedFrom string, mask uint64) bool
event := w.newEvent(name, uint32(mask))
event.renamedFrom = renamedFrom
select {
- case ch := <-w.quit:
- w.quit <- ch
+ case ch := <-w.done:
+ w.done <- ch
case w.Events <- event:
}
return true
@@ -83,10 +81,10 @@ func (w *readDirChangesW) sendError(err error) bool {
return true
}
select {
+ case <-w.done:
+ return false
case w.Errors <- err:
return true
- case <-w.quit:
- return false
}
}
@@ -99,9 +97,9 @@ func (w *readDirChangesW) Close() error {
w.closed = true
w.mu.Unlock()
- // Send "quit" message to the reader goroutine
+ // Send "done" message to the reader goroutine
ch := make(chan error)
- w.quit <- ch
+ w.done <- ch
if err := w.wakeupReader(); err != nil {
return err
}
@@ -495,7 +493,7 @@ func (w *readDirChangesW) readEvents() {
watch := (*watch)(unsafe.Pointer(ov))
if watch == nil {
select {
- case ch := <-w.quit:
+ case ch := <-w.done:
w.mu.Lock()
var indexes []indexMap
for _, index := range w.watches {
diff --git a/vendor/github.com/fsnotify/fsnotify/fsnotify.go b/vendor/github.com/fsnotify/fsnotify/fsnotify.go
index 0760efe91..f64be4bf9 100644
--- a/vendor/github.com/fsnotify/fsnotify/fsnotify.go
+++ b/vendor/github.com/fsnotify/fsnotify/fsnotify.go
@@ -244,12 +244,13 @@ var (
// ErrUnsupported is returned by AddWith() when WithOps() specified an
// Unportable event that's not supported on this platform.
+ //lint:ignore ST1012 not relevant
xErrUnsupported = errors.New("fsnotify: not supported with this backend")
)
// NewWatcher creates a new Watcher.
func NewWatcher() (*Watcher, error) {
- ev, errs := make(chan Event), make(chan error)
+ ev, errs := make(chan Event, defaultBufferSize), make(chan error)
b, err := newBackend(ev, errs)
if err != nil {
return nil, err
@@ -266,8 +267,8 @@ func NewWatcher() (*Watcher, error) {
// cases, and whenever possible you will be better off increasing the kernel
// buffers instead of adding a large userspace buffer.
func NewBufferedWatcher(sz uint) (*Watcher, error) {
- ev, errs := make(chan Event), make(chan error)
- b, err := newBufferedBackend(sz, ev, errs)
+ ev, errs := make(chan Event, sz), make(chan error)
+ b, err := newBackend(ev, errs)
if err != nil {
return nil, err
}
@@ -337,7 +338,8 @@ func (w *Watcher) Close() error { return w.b.Close() }
// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
// yet removed).
//
-// Returns nil if [Watcher.Close] was called.
+// The order is undefined, and may differ per call. Returns nil if
+// [Watcher.Close] was called.
func (w *Watcher) WatchList() []string { return w.b.WatchList() }
// Supports reports if all the listed operations are supported by this platform.
diff --git a/vendor/github.com/fsnotify/fsnotify/internal/darwin.go b/vendor/github.com/fsnotify/fsnotify/internal/darwin.go
index b0eab1009..0b01bc182 100644
--- a/vendor/github.com/fsnotify/fsnotify/internal/darwin.go
+++ b/vendor/github.com/fsnotify/fsnotify/internal/darwin.go
@@ -9,14 +9,14 @@ import (
)
var (
- SyscallEACCES = syscall.EACCES
- UnixEACCES = unix.EACCES
+ ErrSyscallEACCES = syscall.EACCES
+ ErrUnixEACCES = unix.EACCES
)
var maxfiles uint64
-// Go 1.19 will do this automatically: https://go-review.googlesource.com/c/go/+/393354/
func SetRlimit() {
+ // Go 1.19 will do this automatically: https://go-review.googlesource.com/c/go/+/393354/
var l syscall.Rlimit
err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &l)
if err == nil && l.Cur != l.Max {
diff --git a/vendor/github.com/fsnotify/fsnotify/internal/freebsd.go b/vendor/github.com/fsnotify/fsnotify/internal/freebsd.go
index 547df1df8..5ac8b5079 100644
--- a/vendor/github.com/fsnotify/fsnotify/internal/freebsd.go
+++ b/vendor/github.com/fsnotify/fsnotify/internal/freebsd.go
@@ -9,8 +9,8 @@ import (
)
var (
- SyscallEACCES = syscall.EACCES
- UnixEACCES = unix.EACCES
+ ErrSyscallEACCES = syscall.EACCES
+ ErrUnixEACCES = unix.EACCES
)
var maxfiles uint64
diff --git a/vendor/github.com/fsnotify/fsnotify/internal/unix.go b/vendor/github.com/fsnotify/fsnotify/internal/unix.go
index 30976ce97..b251fb803 100644
--- a/vendor/github.com/fsnotify/fsnotify/internal/unix.go
+++ b/vendor/github.com/fsnotify/fsnotify/internal/unix.go
@@ -1,4 +1,4 @@
-//go:build !windows && !darwin && !freebsd
+//go:build !windows && !darwin && !freebsd && !plan9
package internal
@@ -9,8 +9,8 @@ import (
)
var (
- SyscallEACCES = syscall.EACCES
- UnixEACCES = unix.EACCES
+ ErrSyscallEACCES = syscall.EACCES
+ ErrUnixEACCES = unix.EACCES
)
var maxfiles uint64
diff --git a/vendor/github.com/fsnotify/fsnotify/internal/windows.go b/vendor/github.com/fsnotify/fsnotify/internal/windows.go
index a72c64954..896bc2e5a 100644
--- a/vendor/github.com/fsnotify/fsnotify/internal/windows.go
+++ b/vendor/github.com/fsnotify/fsnotify/internal/windows.go
@@ -10,8 +10,8 @@ import (
// Just a dummy.
var (
- SyscallEACCES = errors.New("dummy")
- UnixEACCES = errors.New("dummy")
+ ErrSyscallEACCES = errors.New("dummy")
+ ErrUnixEACCES = errors.New("dummy")
)
func SetRlimit() {}
diff --git a/vendor/github.com/fsnotify/fsnotify/shared.go b/vendor/github.com/fsnotify/fsnotify/shared.go
new file mode 100644
index 000000000..3ee9b58f1
--- /dev/null
+++ b/vendor/github.com/fsnotify/fsnotify/shared.go
@@ -0,0 +1,64 @@
+package fsnotify
+
+import "sync"
+
+type shared struct {
+ Events chan Event
+ Errors chan error
+ done chan struct{}
+ mu sync.Mutex
+}
+
+func newShared(ev chan Event, errs chan error) *shared {
+ return &shared{
+ Events: ev,
+ Errors: errs,
+ done: make(chan struct{}),
+ }
+}
+
+// Returns true if the event was sent, or false if watcher is closed.
+func (w *shared) sendEvent(e Event) bool {
+ if e.Op == 0 {
+ return true
+ }
+ select {
+ case <-w.done:
+ return false
+ case w.Events <- e:
+ return true
+ }
+}
+
+// Returns true if the error was sent, or false if watcher is closed.
+func (w *shared) sendError(err error) bool {
+ if err == nil {
+ return true
+ }
+ select {
+ case <-w.done:
+ return false
+ case w.Errors <- err:
+ return true
+ }
+}
+
+func (w *shared) isClosed() bool {
+ select {
+ case <-w.done:
+ return true
+ default:
+ return false
+ }
+}
+
+// Mark as closed; returns true if it was already closed.
+func (w *shared) close() bool {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ if w.isClosed() {
+ return true
+ }
+ close(w.done)
+ return false
+}
diff --git a/vendor/github.com/fsnotify/fsnotify/staticcheck.conf b/vendor/github.com/fsnotify/fsnotify/staticcheck.conf
new file mode 100644
index 000000000..8fa7351f0
--- /dev/null
+++ b/vendor/github.com/fsnotify/fsnotify/staticcheck.conf
@@ -0,0 +1,3 @@
+checks = ['all',
+ '-U1000', # Don't complain about unused functions.
+]
diff --git a/vendor/github.com/go-viper/mapstructure/v2/.editorconfig b/vendor/github.com/go-viper/mapstructure/v2/.editorconfig
index 1f664d13a..faef0c91e 100644
--- a/vendor/github.com/go-viper/mapstructure/v2/.editorconfig
+++ b/vendor/github.com/go-viper/mapstructure/v2/.editorconfig
@@ -16,3 +16,6 @@ indent_style = tab
[*.nix]
indent_size = 2
+
+[.golangci.yaml]
+indent_size = 2
diff --git a/vendor/github.com/go-viper/mapstructure/v2/.golangci.yaml b/vendor/github.com/go-viper/mapstructure/v2/.golangci.yaml
index 763143aa7..bda962566 100644
--- a/vendor/github.com/go-viper/mapstructure/v2/.golangci.yaml
+++ b/vendor/github.com/go-viper/mapstructure/v2/.golangci.yaml
@@ -1,23 +1,48 @@
-run:
- timeout: 5m
+version: "2"
-linters-settings:
- gci:
- sections:
- - standard
- - default
- - prefix(github.com/go-viper/mapstructure)
- golint:
- min-confidence: 0
- goimports:
- local-prefixes: github.com/go-viper/maptstructure
+run:
+ timeout: 10m
linters:
- disable-all: true
+ enable:
+ - govet
+ - ineffassign
+ # - misspell
+ - nolintlint
+ # - revive
+
+ disable:
+ - errcheck
+ - staticcheck
+ - unused
+
+ settings:
+ misspell:
+ locale: US
+ nolintlint:
+ allow-unused: false # report any unused nolint directives
+ require-specific: false # don't require nolint directives to be specific about which linter is being skipped
+
+formatters:
enable:
- gci
- gofmt
- gofumpt
- goimports
- - staticcheck
- # - stylecheck
+ # - golines
+
+ settings:
+ gci:
+ sections:
+ - standard
+ - default
+ - localmodule
+ gofmt:
+ simplify: true
+ rewrite-rules:
+ - pattern: interface{}
+ replacement: any
+
+ exclusions:
+ paths:
+ - internal/
diff --git a/vendor/github.com/go-viper/mapstructure/v2/README.md b/vendor/github.com/go-viper/mapstructure/v2/README.md
index dd5ec69dd..45db71975 100644
--- a/vendor/github.com/go-viper/mapstructure/v2/README.md
+++ b/vendor/github.com/go-viper/mapstructure/v2/README.md
@@ -1,8 +1,9 @@
# mapstructure
-[](https://github.com/go-viper/mapstructure/actions?query=workflow%3ACI)
+[](https://github.com/go-viper/mapstructure/actions/workflows/ci.yaml)
[](https://pkg.go.dev/mod/github.com/go-viper/mapstructure/v2)
-
+
+[](https://deps.dev/go/github.com%252Fgo-viper%252Fmapstructure%252Fv2)
mapstructure is a Go library for decoding generic map values to structures
and vice versa, while providing helpful error handling.
@@ -29,7 +30,7 @@ The API is the same, so you don't need to change anything else.
Here is a script that can help you with the migration:
```shell
-sed -i 's/github.com\/mitchellh\/mapstructure/github.com\/go-viper\/mapstructure\/v2/g' $(find . -type f -name '*.go')
+sed -i 's|github.com/mitchellh/mapstructure|github.com/go-viper/mapstructure/v2|g' $(find . -type f -name '*.go')
```
If you need more time to migrate your code, that is absolutely fine.
diff --git a/vendor/github.com/go-viper/mapstructure/v2/decode_hooks.go b/vendor/github.com/go-viper/mapstructure/v2/decode_hooks.go
index 1f3c69d4b..a852a0a04 100644
--- a/vendor/github.com/go-viper/mapstructure/v2/decode_hooks.go
+++ b/vendor/github.com/go-viper/mapstructure/v2/decode_hooks.go
@@ -13,7 +13,7 @@ import (
"time"
)
-// typedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns
+// typedDecodeHook takes a raw DecodeHookFunc (an any) and turns
// it into the proper DecodeHookFunc type, such as DecodeHookFuncType.
func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
// Create variables here so we can reference them with the reflect pkg
@@ -23,7 +23,7 @@ func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
// Fill in the variables into this interface and the rest is done
// automatically using the reflect package.
- potential := []interface{}{f1, f2, f3}
+ potential := []any{f1, f2, f3}
v := reflect.ValueOf(h)
vt := v.Type()
@@ -37,25 +37,25 @@ func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
return nil
}
-// cachedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns
+// cachedDecodeHook takes a raw DecodeHookFunc (an any) and turns
// it into a closure to be used directly
// if the type fails to convert we return a closure always erroring to keep the previous behaviour
-func cachedDecodeHook(raw DecodeHookFunc) func(from reflect.Value, to reflect.Value) (interface{}, error) {
+func cachedDecodeHook(raw DecodeHookFunc) func(from reflect.Value, to reflect.Value) (any, error) {
switch f := typedDecodeHook(raw).(type) {
case DecodeHookFuncType:
- return func(from reflect.Value, to reflect.Value) (interface{}, error) {
+ return func(from reflect.Value, to reflect.Value) (any, error) {
return f(from.Type(), to.Type(), from.Interface())
}
case DecodeHookFuncKind:
- return func(from reflect.Value, to reflect.Value) (interface{}, error) {
+ return func(from reflect.Value, to reflect.Value) (any, error) {
return f(from.Kind(), to.Kind(), from.Interface())
}
case DecodeHookFuncValue:
- return func(from reflect.Value, to reflect.Value) (interface{}, error) {
+ return func(from reflect.Value, to reflect.Value) (any, error) {
return f(from, to)
}
default:
- return func(from reflect.Value, to reflect.Value) (interface{}, error) {
+ return func(from reflect.Value, to reflect.Value) (any, error) {
return nil, errors.New("invalid decode hook signature")
}
}
@@ -67,7 +67,7 @@ func cachedDecodeHook(raw DecodeHookFunc) func(from reflect.Value, to reflect.Va
func DecodeHookExec(
raw DecodeHookFunc,
from reflect.Value, to reflect.Value,
-) (interface{}, error) {
+) (any, error) {
switch f := typedDecodeHook(raw).(type) {
case DecodeHookFuncType:
return f(from.Type(), to.Type(), from.Interface())
@@ -86,11 +86,11 @@ func DecodeHookExec(
// The composed funcs are called in order, with the result of the
// previous transformation.
func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
- cached := make([]func(from reflect.Value, to reflect.Value) (interface{}, error), 0, len(fs))
+ cached := make([]func(from reflect.Value, to reflect.Value) (any, error), 0, len(fs))
for _, f := range fs {
cached = append(cached, cachedDecodeHook(f))
}
- return func(f reflect.Value, t reflect.Value) (interface{}, error) {
+ return func(f reflect.Value, t reflect.Value) (any, error) {
var err error
data := f.Interface()
@@ -100,7 +100,11 @@ func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
if err != nil {
return nil, err
}
- newFrom = reflect.ValueOf(data)
+ if v, ok := data.(reflect.Value); ok {
+ newFrom = v
+ } else {
+ newFrom = reflect.ValueOf(data)
+ }
}
return data, nil
@@ -110,13 +114,13 @@ func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
// OrComposeDecodeHookFunc executes all input hook functions until one of them returns no error. In that case its value is returned.
// If all hooks return an error, OrComposeDecodeHookFunc returns an error concatenating all error messages.
func OrComposeDecodeHookFunc(ff ...DecodeHookFunc) DecodeHookFunc {
- cached := make([]func(from reflect.Value, to reflect.Value) (interface{}, error), 0, len(ff))
+ cached := make([]func(from reflect.Value, to reflect.Value) (any, error), 0, len(ff))
for _, f := range ff {
cached = append(cached, cachedDecodeHook(f))
}
- return func(a, b reflect.Value) (interface{}, error) {
+ return func(a, b reflect.Value) (any, error) {
var allErrs string
- var out interface{}
+ var out any
var err error
for _, c := range cached {
@@ -139,8 +143,8 @@ func StringToSliceHookFunc(sep string) DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
- data interface{},
- ) (interface{}, error) {
+ data any,
+ ) (any, error) {
if f.Kind() != reflect.String {
return data, nil
}
@@ -157,14 +161,37 @@ func StringToSliceHookFunc(sep string) DecodeHookFunc {
}
}
+// StringToWeakSliceHookFunc brings back the old (pre-v2) behavior of [StringToSliceHookFunc].
+//
+// As of mapstructure v2.0.0 [StringToSliceHookFunc] checks if the return type is a string slice.
+// This function removes that check.
+func StringToWeakSliceHookFunc(sep string) DecodeHookFunc {
+ return func(
+ f reflect.Type,
+ t reflect.Type,
+ data any,
+ ) (any, error) {
+ if f.Kind() != reflect.String || t.Kind() != reflect.Slice {
+ return data, nil
+ }
+
+ raw := data.(string)
+ if raw == "" {
+ return []string{}, nil
+ }
+
+ return strings.Split(raw, sep), nil
+ }
+}
+
// StringToTimeDurationHookFunc returns a DecodeHookFunc that converts
// strings to time.Duration.
func StringToTimeDurationHookFunc() DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
- data interface{},
- ) (interface{}, error) {
+ data any,
+ ) (any, error) {
if f.Kind() != reflect.String {
return data, nil
}
@@ -173,7 +200,29 @@ func StringToTimeDurationHookFunc() DecodeHookFunc {
}
// Convert it by parsing
- return time.ParseDuration(data.(string))
+ d, err := time.ParseDuration(data.(string))
+
+ return d, wrapTimeParseDurationError(err)
+ }
+}
+
+// StringToTimeLocationHookFunc returns a DecodeHookFunc that converts
+// strings to *time.Location.
+func StringToTimeLocationHookFunc() DecodeHookFunc {
+ return func(
+ f reflect.Type,
+ t reflect.Type,
+ data any,
+ ) (any, error) {
+ if f.Kind() != reflect.String {
+ return data, nil
+ }
+ if t != reflect.TypeOf(time.Local) {
+ return data, nil
+ }
+ d, err := time.LoadLocation(data.(string))
+
+ return d, wrapTimeParseLocationError(err)
}
}
@@ -183,8 +232,8 @@ func StringToURLHookFunc() DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
- data interface{},
- ) (interface{}, error) {
+ data any,
+ ) (any, error) {
if f.Kind() != reflect.String {
return data, nil
}
@@ -193,7 +242,9 @@ func StringToURLHookFunc() DecodeHookFunc {
}
// Convert it by parsing
- return url.Parse(data.(string))
+ u, err := url.Parse(data.(string))
+
+ return u, wrapUrlError(err)
}
}
@@ -203,8 +254,8 @@ func StringToIPHookFunc() DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
- data interface{},
- ) (interface{}, error) {
+ data any,
+ ) (any, error) {
if f.Kind() != reflect.String {
return data, nil
}
@@ -215,7 +266,7 @@ func StringToIPHookFunc() DecodeHookFunc {
// Convert it by parsing
ip := net.ParseIP(data.(string))
if ip == nil {
- return net.IP{}, fmt.Errorf("failed parsing ip %v", data)
+ return net.IP{}, fmt.Errorf("failed parsing ip")
}
return ip, nil
@@ -228,8 +279,8 @@ func StringToIPNetHookFunc() DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
- data interface{},
- ) (interface{}, error) {
+ data any,
+ ) (any, error) {
if f.Kind() != reflect.String {
return data, nil
}
@@ -239,7 +290,7 @@ func StringToIPNetHookFunc() DecodeHookFunc {
// Convert it by parsing
_, net, err := net.ParseCIDR(data.(string))
- return net, err
+ return net, wrapNetParseError(err)
}
}
@@ -249,8 +300,8 @@ func StringToTimeHookFunc(layout string) DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
- data interface{},
- ) (interface{}, error) {
+ data any,
+ ) (any, error) {
if f.Kind() != reflect.String {
return data, nil
}
@@ -259,7 +310,9 @@ func StringToTimeHookFunc(layout string) DecodeHookFunc {
}
// Convert it by parsing
- return time.Parse(layout, data.(string))
+ ti, err := time.Parse(layout, data.(string))
+
+ return ti, wrapTimeParseError(err)
}
}
@@ -271,8 +324,8 @@ func StringToTimeHookFunc(layout string) DecodeHookFunc {
func WeaklyTypedHook(
f reflect.Kind,
t reflect.Kind,
- data interface{},
-) (interface{}, error) {
+ data any,
+) (any, error) {
dataVal := reflect.ValueOf(data)
switch t {
case reflect.String:
@@ -301,17 +354,17 @@ func WeaklyTypedHook(
}
func RecursiveStructToMapHookFunc() DecodeHookFunc {
- return func(f reflect.Value, t reflect.Value) (interface{}, error) {
+ return func(f reflect.Value, t reflect.Value) (any, error) {
if f.Kind() != reflect.Struct {
return f.Interface(), nil
}
- var i interface{} = struct{}{}
+ var i any = struct{}{}
if t.Type() != reflect.TypeOf(&i).Elem() {
return f.Interface(), nil
}
- m := make(map[string]interface{})
+ m := make(map[string]any)
t.Set(reflect.ValueOf(m))
return f.Interface(), nil
@@ -325,8 +378,8 @@ func TextUnmarshallerHookFunc() DecodeHookFuncType {
return func(
f reflect.Type,
t reflect.Type,
- data interface{},
- ) (interface{}, error) {
+ data any,
+ ) (any, error) {
if f.Kind() != reflect.String {
return data, nil
}
@@ -352,8 +405,8 @@ func StringToNetIPAddrHookFunc() DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
- data interface{},
- ) (interface{}, error) {
+ data any,
+ ) (any, error) {
if f.Kind() != reflect.String {
return data, nil
}
@@ -362,7 +415,9 @@ func StringToNetIPAddrHookFunc() DecodeHookFunc {
}
// Convert it by parsing
- return netip.ParseAddr(data.(string))
+ addr, err := netip.ParseAddr(data.(string))
+
+ return addr, wrapNetIPParseAddrError(err)
}
}
@@ -372,8 +427,8 @@ func StringToNetIPAddrPortHookFunc() DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
- data interface{},
- ) (interface{}, error) {
+ data any,
+ ) (any, error) {
if f.Kind() != reflect.String {
return data, nil
}
@@ -382,7 +437,31 @@ func StringToNetIPAddrPortHookFunc() DecodeHookFunc {
}
// Convert it by parsing
- return netip.ParseAddrPort(data.(string))
+ addrPort, err := netip.ParseAddrPort(data.(string))
+
+ return addrPort, wrapNetIPParseAddrPortError(err)
+ }
+}
+
+// StringToNetIPPrefixHookFunc returns a DecodeHookFunc that converts
+// strings to netip.Prefix.
+func StringToNetIPPrefixHookFunc() DecodeHookFunc {
+ return func(
+ f reflect.Type,
+ t reflect.Type,
+ data any,
+ ) (any, error) {
+ if f.Kind() != reflect.String {
+ return data, nil
+ }
+ if t != reflect.TypeOf(netip.Prefix{}) {
+ return data, nil
+ }
+
+ // Convert it by parsing
+ prefix, err := netip.ParsePrefix(data.(string))
+
+ return prefix, wrapNetIPParsePrefixError(err)
}
}
@@ -415,178 +494,182 @@ func StringToBasicTypeHookFunc() DecodeHookFunc {
// StringToInt8HookFunc returns a DecodeHookFunc that converts
// strings to int8.
func StringToInt8HookFunc() 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 || t.Kind() != reflect.Int8 {
return data, nil
}
// Convert it by parsing
i64, err := strconv.ParseInt(data.(string), 0, 8)
- return int8(i64), err
+ return int8(i64), wrapStrconvNumError(err)
}
}
// StringToUint8HookFunc returns a DecodeHookFunc that converts
// strings to uint8.
func StringToUint8HookFunc() 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 || t.Kind() != reflect.Uint8 {
return data, nil
}
// Convert it by parsing
u64, err := strconv.ParseUint(data.(string), 0, 8)
- return uint8(u64), err
+ return uint8(u64), wrapStrconvNumError(err)
}
}
// StringToInt16HookFunc returns a DecodeHookFunc that converts
// strings to int16.
func StringToInt16HookFunc() 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 || t.Kind() != reflect.Int16 {
return data, nil
}
// Convert it by parsing
i64, err := strconv.ParseInt(data.(string), 0, 16)
- return int16(i64), err
+ return int16(i64), wrapStrconvNumError(err)
}
}
// StringToUint16HookFunc returns a DecodeHookFunc that converts
// strings to uint16.
func StringToUint16HookFunc() 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 || t.Kind() != reflect.Uint16 {
return data, nil
}
// Convert it by parsing
u64, err := strconv.ParseUint(data.(string), 0, 16)
- return uint16(u64), err
+ return uint16(u64), wrapStrconvNumError(err)
}
}
// StringToInt32HookFunc returns a DecodeHookFunc that converts
// strings to int32.
func StringToInt32HookFunc() 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 || t.Kind() != reflect.Int32 {
return data, nil
}
// Convert it by parsing
i64, err := strconv.ParseInt(data.(string), 0, 32)
- return int32(i64), err
+ return int32(i64), wrapStrconvNumError(err)
}
}
// StringToUint32HookFunc returns a DecodeHookFunc that converts
// strings to uint32.
func StringToUint32HookFunc() 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 || t.Kind() != reflect.Uint32 {
return data, nil
}
// Convert it by parsing
u64, err := strconv.ParseUint(data.(string), 0, 32)
- return uint32(u64), err
+ return uint32(u64), wrapStrconvNumError(err)
}
}
// StringToInt64HookFunc returns a DecodeHookFunc that converts
// strings to int64.
func StringToInt64HookFunc() 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 || t.Kind() != reflect.Int64 {
return data, nil
}
// Convert it by parsing
- return strconv.ParseInt(data.(string), 0, 64)
+ i64, err := strconv.ParseInt(data.(string), 0, 64)
+ return int64(i64), wrapStrconvNumError(err)
}
}
// StringToUint64HookFunc returns a DecodeHookFunc that converts
// strings to uint64.
func StringToUint64HookFunc() 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 || t.Kind() != reflect.Uint64 {
return data, nil
}
// Convert it by parsing
- return strconv.ParseUint(data.(string), 0, 64)
+ u64, err := strconv.ParseUint(data.(string), 0, 64)
+ return uint64(u64), wrapStrconvNumError(err)
}
}
// StringToIntHookFunc returns a DecodeHookFunc that converts
// strings to int.
func StringToIntHookFunc() 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 || t.Kind() != reflect.Int {
return data, nil
}
// Convert it by parsing
i64, err := strconv.ParseInt(data.(string), 0, 0)
- return int(i64), err
+ return int(i64), wrapStrconvNumError(err)
}
}
// StringToUintHookFunc returns a DecodeHookFunc that converts
// strings to uint.
func StringToUintHookFunc() 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 || t.Kind() != reflect.Uint {
return data, nil
}
// Convert it by parsing
u64, err := strconv.ParseUint(data.(string), 0, 0)
- return uint(u64), err
+ return uint(u64), wrapStrconvNumError(err)
}
}
// StringToFloat32HookFunc returns a DecodeHookFunc that converts
// strings to float32.
func StringToFloat32HookFunc() 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 || t.Kind() != reflect.Float32 {
return data, nil
}
// Convert it by parsing
f64, err := strconv.ParseFloat(data.(string), 32)
- return float32(f64), err
+ return float32(f64), wrapStrconvNumError(err)
}
}
// StringToFloat64HookFunc returns a DecodeHookFunc that converts
// strings to float64.
func StringToFloat64HookFunc() 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 || t.Kind() != reflect.Float64 {
return data, nil
}
// Convert it by parsing
- return strconv.ParseFloat(data.(string), 64)
+ f64, err := strconv.ParseFloat(data.(string), 64)
+ return f64, wrapStrconvNumError(err)
}
}
// StringToBoolHookFunc returns a DecodeHookFunc that converts
// strings to bool.
func StringToBoolHookFunc() 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 || t.Kind() != reflect.Bool {
return data, nil
}
// Convert it by parsing
- return strconv.ParseBool(data.(string))
+ b, err := strconv.ParseBool(data.(string))
+ return b, wrapStrconvNumError(err)
}
}
@@ -605,26 +688,27 @@ func StringToRuneHookFunc() DecodeHookFunc {
// StringToComplex64HookFunc returns a DecodeHookFunc that converts
// strings to complex64.
func StringToComplex64HookFunc() 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 || t.Kind() != reflect.Complex64 {
return data, nil
}
// Convert it by parsing
c128, err := strconv.ParseComplex(data.(string), 64)
- return complex64(c128), err
+ return complex64(c128), wrapStrconvNumError(err)
}
}
// StringToComplex128HookFunc returns a DecodeHookFunc that converts
// strings to complex128.
func StringToComplex128HookFunc() 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 || t.Kind() != reflect.Complex128 {
return data, nil
}
// Convert it by parsing
- return strconv.ParseComplex(data.(string), 128)
+ c128, err := strconv.ParseComplex(data.(string), 128)
+ return c128, wrapStrconvNumError(err)
}
}
diff --git a/vendor/github.com/go-viper/mapstructure/v2/errors.go b/vendor/github.com/go-viper/mapstructure/v2/errors.go
new file mode 100644
index 000000000..07d31c22a
--- /dev/null
+++ b/vendor/github.com/go-viper/mapstructure/v2/errors.go
@@ -0,0 +1,244 @@
+package mapstructure
+
+import (
+ "errors"
+ "fmt"
+ "net"
+ "net/url"
+ "reflect"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// Error interface is implemented by all errors emitted by mapstructure.
+//
+// Use [errors.As] to check if an error implements this interface.
+type Error interface {
+ error
+
+ mapstructure()
+}
+
+// DecodeError is a generic error type that holds information about
+// a decoding error together with the name of the field that caused the error.
+type DecodeError struct {
+ name string
+ err error
+}
+
+func newDecodeError(name string, err error) *DecodeError {
+ return &DecodeError{
+ name: name,
+ err: err,
+ }
+}
+
+func (e *DecodeError) Name() string {
+ return e.name
+}
+
+func (e *DecodeError) Unwrap() error {
+ return e.err
+}
+
+func (e *DecodeError) Error() string {
+ return fmt.Sprintf("'%s' %s", e.name, e.err)
+}
+
+func (*DecodeError) mapstructure() {}
+
+// ParseError is an error type that indicates a value could not be parsed
+// into the expected type.
+type ParseError struct {
+ Expected reflect.Value
+ Value any
+ Err error
+}
+
+func (e *ParseError) Error() string {
+ return fmt.Sprintf("cannot parse value as '%s': %s", e.Expected.Type(), e.Err)
+}
+
+func (*ParseError) mapstructure() {}
+
+// UnconvertibleTypeError is an error type that indicates a value could not be
+// converted to the expected type.
+type UnconvertibleTypeError struct {
+ Expected reflect.Value
+ Value any
+}
+
+func (e *UnconvertibleTypeError) Error() string {
+ return fmt.Sprintf(
+ "expected type '%s', got unconvertible type '%s'",
+ e.Expected.Type(),
+ reflect.TypeOf(e.Value),
+ )
+}
+
+func (*UnconvertibleTypeError) mapstructure() {}
+
+func wrapStrconvNumError(err error) error {
+ if err == nil {
+ return nil
+ }
+
+ if err, ok := err.(*strconv.NumError); ok {
+ return &strconvNumError{Err: err}
+ }
+
+ return err
+}
+
+type strconvNumError struct {
+ Err *strconv.NumError
+}
+
+func (e *strconvNumError) Error() string {
+ return "strconv." + e.Err.Func + ": " + e.Err.Err.Error()
+}
+
+func (e *strconvNumError) Unwrap() error { return e.Err }
+
+func wrapUrlError(err error) error {
+ if err == nil {
+ return nil
+ }
+
+ if err, ok := err.(*url.Error); ok {
+ return &urlError{Err: err}
+ }
+
+ return err
+}
+
+type urlError struct {
+ Err *url.Error
+}
+
+func (e *urlError) Error() string {
+ return fmt.Sprintf("%s", e.Err.Err)
+}
+
+func (e *urlError) Unwrap() error { return e.Err }
+
+func wrapNetParseError(err error) error {
+ if err == nil {
+ return nil
+ }
+
+ if err, ok := err.(*net.ParseError); ok {
+ return &netParseError{Err: err}
+ }
+
+ return err
+}
+
+type netParseError struct {
+ Err *net.ParseError
+}
+
+func (e *netParseError) Error() string {
+ return "invalid " + e.Err.Type
+}
+
+func (e *netParseError) Unwrap() error { return e.Err }
+
+func wrapTimeParseError(err error) error {
+ if err == nil {
+ return nil
+ }
+
+ if err, ok := err.(*time.ParseError); ok {
+ return &timeParseError{Err: err}
+ }
+
+ return err
+}
+
+type timeParseError struct {
+ Err *time.ParseError
+}
+
+func (e *timeParseError) Error() string {
+ if e.Err.Message == "" {
+ return fmt.Sprintf("parsing time as %q: cannot parse as %q", e.Err.Layout, e.Err.LayoutElem)
+ }
+
+ return "parsing time " + e.Err.Message
+}
+
+func (e *timeParseError) Unwrap() error { return e.Err }
+
+func wrapNetIPParseAddrError(err error) error {
+ if err == nil {
+ return nil
+ }
+
+ if errMsg := err.Error(); strings.HasPrefix(errMsg, "ParseAddr") {
+ errPieces := strings.Split(errMsg, ": ")
+
+ return fmt.Errorf("ParseAddr: %s", errPieces[len(errPieces)-1])
+ }
+
+ return err
+}
+
+func wrapNetIPParseAddrPortError(err error) error {
+ if err == nil {
+ return nil
+ }
+
+ errMsg := err.Error()
+ if strings.HasPrefix(errMsg, "invalid port ") {
+ return errors.New("invalid port")
+ } else if strings.HasPrefix(errMsg, "invalid ip:port ") {
+ return errors.New("invalid ip:port")
+ }
+
+ return err
+}
+
+func wrapNetIPParsePrefixError(err error) error {
+ if err == nil {
+ return nil
+ }
+
+ if errMsg := err.Error(); strings.HasPrefix(errMsg, "netip.ParsePrefix") {
+ errPieces := strings.Split(errMsg, ": ")
+
+ return fmt.Errorf("netip.ParsePrefix: %s", errPieces[len(errPieces)-1])
+ }
+
+ return err
+}
+
+func wrapTimeParseDurationError(err error) error {
+ if err == nil {
+ return nil
+ }
+
+ errMsg := err.Error()
+ if strings.HasPrefix(errMsg, "time: unknown unit ") {
+ return errors.New("time: unknown unit")
+ } else if strings.HasPrefix(errMsg, "time: ") {
+ idx := strings.LastIndex(errMsg, " ")
+
+ return errors.New(errMsg[:idx])
+ }
+
+ return err
+}
+
+func wrapTimeParseLocationError(err error) error {
+ if err == nil {
+ return nil
+ }
+ errMsg := err.Error()
+ if strings.Contains(errMsg, "unknown time zone") || strings.HasPrefix(errMsg, "time: unknown format") {
+ return fmt.Errorf("invalid time zone format: %w", err)
+ }
+
+ return err
+}
diff --git a/vendor/github.com/go-viper/mapstructure/v2/flake.lock b/vendor/github.com/go-viper/mapstructure/v2/flake.lock
index 4bea8154e..5e67bdd6b 100644
--- a/vendor/github.com/go-viper/mapstructure/v2/flake.lock
+++ b/vendor/github.com/go-viper/mapstructure/v2/flake.lock
@@ -2,30 +2,28 @@
"nodes": {
"cachix": {
"inputs": {
- "devenv": "devenv_2",
+ "devenv": [
+ "devenv"
+ ],
"flake-compat": [
- "devenv",
- "flake-compat"
+ "devenv"
],
- "nixpkgs": [
- "devenv",
- "nixpkgs"
+ "git-hooks": [
+ "devenv"
],
- "pre-commit-hooks": [
- "devenv",
- "pre-commit-hooks"
- ]
+ "nixpkgs": "nixpkgs"
},
"locked": {
- "lastModified": 1712055811,
- "narHash": "sha256-7FcfMm5A/f02yyzuavJe06zLa9hcMHsagE28ADcmQvk=",
+ "lastModified": 1742042642,
+ "narHash": "sha256-D0gP8srrX0qj+wNYNPdtVJsQuFzIng3q43thnHXQ/es=",
"owner": "cachix",
"repo": "cachix",
- "rev": "02e38da89851ec7fec3356a5c04bc8349cae0e30",
+ "rev": "a624d3eaf4b1d225f918de8543ed739f2f574203",
"type": "github"
},
"original": {
"owner": "cachix",
+ "ref": "latest",
"repo": "cachix",
"type": "github"
}
@@ -33,52 +31,21 @@
"devenv": {
"inputs": {
"cachix": "cachix",
- "flake-compat": "flake-compat_2",
- "nix": "nix_2",
- "nixpkgs": "nixpkgs_2",
- "pre-commit-hooks": "pre-commit-hooks"
- },
- "locked": {
- "lastModified": 1717245169,
- "narHash": "sha256-+mW3rTBjGU8p1THJN0lX/Dd/8FbnF+3dB+mJuSaxewE=",
- "owner": "cachix",
- "repo": "devenv",
- "rev": "c3f9f053c077c6f88a3de5276d9178c62baa3fc3",
- "type": "github"
- },
- "original": {
- "owner": "cachix",
- "repo": "devenv",
- "type": "github"
- }
- },
- "devenv_2": {
- "inputs": {
- "flake-compat": [
- "devenv",
- "cachix",
- "flake-compat"
- ],
+ "flake-compat": "flake-compat",
+ "git-hooks": "git-hooks",
"nix": "nix",
- "nixpkgs": "nixpkgs",
- "poetry2nix": "poetry2nix",
- "pre-commit-hooks": [
- "devenv",
- "cachix",
- "pre-commit-hooks"
- ]
+ "nixpkgs": "nixpkgs_3"
},
"locked": {
- "lastModified": 1708704632,
- "narHash": "sha256-w+dOIW60FKMaHI1q5714CSibk99JfYxm0CzTinYWr+Q=",
+ "lastModified": 1744876578,
+ "narHash": "sha256-8MTBj2REB8t29sIBLpxbR0+AEGJ7f+RkzZPAGsFd40c=",
"owner": "cachix",
"repo": "devenv",
- "rev": "2ee4450b0f4b95a1b90f2eb5ffea98b90e48c196",
+ "rev": "7ff7c351bba20d0615be25ecdcbcf79b57b85fe1",
"type": "github"
},
"original": {
"owner": "cachix",
- "ref": "python-rewrite",
"repo": "devenv",
"type": "github"
}
@@ -86,27 +53,11 @@
"flake-compat": {
"flake": false,
"locked": {
- "lastModified": 1673956053,
- "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
- "owner": "edolstra",
- "repo": "flake-compat",
- "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
- "type": "github"
- },
- "original": {
- "owner": "edolstra",
- "repo": "flake-compat",
- "type": "github"
- }
- },
- "flake-compat_2": {
- "flake": false,
- "locked": {
- "lastModified": 1696426674,
- "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
+ "lastModified": 1733328505,
+ "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
"owner": "edolstra",
"repo": "flake-compat",
- "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
+ "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
"type": "github"
},
"original": {
@@ -117,14 +68,18 @@
},
"flake-parts": {
"inputs": {
- "nixpkgs-lib": "nixpkgs-lib"
+ "nixpkgs-lib": [
+ "devenv",
+ "nix",
+ "nixpkgs"
+ ]
},
"locked": {
- "lastModified": 1717285511,
- "narHash": "sha256-iKzJcpdXih14qYVcZ9QC9XuZYnPc6T8YImb6dX166kw=",
+ "lastModified": 1712014858,
+ "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
"owner": "hercules-ci",
"repo": "flake-parts",
- "rev": "2a55567fcf15b1b1c7ed712a2c6fadaec7412ea8",
+ "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
"type": "github"
},
"original": {
@@ -133,39 +88,46 @@
"type": "github"
}
},
- "flake-utils": {
+ "flake-parts_2": {
"inputs": {
- "systems": "systems"
+ "nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
- "lastModified": 1689068808,
- "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
- "owner": "numtide",
- "repo": "flake-utils",
- "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
+ "lastModified": 1743550720,
+ "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=",
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "rev": "c621e8422220273271f52058f618c94e405bb0f5",
"type": "github"
},
"original": {
- "owner": "numtide",
- "repo": "flake-utils",
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
"type": "github"
}
},
- "flake-utils_2": {
+ "git-hooks": {
"inputs": {
- "systems": "systems_2"
+ "flake-compat": [
+ "devenv"
+ ],
+ "gitignore": "gitignore",
+ "nixpkgs": [
+ "devenv",
+ "nixpkgs"
+ ]
},
"locked": {
- "lastModified": 1710146030,
- "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
- "owner": "numtide",
- "repo": "flake-utils",
- "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
+ "lastModified": 1742649964,
+ "narHash": "sha256-DwOTp7nvfi8mRfuL1escHDXabVXFGT1VlPD1JHrtrco=",
+ "owner": "cachix",
+ "repo": "git-hooks.nix",
+ "rev": "dcf5072734cb576d2b0c59b2ac44f5050b5eac82",
"type": "github"
},
"original": {
- "owner": "numtide",
- "repo": "flake-utils",
+ "owner": "cachix",
+ "repo": "git-hooks.nix",
"type": "github"
}
},
@@ -173,7 +135,7 @@
"inputs": {
"nixpkgs": [
"devenv",
- "pre-commit-hooks",
+ "git-hooks",
"nixpkgs"
]
},
@@ -191,166 +153,109 @@
"type": "github"
}
},
- "nix": {
- "inputs": {
- "flake-compat": "flake-compat",
- "nixpkgs": [
- "devenv",
- "cachix",
- "devenv",
- "nixpkgs"
- ],
- "nixpkgs-regression": "nixpkgs-regression"
- },
- "locked": {
- "lastModified": 1712911606,
- "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=",
- "owner": "domenkozar",
- "repo": "nix",
- "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12",
- "type": "github"
- },
- "original": {
- "owner": "domenkozar",
- "ref": "devenv-2.21",
- "repo": "nix",
- "type": "github"
- }
- },
- "nix-github-actions": {
- "inputs": {
- "nixpkgs": [
- "devenv",
- "cachix",
- "devenv",
- "poetry2nix",
- "nixpkgs"
- ]
- },
+ "libgit2": {
+ "flake": false,
"locked": {
- "lastModified": 1688870561,
- "narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=",
- "owner": "nix-community",
- "repo": "nix-github-actions",
- "rev": "165b1650b753316aa7f1787f3005a8d2da0f5301",
+ "lastModified": 1697646580,
+ "narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=",
+ "owner": "libgit2",
+ "repo": "libgit2",
+ "rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5",
"type": "github"
},
"original": {
- "owner": "nix-community",
- "repo": "nix-github-actions",
+ "owner": "libgit2",
+ "repo": "libgit2",
"type": "github"
}
},
- "nix_2": {
+ "nix": {
"inputs": {
"flake-compat": [
- "devenv",
- "flake-compat"
+ "devenv"
],
- "nixpkgs": [
- "devenv",
- "nixpkgs"
+ "flake-parts": "flake-parts",
+ "libgit2": "libgit2",
+ "nixpkgs": "nixpkgs_2",
+ "nixpkgs-23-11": [
+ "devenv"
+ ],
+ "nixpkgs-regression": [
+ "devenv"
],
- "nixpkgs-regression": "nixpkgs-regression_2"
+ "pre-commit-hooks": [
+ "devenv"
+ ]
},
"locked": {
- "lastModified": 1712911606,
- "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=",
+ "lastModified": 1741798497,
+ "narHash": "sha256-E3j+3MoY8Y96mG1dUIiLFm2tZmNbRvSiyN7CrSKuAVg=",
"owner": "domenkozar",
"repo": "nix",
- "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12",
+ "rev": "f3f44b2baaf6c4c6e179de8cbb1cc6db031083cd",
"type": "github"
},
"original": {
"owner": "domenkozar",
- "ref": "devenv-2.21",
+ "ref": "devenv-2.24",
"repo": "nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
- "lastModified": 1692808169,
- "narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=",
+ "lastModified": 1733212471,
+ "narHash": "sha256-M1+uCoV5igihRfcUKrr1riygbe73/dzNnzPsmaLCmpo=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "9201b5ff357e781bf014d0330d18555695df7ba8",
+ "rev": "55d15ad12a74eb7d4646254e13638ad0c4128776",
"type": "github"
},
"original": {
"owner": "NixOS",
- "ref": "nixpkgs-unstable",
+ "ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
- "lastModified": 1717284937,
- "narHash": "sha256-lIbdfCsf8LMFloheeE6N31+BMIeixqyQWbSr2vk79EQ=",
- "type": "tarball",
- "url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz"
- },
- "original": {
- "type": "tarball",
- "url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz"
- }
- },
- "nixpkgs-regression": {
- "locked": {
- "lastModified": 1643052045,
- "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
- "owner": "NixOS",
- "repo": "nixpkgs",
- "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
- "type": "github"
- },
- "original": {
- "owner": "NixOS",
- "repo": "nixpkgs",
- "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
- "type": "github"
- }
- },
- "nixpkgs-regression_2": {
- "locked": {
- "lastModified": 1643052045,
- "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
- "owner": "NixOS",
- "repo": "nixpkgs",
- "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
+ "lastModified": 1743296961,
+ "narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=",
+ "owner": "nix-community",
+ "repo": "nixpkgs.lib",
+ "rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa",
"type": "github"
},
"original": {
- "owner": "NixOS",
- "repo": "nixpkgs",
- "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
+ "owner": "nix-community",
+ "repo": "nixpkgs.lib",
"type": "github"
}
},
- "nixpkgs-stable": {
+ "nixpkgs_2": {
"locked": {
- "lastModified": 1710695816,
- "narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=",
+ "lastModified": 1717432640,
+ "narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "614b4613980a522ba49f0d194531beddbb7220d3",
+ "rev": "88269ab3044128b7c2f4c7d68448b2fb50456870",
"type": "github"
},
"original": {
"owner": "NixOS",
- "ref": "nixos-23.11",
+ "ref": "release-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
- "nixpkgs_2": {
+ "nixpkgs_3": {
"locked": {
- "lastModified": 1713361204,
- "narHash": "sha256-TA6EDunWTkc5FvDCqU3W2T3SFn0gRZqh6D/hJnM02MM=",
+ "lastModified": 1733477122,
+ "narHash": "sha256-qamMCz5mNpQmgBwc8SB5tVMlD5sbwVIToVZtSxMph9s=",
"owner": "cachix",
"repo": "devenv-nixpkgs",
- "rev": "285676e87ad9f0ca23d8714a6ab61e7e027020c6",
+ "rev": "7bd9e84d0452f6d2e63b6e6da29fe73fac951857",
"type": "github"
},
"original": {
@@ -360,13 +265,13 @@
"type": "github"
}
},
- "nixpkgs_3": {
+ "nixpkgs_4": {
"locked": {
- "lastModified": 1717112898,
- "narHash": "sha256-7R2ZvOnvd9h8fDd65p0JnB7wXfUvreox3xFdYWd1BnY=",
+ "lastModified": 1744536153,
+ "narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "6132b0f6e344ce2fe34fc051b72fb46e34f668e0",
+ "rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11",
"type": "github"
},
"original": {
@@ -376,94 +281,11 @@
"type": "github"
}
},
- "poetry2nix": {
- "inputs": {
- "flake-utils": "flake-utils",
- "nix-github-actions": "nix-github-actions",
- "nixpkgs": [
- "devenv",
- "cachix",
- "devenv",
- "nixpkgs"
- ]
- },
- "locked": {
- "lastModified": 1692876271,
- "narHash": "sha256-IXfZEkI0Mal5y1jr6IRWMqK8GW2/f28xJenZIPQqkY0=",
- "owner": "nix-community",
- "repo": "poetry2nix",
- "rev": "d5006be9c2c2417dafb2e2e5034d83fabd207ee3",
- "type": "github"
- },
- "original": {
- "owner": "nix-community",
- "repo": "poetry2nix",
- "type": "github"
- }
- },
- "pre-commit-hooks": {
- "inputs": {
- "flake-compat": [
- "devenv",
- "flake-compat"
- ],
- "flake-utils": "flake-utils_2",
- "gitignore": "gitignore",
- "nixpkgs": [
- "devenv",
- "nixpkgs"
- ],
- "nixpkgs-stable": "nixpkgs-stable"
- },
- "locked": {
- "lastModified": 1713775815,
- "narHash": "sha256-Wu9cdYTnGQQwtT20QQMg7jzkANKQjwBD9iccfGKkfls=",
- "owner": "cachix",
- "repo": "pre-commit-hooks.nix",
- "rev": "2ac4dcbf55ed43f3be0bae15e181f08a57af24a4",
- "type": "github"
- },
- "original": {
- "owner": "cachix",
- "repo": "pre-commit-hooks.nix",
- "type": "github"
- }
- },
"root": {
"inputs": {
"devenv": "devenv",
- "flake-parts": "flake-parts",
- "nixpkgs": "nixpkgs_3"
- }
- },
- "systems": {
- "locked": {
- "lastModified": 1681028828,
- "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
- "owner": "nix-systems",
- "repo": "default",
- "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
- "type": "github"
- },
- "original": {
- "owner": "nix-systems",
- "repo": "default",
- "type": "github"
- }
- },
- "systems_2": {
- "locked": {
- "lastModified": 1681028828,
- "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
- "owner": "nix-systems",
- "repo": "default",
- "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
- "type": "github"
- },
- "original": {
- "owner": "nix-systems",
- "repo": "default",
- "type": "github"
+ "flake-parts": "flake-parts_2",
+ "nixpkgs": "nixpkgs_4"
}
}
},
diff --git a/vendor/github.com/go-viper/mapstructure/v2/flake.nix b/vendor/github.com/go-viper/mapstructure/v2/flake.nix
index 4ed0f5331..3b116f426 100644
--- a/vendor/github.com/go-viper/mapstructure/v2/flake.nix
+++ b/vendor/github.com/go-viper/mapstructure/v2/flake.nix
@@ -5,35 +5,42 @@
devenv.url = "github:cachix/devenv";
};
- outputs = inputs@{ flake-parts, ... }:
+ outputs =
+ inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
imports = [
inputs.devenv.flakeModule
];
- systems = [ "x86_64-linux" "x86_64-darwin" "aarch64-darwin" ];
+ systems = [
+ "x86_64-linux"
+ "x86_64-darwin"
+ "aarch64-darwin"
+ ];
- perSystem = { config, self', inputs', pkgs, system, ... }: rec {
- devenv.shells = {
- default = {
- languages = {
- go.enable = true;
- };
+ perSystem =
+ { pkgs, ... }:
+ rec {
+ devenv.shells = {
+ default = {
+ languages = {
+ go.enable = true;
+ };
- pre-commit.hooks = {
- nixpkgs-fmt.enable = true;
- };
+ pre-commit.hooks = {
+ nixpkgs-fmt.enable = true;
+ };
- packages = with pkgs; [
- golangci-lint
- ];
+ packages = with pkgs; [
+ golangci-lint
+ ];
- # https://github.com/cachix/devenv/issues/528#issuecomment-1556108767
- containers = pkgs.lib.mkForce { };
- };
+ # https://github.com/cachix/devenv/issues/528#issuecomment-1556108767
+ containers = pkgs.lib.mkForce { };
+ };
- ci = devenv.shells.default;
+ ci = devenv.shells.default;
+ };
};
- };
};
}
diff --git a/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go b/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go
index e77e63ba3..7c35bce02 100644
--- a/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go
+++ b/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go
@@ -1,5 +1,5 @@
// Package mapstructure exposes functionality to convert one arbitrary
-// Go type into another, typically to convert a map[string]interface{}
+// Go type into another, typically to convert a map[string]any
// into a native Go structure.
//
// The Go structure can be arbitrarily complex, containing slices,
@@ -54,8 +54,8 @@
//
// This would require an input that looks like below:
//
-// map[string]interface{}{
-// "person": map[string]interface{}{"name": "alice"},
+// map[string]any{
+// "person": map[string]any{"name": "alice"},
// }
//
// If your "person" value is NOT nested, then you can append ",squash" to
@@ -68,7 +68,7 @@
//
// Now the following input would be accepted:
//
-// map[string]interface{}{
+// map[string]any{
// "name": "alice",
// }
//
@@ -79,7 +79,7 @@
//
// Will be decoded into a map:
//
-// map[string]interface{}{
+// map[string]any{
// "name": "alice",
// }
//
@@ -95,18 +95,18 @@
//
// You can also use the ",remain" suffix on your tag to collect all unused
// values in a map. The field with this tag MUST be a map type and should
-// probably be a "map[string]interface{}" or "map[interface{}]interface{}".
+// probably be a "map[string]any" or "map[any]any".
// See example below:
//
// type Friend struct {
// Name string
-// Other map[string]interface{} `mapstructure:",remain"`
+// Other map[string]any `mapstructure:",remain"`
// }
//
// Given the input below, Other would be populated with the other
// values that weren't used (everything but "name"):
//
-// map[string]interface{}{
+// map[string]any{
// "name": "bob",
// "address": "123 Maple St.",
// }
@@ -115,15 +115,36 @@
//
// When decoding from a struct to any other value, you may use the
// ",omitempty" suffix on your tag to omit that value if it equates to
-// the zero value. The zero value of all types is specified in the Go
-// specification.
+// the zero value, or a zero-length element. The zero value of all types is
+// specified in the Go specification.
//
// For example, the zero type of a numeric type is zero ("0"). If the struct
// field value is zero and a numeric type, the field is empty, and it won't
-// be encoded into the destination type.
+// be encoded into the destination type. And likewise for the URLs field, if the
+// slice is nil or empty, it won't be encoded into the destination type.
//
// type Source struct {
-// Age int `mapstructure:",omitempty"`
+// Age int `mapstructure:",omitempty"`
+// URLs []string `mapstructure:",omitempty"`
+// }
+//
+// # Omit Zero Values
+//
+// When decoding from a struct to any other value, you may use the
+// ",omitzero" suffix on your tag to omit that value if it equates to the zero
+// value. The zero value of all types is specified in the Go specification.
+//
+// For example, the zero type of a numeric type is zero ("0"). If the struct
+// field value is zero and a numeric type, the field is empty, and it won't
+// be encoded into the destination type. And likewise for the URLs field, if the
+// slice is nil, it won't be encoded into the destination type.
+//
+// Note that if the field is a slice, and it is empty but not nil, it will
+// still be encoded into the destination type.
+//
+// type Source struct {
+// Age int `mapstructure:",omitzero"`
+// URLs []string `mapstructure:",omitzero"`
// }
//
// # Unexported fields
@@ -140,7 +161,7 @@
//
// Using this map as input:
//
-// map[string]interface{}{
+// map[string]any{
// "private": "I will be ignored",
// "Public": "I made it through!",
// }
@@ -183,19 +204,19 @@ import (
// we started with Kinds and then realized Types were the better solution,
// but have a promise to not break backwards compat so we now support
// both.
-type DecodeHookFunc interface{}
+type DecodeHookFunc any
// DecodeHookFuncType is a DecodeHookFunc which has complete information about
// the source and target types.
-type DecodeHookFuncType func(reflect.Type, reflect.Type, interface{}) (interface{}, error)
+type DecodeHookFuncType func(reflect.Type, reflect.Type, any) (any, error)
// DecodeHookFuncKind is a DecodeHookFunc which knows only the Kinds of the
// source and target types.
-type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error)
+type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, any) (any, error)
// DecodeHookFuncValue is a DecodeHookFunc which has complete access to both the source and target
// values.
-type DecodeHookFuncValue func(from reflect.Value, to reflect.Value) (interface{}, error)
+type DecodeHookFuncValue func(from reflect.Value, to reflect.Value) (any, error)
// DecoderConfig is the configuration that is used to create a new decoder
// and allows customization of various aspects of decoding.
@@ -222,6 +243,12 @@ type DecoderConfig struct {
// will affect all nested structs as well.
ErrorUnset bool
+ // AllowUnsetPointer, if set to true, will prevent fields with pointer types
+ // from being reported as unset, even if ErrorUnset is true and the field was
+ // not present in the input data. This allows pointer fields to be optional
+ // without triggering an error when they are missing.
+ AllowUnsetPointer bool
+
// ZeroFields, if set to true, will zero fields before writing them.
// For example, a map will be emptied before decoded values are put in
// it. If this is false, a map will be merged.
@@ -260,7 +287,7 @@ type DecoderConfig struct {
// Result is a pointer to the struct that will contain the decoded
// value.
- Result interface{}
+ Result any
// The tag name that mapstructure reads for field names. This
// defaults to "mapstructure"
@@ -292,7 +319,7 @@ type DecoderConfig struct {
// up the most basic Decoder.
type Decoder struct {
config *DecoderConfig
- cachedDecodeHook func(from reflect.Value, to reflect.Value) (interface{}, error)
+ cachedDecodeHook func(from reflect.Value, to reflect.Value) (any, error)
}
// Metadata contains information about decoding a structure that
@@ -313,7 +340,7 @@ type Metadata struct {
// Decode takes an input structure and uses reflection to translate it to
// the output structure. output must be a pointer to a map or struct.
-func Decode(input interface{}, output interface{}) error {
+func Decode(input any, output any) error {
config := &DecoderConfig{
Metadata: nil,
Result: output,
@@ -329,7 +356,7 @@ func Decode(input interface{}, output interface{}) error {
// WeakDecode is the same as Decode but is shorthand to enable
// WeaklyTypedInput. See DecoderConfig for more info.
-func WeakDecode(input, output interface{}) error {
+func WeakDecode(input, output any) error {
config := &DecoderConfig{
Metadata: nil,
Result: output,
@@ -346,7 +373,7 @@ func WeakDecode(input, output interface{}) error {
// DecodeMetadata is the same as Decode, but is shorthand to
// enable metadata collection. See DecoderConfig for more info.
-func DecodeMetadata(input interface{}, output interface{}, metadata *Metadata) error {
+func DecodeMetadata(input any, output any, metadata *Metadata) error {
config := &DecoderConfig{
Metadata: metadata,
Result: output,
@@ -363,7 +390,7 @@ func DecodeMetadata(input interface{}, output interface{}, metadata *Metadata) e
// WeakDecodeMetadata is the same as Decode, but is shorthand to
// enable both WeaklyTypedInput and metadata collection. See
// DecoderConfig for more info.
-func WeakDecodeMetadata(input interface{}, output interface{}, metadata *Metadata) error {
+func WeakDecodeMetadata(input any, output any, metadata *Metadata) error {
config := &DecoderConfig{
Metadata: metadata,
Result: output,
@@ -430,7 +457,7 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) {
// Decode decodes the given raw interface to the target pointer specified
// by the configuration.
-func (d *Decoder) Decode(input interface{}) error {
+func (d *Decoder) Decode(input any) error {
err := d.decode("", input, reflect.ValueOf(d.config.Result).Elem())
// Retain some of the original behavior when multiple errors ocurr
@@ -443,7 +470,7 @@ func (d *Decoder) Decode(input interface{}) error {
}
// isNil returns true if the input is nil or a typed nil pointer.
-func isNil(input interface{}) bool {
+func isNil(input any) bool {
if input == nil {
return true
}
@@ -452,7 +479,7 @@ func isNil(input interface{}) bool {
}
// Decodes an unknown data type into a specific reflection value.
-func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) error {
+func (d *Decoder) decode(name string, input any, outVal reflect.Value) error {
var (
inputVal = reflect.ValueOf(input)
outputKind = getKind(outVal)
@@ -489,10 +516,10 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
// Hooks need a valid inputVal, so reset it to zero value of outVal type.
switch outputKind {
case reflect.Struct, reflect.Map:
- var mapVal map[string]interface{}
+ var mapVal map[string]any
inputVal = reflect.ValueOf(mapVal) // create nil map pointer
case reflect.Slice, reflect.Array:
- var sliceVal []interface{}
+ var sliceVal []any
inputVal = reflect.ValueOf(sliceVal) // create nil slice pointer
default:
inputVal = reflect.Zero(outVal.Type())
@@ -504,7 +531,7 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
var err error
input, err = d.cachedDecodeHook(inputVal, outVal)
if err != nil {
- return fmt.Errorf("error decoding '%s': %w", name, err)
+ return newDecodeError(name, err)
}
}
if isNil(input) {
@@ -542,7 +569,7 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
err = d.decodeFunc(name, input, outVal)
default:
// If we reached this point then we weren't able to decode it
- return fmt.Errorf("%s: unsupported type: %s", name, outputKind)
+ return newDecodeError(name, fmt.Errorf("unsupported type: %s", outputKind))
}
// If we reached here, then we successfully decoded SOMETHING, so
@@ -556,7 +583,7 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e
// This decodes a basic type (bool, int, string, etc.) and sets the
// value to "data" of that type.
-func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value) error {
+func (d *Decoder) decodeBasic(name string, data any, val reflect.Value) error {
if val.IsValid() && val.Elem().IsValid() {
elem := val.Elem()
@@ -603,16 +630,17 @@ func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value)
dataValType := dataVal.Type()
if !dataValType.AssignableTo(val.Type()) {
- return fmt.Errorf(
- "'%s' expected type '%s', got '%s'",
- name, val.Type(), dataValType)
+ return newDecodeError(name, &UnconvertibleTypeError{
+ Expected: val,
+ Value: data,
+ })
}
val.Set(dataVal)
return nil
}
-func (d *Decoder) decodeString(name string, data interface{}, val reflect.Value) error {
+func (d *Decoder) decodeString(name string, data any, val reflect.Value) error {
dataVal := reflect.Indirect(reflect.ValueOf(data))
dataKind := getKind(dataVal)
@@ -656,15 +684,16 @@ func (d *Decoder) decodeString(name string, data interface{}, val reflect.Value)
}
if !converted {
- return fmt.Errorf(
- "'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
- name, val.Type(), dataVal.Type(), data)
+ return newDecodeError(name, &UnconvertibleTypeError{
+ Expected: val,
+ Value: data,
+ })
}
return nil
}
-func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) error {
+func (d *Decoder) decodeInt(name string, data any, val reflect.Value) error {
dataVal := reflect.Indirect(reflect.ValueOf(data))
dataKind := getKind(dataVal)
dataType := dataVal.Type()
@@ -692,26 +721,34 @@ func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) er
if err == nil {
val.SetInt(i)
} else {
- return fmt.Errorf("cannot parse '%s' as int: %s", name, err)
+ return newDecodeError(name, &ParseError{
+ Expected: val,
+ Value: data,
+ Err: wrapStrconvNumError(err),
+ })
}
case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":
jn := data.(json.Number)
i, err := jn.Int64()
if err != nil {
- return fmt.Errorf(
- "error decoding json.Number into %s: %s", name, err)
+ return newDecodeError(name, &ParseError{
+ Expected: val,
+ Value: data,
+ Err: err,
+ })
}
val.SetInt(i)
default:
- return fmt.Errorf(
- "'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
- name, val.Type(), dataVal.Type(), data)
+ return newDecodeError(name, &UnconvertibleTypeError{
+ Expected: val,
+ Value: data,
+ })
}
return nil
}
-func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) error {
+func (d *Decoder) decodeUint(name string, data any, val reflect.Value) error {
dataVal := reflect.Indirect(reflect.ValueOf(data))
dataKind := getKind(dataVal)
dataType := dataVal.Type()
@@ -720,8 +757,11 @@ func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) e
case dataKind == reflect.Int:
i := dataVal.Int()
if i < 0 && !d.config.WeaklyTypedInput {
- return fmt.Errorf("cannot parse '%s', %d overflows uint",
- name, i)
+ return newDecodeError(name, &ParseError{
+ Expected: val,
+ Value: data,
+ Err: fmt.Errorf("%d overflows uint", i),
+ })
}
val.SetUint(uint64(i))
case dataKind == reflect.Uint:
@@ -729,8 +769,11 @@ func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) e
case dataKind == reflect.Float32:
f := dataVal.Float()
if f < 0 && !d.config.WeaklyTypedInput {
- return fmt.Errorf("cannot parse '%s', %f overflows uint",
- name, f)
+ return newDecodeError(name, &ParseError{
+ Expected: val,
+ Value: data,
+ Err: fmt.Errorf("%f overflows uint", f),
+ })
}
val.SetUint(uint64(f))
case dataKind == reflect.Bool && d.config.WeaklyTypedInput:
@@ -749,26 +792,34 @@ func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) e
if err == nil {
val.SetUint(i)
} else {
- return fmt.Errorf("cannot parse '%s' as uint: %s", name, err)
+ return newDecodeError(name, &ParseError{
+ Expected: val,
+ Value: data,
+ Err: wrapStrconvNumError(err),
+ })
}
case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":
jn := data.(json.Number)
i, err := strconv.ParseUint(string(jn), 0, 64)
if err != nil {
- return fmt.Errorf(
- "error decoding json.Number into %s: %s", name, err)
+ return newDecodeError(name, &ParseError{
+ Expected: val,
+ Value: data,
+ Err: wrapStrconvNumError(err),
+ })
}
val.SetUint(i)
default:
- return fmt.Errorf(
- "'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
- name, val.Type(), dataVal.Type(), data)
+ return newDecodeError(name, &UnconvertibleTypeError{
+ Expected: val,
+ Value: data,
+ })
}
return nil
}
-func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) error {
+func (d *Decoder) decodeBool(name string, data any, val reflect.Value) error {
dataVal := reflect.Indirect(reflect.ValueOf(data))
dataKind := getKind(dataVal)
@@ -788,18 +839,23 @@ func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) e
} else if dataVal.String() == "" {
val.SetBool(false)
} else {
- return fmt.Errorf("cannot parse '%s' as bool: %s", name, err)
+ return newDecodeError(name, &ParseError{
+ Expected: val,
+ Value: data,
+ Err: wrapStrconvNumError(err),
+ })
}
default:
- return fmt.Errorf(
- "'%s' expected type '%s', got unconvertible type '%#v', value: '%#v'",
- name, val, dataVal, data)
+ return newDecodeError(name, &UnconvertibleTypeError{
+ Expected: val,
+ Value: data,
+ })
}
return nil
}
-func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value) error {
+func (d *Decoder) decodeFloat(name string, data any, val reflect.Value) error {
dataVal := reflect.Indirect(reflect.ValueOf(data))
dataKind := getKind(dataVal)
dataType := dataVal.Type()
@@ -827,26 +883,34 @@ func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value)
if err == nil {
val.SetFloat(f)
} else {
- return fmt.Errorf("cannot parse '%s' as float: %s", name, err)
+ return newDecodeError(name, &ParseError{
+ Expected: val,
+ Value: data,
+ Err: wrapStrconvNumError(err),
+ })
}
case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number":
jn := data.(json.Number)
i, err := jn.Float64()
if err != nil {
- return fmt.Errorf(
- "error decoding json.Number into %s: %s", name, err)
+ return newDecodeError(name, &ParseError{
+ Expected: val,
+ Value: data,
+ Err: err,
+ })
}
val.SetFloat(i)
default:
- return fmt.Errorf(
- "'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
- name, val.Type(), dataVal.Type(), data)
+ return newDecodeError(name, &UnconvertibleTypeError{
+ Expected: val,
+ Value: data,
+ })
}
return nil
}
-func (d *Decoder) decodeComplex(name string, data interface{}, val reflect.Value) error {
+func (d *Decoder) decodeComplex(name string, data any, val reflect.Value) error {
dataVal := reflect.Indirect(reflect.ValueOf(data))
dataKind := getKind(dataVal)
@@ -854,15 +918,16 @@ func (d *Decoder) decodeComplex(name string, data interface{}, val reflect.Value
case dataKind == reflect.Complex64:
val.SetComplex(dataVal.Complex())
default:
- return fmt.Errorf(
- "'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
- name, val.Type(), dataVal.Type(), data)
+ return newDecodeError(name, &UnconvertibleTypeError{
+ Expected: val,
+ Value: data,
+ })
}
return nil
}
-func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) error {
+func (d *Decoder) decodeMap(name string, data any, val reflect.Value) error {
valType := val.Type()
valKeyType := valType.Key()
valElemType := valType.Elem()
@@ -900,7 +965,10 @@ func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) er
fallthrough
default:
- return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind())
+ return newDecodeError(name, &UnconvertibleTypeError{
+ Expected: val,
+ Value: data,
+ })
}
}
@@ -986,7 +1054,10 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
// to the map value.
v := dataVal.Field(i)
if !v.Type().AssignableTo(valMap.Type().Elem()) {
- return fmt.Errorf("cannot assign type '%s' to map value field of type '%s'", v.Type(), valMap.Type().Elem())
+ return newDecodeError(
+ name+"."+f.Name,
+ fmt.Errorf("cannot assign type %q to map value field of type %q", v.Type(), valMap.Type().Elem()),
+ )
}
tagValue := f.Tag.Get(d.config.TagName)
@@ -1011,6 +1082,11 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
continue
}
+ // If "omitzero" is specified in the tag, it ignores zero values.
+ if strings.Index(tagValue[index+1:], "omitzero") != -1 && v.IsZero() {
+ continue
+ }
+
// If "squash" is specified in the tag, we squash the field down.
squash = squash || strings.Contains(tagValue[index+1:], d.config.SquashTagOption)
if squash {
@@ -1021,12 +1097,18 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
// The final type must be a struct
if v.Kind() != reflect.Struct {
- return fmt.Errorf("cannot squash non-struct type '%s'", v.Type())
+ return newDecodeError(
+ name+"."+f.Name,
+ fmt.Errorf("cannot squash non-struct type %q", v.Type()),
+ )
}
} else {
if strings.Index(tagValue[index+1:], "remain") != -1 {
if v.Kind() != reflect.Map {
- return fmt.Errorf("error remain-tag field with invalid type: '%s'", v.Type())
+ return newDecodeError(
+ name+"."+f.Name,
+ fmt.Errorf("error remain-tag field with invalid type: %q", v.Type()),
+ )
}
ptr := v.MapRange()
@@ -1094,7 +1176,7 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
return nil
}
-func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) (bool, error) {
+func (d *Decoder) decodePtr(name string, data any, val reflect.Value) (bool, error) {
// If the input data is nil, then we want to just set the output
// pointer to be nil as well.
isNil := data == nil
@@ -1141,20 +1223,21 @@ func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) (b
return false, nil
}
-func (d *Decoder) decodeFunc(name string, data interface{}, val reflect.Value) error {
+func (d *Decoder) decodeFunc(name string, data any, val reflect.Value) error {
// Create an element of the concrete (non pointer) type and decode
// into that. Then set the value of the pointer to this type.
dataVal := reflect.Indirect(reflect.ValueOf(data))
if val.Type() != dataVal.Type() {
- return fmt.Errorf(
- "'%s' expected type '%s', got unconvertible type '%s', value: '%v'",
- name, val.Type(), dataVal.Type(), data)
+ return newDecodeError(name, &UnconvertibleTypeError{
+ Expected: val,
+ Value: data,
+ })
}
val.Set(dataVal)
return nil
}
-func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value) error {
+func (d *Decoder) decodeSlice(name string, data any, val reflect.Value) error {
dataVal := reflect.Indirect(reflect.ValueOf(data))
dataValKind := dataVal.Kind()
valType := val.Type()
@@ -1176,7 +1259,7 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
return nil
}
// Create slice of maps of other sizes
- return d.decodeSlice(name, []interface{}{data}, val)
+ return d.decodeSlice(name, []any{data}, val)
case dataValKind == reflect.String && valElemType.Kind() == reflect.Uint8:
return d.decodeSlice(name, []byte(dataVal.String()), val)
@@ -1185,12 +1268,12 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
// and "lift" it into it. i.e. a string becomes a string slice.
default:
// Just re-try this function with data as a slice.
- return d.decodeSlice(name, []interface{}{data}, val)
+ return d.decodeSlice(name, []any{data}, val)
}
}
- return fmt.Errorf(
- "'%s': source data must be an array or slice, got %s", name, dataValKind)
+ return newDecodeError(name,
+ fmt.Errorf("source data must be an array or slice, got %s", dataValKind))
}
// If the input value is nil, then don't allocate since empty != nil
@@ -1228,7 +1311,7 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
return errors.Join(errs...)
}
-func (d *Decoder) decodeArray(name string, data interface{}, val reflect.Value) error {
+func (d *Decoder) decodeArray(name string, data any, val reflect.Value) error {
dataVal := reflect.Indirect(reflect.ValueOf(data))
dataValKind := dataVal.Kind()
valType := val.Type()
@@ -1253,17 +1336,17 @@ func (d *Decoder) decodeArray(name string, data interface{}, val reflect.Value)
// and "lift" it into it. i.e. a string becomes a string array.
default:
// Just re-try this function with data as a slice.
- return d.decodeArray(name, []interface{}{data}, val)
+ return d.decodeArray(name, []any{data}, val)
}
}
- return fmt.Errorf(
- "'%s': source data must be an array or slice, got %s", name, dataValKind)
+ return newDecodeError(name,
+ fmt.Errorf("source data must be an array or slice, got %s", dataValKind))
}
if dataVal.Len() > arrayType.Len() {
- return fmt.Errorf(
- "'%s': expected source data to have length less or equal to %d, got %d", name, arrayType.Len(), dataVal.Len())
+ return newDecodeError(name,
+ fmt.Errorf("expected source data to have length less or equal to %d, got %d", arrayType.Len(), dataVal.Len()))
}
// Make a new array to hold our result, same size as the original data.
@@ -1289,7 +1372,7 @@ func (d *Decoder) decodeArray(name string, data interface{}, val reflect.Value)
return errors.Join(errs...)
}
-func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) error {
+func (d *Decoder) decodeStruct(name string, data any, val reflect.Value) error {
dataVal := reflect.Indirect(reflect.ValueOf(data))
// If the type of the value to write to and the data match directly,
@@ -1310,7 +1393,7 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value)
// as an intermediary.
// Make a new map to hold our result
- mapType := reflect.TypeOf((map[string]interface{})(nil))
+ mapType := reflect.TypeOf((map[string]any)(nil))
mval := reflect.MakeMap(mapType)
// Creating a pointer to a map so that other methods can completely
@@ -1328,26 +1411,26 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value)
return result
default:
- return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind())
+ return newDecodeError(name,
+ fmt.Errorf("expected a map or struct, got %q", dataValKind))
}
}
func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) error {
dataValType := dataVal.Type()
if kind := dataValType.Key().Kind(); kind != reflect.String && kind != reflect.Interface {
- return fmt.Errorf(
- "'%s' needs a map with string keys, has '%s' keys",
- name, dataValType.Key().Kind())
+ return newDecodeError(name,
+ fmt.Errorf("needs a map with string keys, has %q keys", kind))
}
dataValKeys := make(map[reflect.Value]struct{})
- dataValKeysUnused := make(map[interface{}]struct{})
+ dataValKeysUnused := make(map[any]struct{})
for _, dataValKey := range dataVal.MapKeys() {
dataValKeys[dataValKey] = struct{}{}
dataValKeysUnused[dataValKey.Interface()] = struct{}{}
}
- targetValKeysUnused := make(map[interface{}]struct{})
+ targetValKeysUnused := make(map[any]struct{})
var errs []error
@@ -1410,7 +1493,10 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
structs = append(structs, fieldVal.Elem().Elem())
}
default:
- errs = append(errs, fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldVal.Kind()))
+ errs = append(errs, newDecodeError(
+ name+"."+fieldType.Name,
+ fmt.Errorf("unsupported type for squash: %s", fieldVal.Kind()),
+ ))
}
continue
}
@@ -1461,7 +1547,9 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
if !rawMapVal.IsValid() {
// There was no matching key in the map for the value in
// the struct. Remember it for potential errors and metadata.
- targetValKeysUnused[fieldName] = struct{}{}
+ if !(d.config.AllowUnsetPointer && fieldValue.Kind() == reflect.Ptr) {
+ targetValKeysUnused[fieldName] = struct{}{}
+ }
continue
}
}
@@ -1495,7 +1583,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
// we put the unused keys directly into the remain field.
if remainField != nil && len(dataValKeysUnused) > 0 {
// Build a map of only the unused values
- remain := map[interface{}]interface{}{}
+ remain := map[any]any{}
for key := range dataValKeysUnused {
remain[key] = dataVal.MapIndex(reflect.ValueOf(key)).Interface()
}
@@ -1517,8 +1605,10 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
}
sort.Strings(keys)
- err := fmt.Errorf("'%s' has invalid keys: %s", name, strings.Join(keys, ", "))
- errs = append(errs, err)
+ errs = append(errs, newDecodeError(
+ name,
+ fmt.Errorf("has invalid keys: %s", strings.Join(keys, ", ")),
+ ))
}
if d.config.ErrorUnset && len(targetValKeysUnused) > 0 {
@@ -1528,8 +1618,10 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
}
sort.Strings(keys)
- err := fmt.Errorf("'%s' has unset fields: %s", name, strings.Join(keys, ", "))
- errs = append(errs, err)
+ errs = append(errs, newDecodeError(
+ name,
+ fmt.Errorf("has unset fields: %s", strings.Join(keys, ", ")),
+ ))
}
if err := errors.Join(errs...); err != nil {
diff --git a/vendor/github.com/pelletier/go-toml/v2/.goreleaser.yaml b/vendor/github.com/pelletier/go-toml/v2/.goreleaser.yaml
index ec52857a3..47f0f5914 100644
--- a/vendor/github.com/pelletier/go-toml/v2/.goreleaser.yaml
+++ b/vendor/github.com/pelletier/go-toml/v2/.goreleaser.yaml
@@ -113,7 +113,7 @@ dockers:
checksum:
name_template: 'sha256sums.txt'
snapshot:
- name_template: "{{ incpatch .Version }}-next"
+ version_template: "{{ incpatch .Version }}-next"
release:
github:
owner: pelletier
diff --git a/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go b/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go
index c3df8bee1..189be525e 100644
--- a/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go
+++ b/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go
@@ -59,7 +59,7 @@ func (d *Decoder) DisallowUnknownFields() *Decoder {
//
// With this feature enabled, types implementing the unstable/Unmarshaler
// interface can be decoded from any structure of the document. It allows types
-// that don't have a straightfoward TOML representation to provide their own
+// that don't have a straightforward TOML representation to provide their own
// decoding logic.
//
// Currently, types can only decode from a single value. Tables and array tables
diff --git a/vendor/github.com/sagikazarmark/locafero/.envrc b/vendor/github.com/sagikazarmark/locafero/.envrc
index 2e0f9f5f7..5c95dc798 100644
--- a/vendor/github.com/sagikazarmark/locafero/.envrc
+++ b/vendor/github.com/sagikazarmark/locafero/.envrc
@@ -1,4 +1,4 @@
-if ! has nix_direnv_version || ! nix_direnv_version 3.0.4; then
- source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.4/direnvrc" "sha256-DzlYZ33mWF/Gs8DDeyjr8mnVmQGx7ASYqA5WlxwvBG4="
+if ! has nix_direnv_version || ! nix_direnv_version 3.1.0; then
+ source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.1.0/direnvrc" "sha256-yMJ2OVMzrFaDPn7q8nCBZFRYpL/f0RcHzhmw/i6btJM="
fi
use flake . --impure
diff --git a/vendor/github.com/sagikazarmark/locafero/.golangci.yaml b/vendor/github.com/sagikazarmark/locafero/.golangci.yaml
index 829de2a4a..a27a42959 100644
--- a/vendor/github.com/sagikazarmark/locafero/.golangci.yaml
+++ b/vendor/github.com/sagikazarmark/locafero/.golangci.yaml
@@ -1,27 +1,37 @@
+version: "2"
+
run:
timeout: 10m
-linters-settings:
- gci:
- sections:
- - standard
- - default
- - prefix(github.com/sagikazarmark/locafero)
- goimports:
- local-prefixes: github.com/sagikazarmark/locafero
- misspell:
- locale: US
- nolintlint:
- allow-leading-space: false # require machine-readable nolint directives (with no leading space)
- allow-unused: false # report any unused nolint directives
- require-specific: false # don't require nolint directives to be specific about which linter is being skipped
- revive:
- confidence: 0
-
linters:
enable:
- - gci
- - goimports
+ - errcheck
+ - govet
+ - ineffassign
- misspell
- nolintlint
- revive
+ - staticcheck
+ - unused
+
+ settings:
+ misspell:
+ locale: US
+ nolintlint:
+ allow-unused: false # report any unused nolint directives
+ require-specific: false # don't require nolint directives to be specific about which linter is being skipped
+
+formatters:
+ enable:
+ - gci
+ - gofmt
+ - gofumpt
+ - goimports
+ - golines
+
+ settings:
+ gci:
+ sections:
+ - standard
+ - default
+ - localmodule
diff --git a/vendor/github.com/sagikazarmark/locafero/README.md b/vendor/github.com/sagikazarmark/locafero/README.md
index a48e8e978..d25fe80f3 100644
--- a/vendor/github.com/sagikazarmark/locafero/README.md
+++ b/vendor/github.com/sagikazarmark/locafero/README.md
@@ -2,8 +2,8 @@
[](https://github.com/sagikazarmark/locafero/actions/workflows/ci.yaml)
[](https://pkg.go.dev/mod/github.com/sagikazarmark/locafero)
-
-[](https://builtwithnix.org)
+
+[](https://deps.dev/go/github.com%252Fsagikazarmark%252Flocafero)
**Finder library for [Afero](https://github.com/spf13/afero) ported from [go-finder](https://github.com/sagikazarmark/go-finder).**
diff --git a/vendor/github.com/sagikazarmark/locafero/file_type.go b/vendor/github.com/sagikazarmark/locafero/file_type.go
index 9a9b14023..5ea57c93e 100644
--- a/vendor/github.com/sagikazarmark/locafero/file_type.go
+++ b/vendor/github.com/sagikazarmark/locafero/file_type.go
@@ -5,19 +5,23 @@ import "io/fs"
// FileType represents the kind of entries [Finder] can return.
type FileType int
+// FileType represents the kind of entries [Finder] can return.
const (
- FileTypeAll FileType = iota
+ FileTypeAny FileType = iota
FileTypeFile
FileTypeDir
+
+ // Deprecated: Use [FileTypeAny] instead.
+ FileTypeAll = FileTypeAny
)
-func (ft FileType) matchFileInfo(info fs.FileInfo) bool {
+func (ft FileType) match(info fs.FileInfo) bool {
switch ft {
- case FileTypeAll:
+ case FileTypeAny:
return true
case FileTypeFile:
- return !info.IsDir()
+ return info.Mode().IsRegular()
case FileTypeDir:
return info.IsDir()
diff --git a/vendor/github.com/sagikazarmark/locafero/finder.go b/vendor/github.com/sagikazarmark/locafero/finder.go
index ef8d54712..ce43c7826 100644
--- a/vendor/github.com/sagikazarmark/locafero/finder.go
+++ b/vendor/github.com/sagikazarmark/locafero/finder.go
@@ -1,4 +1,4 @@
-// Package finder looks for files and directories in an {fs.Fs} filesystem.
+// Package locafero looks for files and directories in an {fs.Fs} filesystem.
package locafero
import (
@@ -7,7 +7,7 @@ import (
"path/filepath"
"strings"
- "github.com/sourcegraph/conc/iter"
+ "github.com/sourcegraph/conc/pool"
"github.com/spf13/afero"
)
@@ -44,65 +44,66 @@ type Finder struct {
// Find looks for files and directories in an [afero.Fs] filesystem.
func (f Finder) Find(fsys afero.Fs) ([]string, error) {
// Arbitrary go routine limit (TODO: make this a parameter)
- // pool := pool.NewWithResults[[]string]().WithMaxGoroutines(5).WithErrors().WithFirstError()
+ p := pool.NewWithResults[[]searchResult]().WithMaxGoroutines(5).WithErrors().WithFirstError()
- type searchItem struct {
- path string
- name string
+ for _, searchPath := range f.Paths {
+ for _, searchName := range f.Names {
+ p.Go(func() ([]searchResult, error) {
+ // If the name contains any glob character, perform a glob match
+ if strings.ContainsAny(searchName, globMatch) {
+ return globWalkSearch(fsys, searchPath, searchName, f.Type)
+ }
+
+ return statSearch(fsys, searchPath, searchName, f.Type)
+ })
+ }
}
- var searchItems []searchItem
+ searchResults, err := flatten(p.Wait())
+ if err != nil {
+ return nil, err
+ }
- for _, searchPath := range f.Paths {
- searchPath := searchPath
+ // Return early if no results were found
+ if len(searchResults) == 0 {
+ return nil, nil
+ }
- for _, searchName := range f.Names {
- searchName := searchName
-
- searchItems = append(searchItems, searchItem{searchPath, searchName})
-
- // pool.Go(func() ([]string, error) {
- // // If the name contains any glob character, perform a glob match
- // if strings.ContainsAny(searchName, globMatch) {
- // return globWalkSearch(fsys, searchPath, searchName, f.Type)
- // }
- //
- // return statSearch(fsys, searchPath, searchName, f.Type)
- // })
- }
+ results := make([]string, 0, len(searchResults))
+
+ for _, searchResult := range searchResults {
+ results = append(results, searchResult.path)
}
- // allResults, err := pool.Wait()
- // if err != nil {
- // return nil, err
- // }
+ return results, nil
+}
- allResults, err := iter.MapErr(searchItems, func(item *searchItem) ([]string, error) {
- // If the name contains any glob character, perform a glob match
- if strings.ContainsAny(item.name, globMatch) {
- return globWalkSearch(fsys, item.path, item.name, f.Type)
- }
+type searchResult struct {
+ path string
+ info fs.FileInfo
+}
- return statSearch(fsys, item.path, item.name, f.Type)
- })
+func flatten[T any](results [][]T, err error) ([]T, error) {
if err != nil {
return nil, err
}
- var results []string
+ var flattened []T
- for _, r := range allResults {
- results = append(results, r...)
+ for _, r := range results {
+ flattened = append(flattened, r...)
}
- // Sort results in alphabetical order for now
- // sort.Strings(results)
-
- return results, nil
+ return flattened, nil
}
-func globWalkSearch(fsys afero.Fs, searchPath string, searchName string, searchType FileType) ([]string, error) {
- var results []string
+func globWalkSearch(
+ fsys afero.Fs,
+ searchPath string,
+ searchName string,
+ searchType FileType,
+) ([]searchResult, error) {
+ var results []searchResult
err := afero.Walk(fsys, searchPath, func(p string, fileInfo fs.FileInfo, err error) error {
if err != nil {
@@ -123,7 +124,7 @@ func globWalkSearch(fsys afero.Fs, searchPath string, searchName string, searchT
}
// Skip unmatching type
- if !searchType.matchFileInfo(fileInfo) {
+ if !searchType.match(fileInfo) {
return result
}
@@ -133,7 +134,7 @@ func globWalkSearch(fsys afero.Fs, searchPath string, searchName string, searchT
}
if match {
- results = append(results, p)
+ results = append(results, searchResult{p, fileInfo})
}
return result
@@ -145,7 +146,12 @@ func globWalkSearch(fsys afero.Fs, searchPath string, searchName string, searchT
return results, nil
}
-func statSearch(fsys afero.Fs, searchPath string, searchName string, searchType FileType) ([]string, error) {
+func statSearch(
+ fsys afero.Fs,
+ searchPath string,
+ searchName string,
+ searchType FileType,
+) ([]searchResult, error) {
filePath := filepath.Join(searchPath, searchName)
fileInfo, err := fsys.Stat(filePath)
@@ -157,9 +163,9 @@ func statSearch(fsys afero.Fs, searchPath string, searchName string, searchType
}
// Skip unmatching type
- if !searchType.matchFileInfo(fileInfo) {
+ if !searchType.match(fileInfo) {
return nil, nil
}
- return []string{filePath}, nil
+ return []searchResult{{filePath, fileInfo}}, nil
}
diff --git a/vendor/github.com/sagikazarmark/locafero/flake.lock b/vendor/github.com/sagikazarmark/locafero/flake.lock
index df2a8ccec..b14a842c2 100644
--- a/vendor/github.com/sagikazarmark/locafero/flake.lock
+++ b/vendor/github.com/sagikazarmark/locafero/flake.lock
@@ -2,30 +2,32 @@
"nodes": {
"cachix": {
"inputs": {
- "devenv": "devenv_2",
+ "devenv": [
+ "devenv"
+ ],
"flake-compat": [
+ "devenv"
+ ],
+ "git-hooks": [
"devenv",
- "flake-compat"
+ "git-hooks"
],
"nixpkgs": [
"devenv",
"nixpkgs"
- ],
- "pre-commit-hooks": [
- "devenv",
- "pre-commit-hooks"
]
},
"locked": {
- "lastModified": 1712055811,
- "narHash": "sha256-7FcfMm5A/f02yyzuavJe06zLa9hcMHsagE28ADcmQvk=",
+ "lastModified": 1748883665,
+ "narHash": "sha256-R0W7uAg+BLoHjMRMQ8+oiSbTq8nkGz5RDpQ+ZfxxP3A=",
"owner": "cachix",
"repo": "cachix",
- "rev": "02e38da89851ec7fec3356a5c04bc8349cae0e30",
+ "rev": "f707778d902af4d62d8dd92c269f8e70de09acbe",
"type": "github"
},
"original": {
"owner": "cachix",
+ "ref": "latest",
"repo": "cachix",
"type": "github"
}
@@ -33,52 +35,21 @@
"devenv": {
"inputs": {
"cachix": "cachix",
- "flake-compat": "flake-compat_2",
- "nix": "nix_2",
- "nixpkgs": "nixpkgs_2",
- "pre-commit-hooks": "pre-commit-hooks"
- },
- "locked": {
- "lastModified": 1725907707,
- "narHash": "sha256-s3pbtzZmVPHzc86WQjK7MGZMNvvw6hWnFMljEkllAfM=",
- "owner": "cachix",
- "repo": "devenv",
- "rev": "2bbbbc468fc02257265a79652a8350651cca495a",
- "type": "github"
- },
- "original": {
- "owner": "cachix",
- "repo": "devenv",
- "type": "github"
- }
- },
- "devenv_2": {
- "inputs": {
- "flake-compat": [
- "devenv",
- "cachix",
- "flake-compat"
- ],
+ "flake-compat": "flake-compat",
+ "git-hooks": "git-hooks",
"nix": "nix",
- "nixpkgs": "nixpkgs",
- "poetry2nix": "poetry2nix",
- "pre-commit-hooks": [
- "devenv",
- "cachix",
- "pre-commit-hooks"
- ]
+ "nixpkgs": "nixpkgs"
},
"locked": {
- "lastModified": 1708704632,
- "narHash": "sha256-w+dOIW60FKMaHI1q5714CSibk99JfYxm0CzTinYWr+Q=",
+ "lastModified": 1753981111,
+ "narHash": "sha256-uBJOyMxOkGRmxhD2M5rbN2aV6oP1T2AKq5oBaHHC4mw=",
"owner": "cachix",
"repo": "devenv",
- "rev": "2ee4450b0f4b95a1b90f2eb5ffea98b90e48c196",
+ "rev": "d4d70df706b153b601a87ab8e81c88a0b1a373b6",
"type": "github"
},
"original": {
"owner": "cachix",
- "ref": "python-rewrite",
"repo": "devenv",
"type": "github"
}
@@ -86,27 +57,11 @@
"flake-compat": {
"flake": false,
"locked": {
- "lastModified": 1673956053,
- "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
+ "lastModified": 1747046372,
+ "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
"owner": "edolstra",
"repo": "flake-compat",
- "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
- "type": "github"
- },
- "original": {
- "owner": "edolstra",
- "repo": "flake-compat",
- "type": "github"
- }
- },
- "flake-compat_2": {
- "flake": false,
- "locked": {
- "lastModified": 1696426674,
- "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
- "owner": "edolstra",
- "repo": "flake-compat",
- "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
+ "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
"type": "github"
},
"original": {
@@ -117,14 +72,18 @@
},
"flake-parts": {
"inputs": {
- "nixpkgs-lib": "nixpkgs-lib"
+ "nixpkgs-lib": [
+ "devenv",
+ "nix",
+ "nixpkgs"
+ ]
},
"locked": {
- "lastModified": 1725234343,
- "narHash": "sha256-+ebgonl3NbiKD2UD0x4BszCZQ6sTfL4xioaM49o5B3Y=",
+ "lastModified": 1733312601,
+ "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=",
"owner": "hercules-ci",
"repo": "flake-parts",
- "rev": "567b938d64d4b4112ee253b9274472dc3a346eb6",
+ "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9",
"type": "github"
},
"original": {
@@ -133,39 +92,47 @@
"type": "github"
}
},
- "flake-utils": {
+ "flake-parts_2": {
"inputs": {
- "systems": "systems"
+ "nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
- "lastModified": 1689068808,
- "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
- "owner": "numtide",
- "repo": "flake-utils",
- "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
+ "lastModified": 1753121425,
+ "narHash": "sha256-TVcTNvOeWWk1DXljFxVRp+E0tzG1LhrVjOGGoMHuXio=",
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "rev": "644e0fc48951a860279da645ba77fe4a6e814c5e",
"type": "github"
},
"original": {
- "owner": "numtide",
- "repo": "flake-utils",
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
"type": "github"
}
},
- "flake-utils_2": {
+ "git-hooks": {
"inputs": {
- "systems": "systems_2"
+ "flake-compat": [
+ "devenv",
+ "flake-compat"
+ ],
+ "gitignore": "gitignore",
+ "nixpkgs": [
+ "devenv",
+ "nixpkgs"
+ ]
},
"locked": {
- "lastModified": 1710146030,
- "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
- "owner": "numtide",
- "repo": "flake-utils",
- "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
+ "lastModified": 1750779888,
+ "narHash": "sha256-wibppH3g/E2lxU43ZQHC5yA/7kIKLGxVEnsnVK1BtRg=",
+ "owner": "cachix",
+ "repo": "git-hooks.nix",
+ "rev": "16ec914f6fb6f599ce988427d9d94efddf25fe6d",
"type": "github"
},
"original": {
- "owner": "numtide",
- "repo": "flake-utils",
+ "owner": "cachix",
+ "repo": "git-hooks.nix",
"type": "github"
}
},
@@ -173,7 +140,7 @@
"inputs": {
"nixpkgs": [
"devenv",
- "pre-commit-hooks",
+ "git-hooks",
"nixpkgs"
]
},
@@ -192,165 +159,49 @@
}
},
"nix": {
- "inputs": {
- "flake-compat": "flake-compat",
- "nixpkgs": [
- "devenv",
- "cachix",
- "devenv",
- "nixpkgs"
- ],
- "nixpkgs-regression": "nixpkgs-regression"
- },
- "locked": {
- "lastModified": 1712911606,
- "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=",
- "owner": "domenkozar",
- "repo": "nix",
- "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12",
- "type": "github"
- },
- "original": {
- "owner": "domenkozar",
- "ref": "devenv-2.21",
- "repo": "nix",
- "type": "github"
- }
- },
- "nix-github-actions": {
- "inputs": {
- "nixpkgs": [
- "devenv",
- "cachix",
- "devenv",
- "poetry2nix",
- "nixpkgs"
- ]
- },
- "locked": {
- "lastModified": 1688870561,
- "narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=",
- "owner": "nix-community",
- "repo": "nix-github-actions",
- "rev": "165b1650b753316aa7f1787f3005a8d2da0f5301",
- "type": "github"
- },
- "original": {
- "owner": "nix-community",
- "repo": "nix-github-actions",
- "type": "github"
- }
- },
- "nix_2": {
"inputs": {
"flake-compat": [
"devenv",
"flake-compat"
],
+ "flake-parts": "flake-parts",
+ "git-hooks-nix": [
+ "devenv",
+ "git-hooks"
+ ],
"nixpkgs": [
"devenv",
"nixpkgs"
],
- "nixpkgs-regression": "nixpkgs-regression_2"
+ "nixpkgs-23-11": [
+ "devenv"
+ ],
+ "nixpkgs-regression": [
+ "devenv"
+ ]
},
"locked": {
- "lastModified": 1712911606,
- "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=",
- "owner": "domenkozar",
+ "lastModified": 1752773918,
+ "narHash": "sha256-dOi/M6yNeuJlj88exI+7k154z+hAhFcuB8tZktiW7rg=",
+ "owner": "cachix",
"repo": "nix",
- "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12",
+ "rev": "031c3cf42d2e9391eee373507d8c12e0f9606779",
"type": "github"
},
"original": {
- "owner": "domenkozar",
- "ref": "devenv-2.21",
+ "owner": "cachix",
+ "ref": "devenv-2.30",
"repo": "nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
- "lastModified": 1692808169,
- "narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=",
- "owner": "NixOS",
- "repo": "nixpkgs",
- "rev": "9201b5ff357e781bf014d0330d18555695df7ba8",
- "type": "github"
- },
- "original": {
- "owner": "NixOS",
- "ref": "nixpkgs-unstable",
- "repo": "nixpkgs",
- "type": "github"
- }
- },
- "nixpkgs-lib": {
- "locked": {
- "lastModified": 1725233747,
- "narHash": "sha256-Ss8QWLXdr2JCBPcYChJhz4xJm+h/xjl4G0c0XlP6a74=",
- "type": "tarball",
- "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz"
- },
- "original": {
- "type": "tarball",
- "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz"
- }
- },
- "nixpkgs-regression": {
- "locked": {
- "lastModified": 1643052045,
- "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
- "owner": "NixOS",
- "repo": "nixpkgs",
- "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
- "type": "github"
- },
- "original": {
- "owner": "NixOS",
- "repo": "nixpkgs",
- "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
- "type": "github"
- }
- },
- "nixpkgs-regression_2": {
- "locked": {
- "lastModified": 1643052045,
- "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
- "owner": "NixOS",
- "repo": "nixpkgs",
- "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
- "type": "github"
- },
- "original": {
- "owner": "NixOS",
- "repo": "nixpkgs",
- "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
- "type": "github"
- }
- },
- "nixpkgs-stable": {
- "locked": {
- "lastModified": 1710695816,
- "narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=",
- "owner": "NixOS",
- "repo": "nixpkgs",
- "rev": "614b4613980a522ba49f0d194531beddbb7220d3",
- "type": "github"
- },
- "original": {
- "owner": "NixOS",
- "ref": "nixos-23.11",
- "repo": "nixpkgs",
- "type": "github"
- }
- },
- "nixpkgs_2": {
- "locked": {
- "lastModified": 1713361204,
- "narHash": "sha256-TA6EDunWTkc5FvDCqU3W2T3SFn0gRZqh6D/hJnM02MM=",
+ "lastModified": 1750441195,
+ "narHash": "sha256-yke+pm+MdgRb6c0dPt8MgDhv7fcBbdjmv1ZceNTyzKg=",
"owner": "cachix",
"repo": "devenv-nixpkgs",
- "rev": "285676e87ad9f0ca23d8714a6ab61e7e027020c6",
+ "rev": "0ceffe312871b443929ff3006960d29b120dc627",
"type": "github"
},
"original": {
@@ -360,110 +211,42 @@
"type": "github"
}
},
- "nixpkgs_3": {
- "locked": {
- "lastModified": 1725910328,
- "narHash": "sha256-n9pCtzGZ0httmTwMuEbi5E78UQ4ZbQMr1pzi5N0LAG8=",
- "owner": "NixOS",
- "repo": "nixpkgs",
- "rev": "5775c2583f1801df7b790bf7f7d710a19bac66f4",
- "type": "github"
- },
- "original": {
- "owner": "NixOS",
- "ref": "nixpkgs-unstable",
- "repo": "nixpkgs",
- "type": "github"
- }
- },
- "poetry2nix": {
- "inputs": {
- "flake-utils": "flake-utils",
- "nix-github-actions": "nix-github-actions",
- "nixpkgs": [
- "devenv",
- "cachix",
- "devenv",
- "nixpkgs"
- ]
- },
+ "nixpkgs-lib": {
"locked": {
- "lastModified": 1692876271,
- "narHash": "sha256-IXfZEkI0Mal5y1jr6IRWMqK8GW2/f28xJenZIPQqkY0=",
+ "lastModified": 1751159883,
+ "narHash": "sha256-urW/Ylk9FIfvXfliA1ywh75yszAbiTEVgpPeinFyVZo=",
"owner": "nix-community",
- "repo": "poetry2nix",
- "rev": "d5006be9c2c2417dafb2e2e5034d83fabd207ee3",
+ "repo": "nixpkgs.lib",
+ "rev": "14a40a1d7fb9afa4739275ac642ed7301a9ba1ab",
"type": "github"
},
"original": {
"owner": "nix-community",
- "repo": "poetry2nix",
+ "repo": "nixpkgs.lib",
"type": "github"
}
},
- "pre-commit-hooks": {
- "inputs": {
- "flake-compat": [
- "devenv",
- "flake-compat"
- ],
- "flake-utils": "flake-utils_2",
- "gitignore": "gitignore",
- "nixpkgs": [
- "devenv",
- "nixpkgs"
- ],
- "nixpkgs-stable": "nixpkgs-stable"
- },
+ "nixpkgs_2": {
"locked": {
- "lastModified": 1713775815,
- "narHash": "sha256-Wu9cdYTnGQQwtT20QQMg7jzkANKQjwBD9iccfGKkfls=",
- "owner": "cachix",
- "repo": "pre-commit-hooks.nix",
- "rev": "2ac4dcbf55ed43f3be0bae15e181f08a57af24a4",
+ "lastModified": 1753939845,
+ "narHash": "sha256-K2ViRJfdVGE8tpJejs8Qpvvejks1+A4GQej/lBk5y7I=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "94def634a20494ee057c76998843c015909d6311",
"type": "github"
},
"original": {
- "owner": "cachix",
- "repo": "pre-commit-hooks.nix",
+ "owner": "NixOS",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"devenv": "devenv",
- "flake-parts": "flake-parts",
- "nixpkgs": "nixpkgs_3"
- }
- },
- "systems": {
- "locked": {
- "lastModified": 1681028828,
- "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
- "owner": "nix-systems",
- "repo": "default",
- "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
- "type": "github"
- },
- "original": {
- "owner": "nix-systems",
- "repo": "default",
- "type": "github"
- }
- },
- "systems_2": {
- "locked": {
- "lastModified": 1681028828,
- "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
- "owner": "nix-systems",
- "repo": "default",
- "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
- "type": "github"
- },
- "original": {
- "owner": "nix-systems",
- "repo": "default",
- "type": "github"
+ "flake-parts": "flake-parts_2",
+ "nixpkgs": "nixpkgs_2"
}
}
},
diff --git a/vendor/github.com/sagikazarmark/locafero/flake.nix b/vendor/github.com/sagikazarmark/locafero/flake.nix
index 312f1ec8c..bdb10dbe4 100644
--- a/vendor/github.com/sagikazarmark/locafero/flake.nix
+++ b/vendor/github.com/sagikazarmark/locafero/flake.nix
@@ -1,64 +1,42 @@
{
- description = "Finder library for Afero";
-
inputs = {
- nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
+ nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-parts.url = "github:hercules-ci/flake-parts";
devenv.url = "github:cachix/devenv";
};
- outputs = inputs@{ flake-parts, ... }:
+ outputs =
+ inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
imports = [
inputs.devenv.flakeModule
];
- systems = [ "x86_64-linux" "aarch64-darwin" ];
-
- perSystem = { config, self', inputs', pkgs, system, ... }: rec {
- devenv.shells = {
- default = {
- languages = {
- go.enable = true;
- go.package = pkgs.lib.mkDefault pkgs.go_1_23;
- };
-
- packages = with pkgs; [
- just
-
- golangci-lint
- ];
-
- # https://github.com/cachix/devenv/issues/528#issuecomment-1556108767
- containers = pkgs.lib.mkForce { };
- };
-
- ci = devenv.shells.default;
-
- ci_1_21 = {
- imports = [ devenv.shells.ci ];
-
- languages = {
- go.package = pkgs.go_1_21;
- };
- };
+ systems = [
+ "x86_64-linux"
+ "aarch64-darwin"
+ ];
- ci_1_22 = {
- imports = [ devenv.shells.ci ];
+ perSystem =
+ { pkgs, ... }:
+ {
+ devenv.shells = {
+ default = {
+ languages = {
+ go.enable = true;
+ go.package = pkgs.lib.mkDefault pkgs.go_1_24;
+ };
- languages = {
- go.package = pkgs.go_1_22;
- };
- };
+ packages = with pkgs; [
+ just
- ci_1_23 = {
- imports = [ devenv.shells.ci ];
+ golangci-lint
+ ];
- languages = {
- go.package = pkgs.go_1_23;
+ # https://github.com/cachix/devenv/issues/528#issuecomment-1556108767
+ containers = pkgs.lib.mkForce { };
};
};
};
- };
};
}
diff --git a/vendor/github.com/sagikazarmark/locafero/justfile b/vendor/github.com/sagikazarmark/locafero/justfile
index 00a88850c..bac5e75db 100644
--- a/vendor/github.com/sagikazarmark/locafero/justfile
+++ b/vendor/github.com/sagikazarmark/locafero/justfile
@@ -2,10 +2,13 @@ default:
just --list
test:
- go test -race -v ./...
+ go test -count 10 -shuffle on -race -v ./...
+
+fuzz:
+ go test -race -v -fuzz=Fuzz -fuzztime=60s ./...
lint:
golangci-lint run
fmt:
- golangci-lint run --fix
+ golangci-lint fmt
diff --git a/vendor/github.com/sourcegraph/conc/Makefile b/vendor/github.com/sourcegraph/conc/Makefile
new file mode 100644
index 000000000..3e0720a12
--- /dev/null
+++ b/vendor/github.com/sourcegraph/conc/Makefile
@@ -0,0 +1,24 @@
+.DEFAULT_GOAL := help
+
+GO_BIN ?= $(shell go env GOPATH)/bin
+
+.PHONY: help
+help:
+ @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
+
+$(GO_BIN)/golangci-lint:
+ @echo "==> Installing golangci-lint within "${GO_BIN}""
+ @go install -v github.com/golangci/golangci-lint/cmd/golangci-lint@latest
+
+.PHONY: lint
+lint: $(GO_BIN)/golangci-lint ## Run linting on Go files
+ @echo "==> Linting Go source files"
+ @golangci-lint run -v --fix -c .golangci.yml ./...
+
+.PHONY: test
+test: ## Run tests
+ go test -race -v ./... -coverprofile ./coverage.txt
+
+.PHONY: bench
+bench: ## Run benchmarks. See https://pkg.go.dev/cmd/go#hdr-Testing_flags
+ go test ./... -bench . -benchtime 5s -timeout 0 -run=XXX -cpu 1 -benchmem
diff --git a/vendor/github.com/sourcegraph/conc/internal/multierror/multierror_go119.go b/vendor/github.com/sourcegraph/conc/internal/multierror/multierror_go119.go
deleted file mode 100644
index 7087e32a8..000000000
--- a/vendor/github.com/sourcegraph/conc/internal/multierror/multierror_go119.go
+++ /dev/null
@@ -1,10 +0,0 @@
-//go:build !go1.20
-// +build !go1.20
-
-package multierror
-
-import "go.uber.org/multierr"
-
-var (
- Join = multierr.Combine
-)
diff --git a/vendor/github.com/sourcegraph/conc/internal/multierror/multierror_go120.go b/vendor/github.com/sourcegraph/conc/internal/multierror/multierror_go120.go
deleted file mode 100644
index 39cff829a..000000000
--- a/vendor/github.com/sourcegraph/conc/internal/multierror/multierror_go120.go
+++ /dev/null
@@ -1,10 +0,0 @@
-//go:build go1.20
-// +build go1.20
-
-package multierror
-
-import "errors"
-
-var (
- Join = errors.Join
-)
diff --git a/vendor/github.com/sourcegraph/conc/iter/iter.go b/vendor/github.com/sourcegraph/conc/iter/iter.go
deleted file mode 100644
index 124b4f940..000000000
--- a/vendor/github.com/sourcegraph/conc/iter/iter.go
+++ /dev/null
@@ -1,85 +0,0 @@
-package iter
-
-import (
- "runtime"
- "sync/atomic"
-
- "github.com/sourcegraph/conc"
-)
-
-// defaultMaxGoroutines returns the default maximum number of
-// goroutines to use within this package.
-func defaultMaxGoroutines() int { return runtime.GOMAXPROCS(0) }
-
-// Iterator can be used to configure the behaviour of ForEach
-// and ForEachIdx. The zero value is safe to use with reasonable
-// defaults.
-//
-// Iterator is also safe for reuse and concurrent use.
-type Iterator[T any] struct {
- // MaxGoroutines controls the maximum number of goroutines
- // to use on this Iterator's methods.
- //
- // If unset, MaxGoroutines defaults to runtime.GOMAXPROCS(0).
- MaxGoroutines int
-}
-
-// ForEach executes f in parallel over each element in input.
-//
-// It is safe to mutate the input parameter, which makes it
-// possible to map in place.
-//
-// ForEach always uses at most runtime.GOMAXPROCS goroutines.
-// It takes roughly 2µs to start up the goroutines and adds
-// an overhead of roughly 50ns per element of input. For
-// a configurable goroutine limit, use a custom Iterator.
-func ForEach[T any](input []T, f func(*T)) { Iterator[T]{}.ForEach(input, f) }
-
-// ForEach executes f in parallel over each element in input,
-// using up to the Iterator's configured maximum number of
-// goroutines.
-//
-// It is safe to mutate the input parameter, which makes it
-// possible to map in place.
-//
-// It takes roughly 2µs to start up the goroutines and adds
-// an overhead of roughly 50ns per element of input.
-func (iter Iterator[T]) ForEach(input []T, f func(*T)) {
- iter.ForEachIdx(input, func(_ int, t *T) {
- f(t)
- })
-}
-
-// ForEachIdx is the same as ForEach except it also provides the
-// index of the element to the callback.
-func ForEachIdx[T any](input []T, f func(int, *T)) { Iterator[T]{}.ForEachIdx(input, f) }
-
-// ForEachIdx is the same as ForEach except it also provides the
-// index of the element to the callback.
-func (iter Iterator[T]) ForEachIdx(input []T, f func(int, *T)) {
- if iter.MaxGoroutines == 0 {
- // iter is a value receiver and is hence safe to mutate
- iter.MaxGoroutines = defaultMaxGoroutines()
- }
-
- numInput := len(input)
- if iter.MaxGoroutines > numInput {
- // No more concurrent tasks than the number of input items.
- iter.MaxGoroutines = numInput
- }
-
- var idx atomic.Int64
- // Create the task outside the loop to avoid extra closure allocations.
- task := func() {
- i := int(idx.Add(1) - 1)
- for ; i < numInput; i = int(idx.Add(1) - 1) {
- f(i, &input[i])
- }
- }
-
- var wg conc.WaitGroup
- for i := 0; i < iter.MaxGoroutines; i++ {
- wg.Go(task)
- }
- wg.Wait()
-}
diff --git a/vendor/github.com/sourcegraph/conc/iter/map.go b/vendor/github.com/sourcegraph/conc/iter/map.go
deleted file mode 100644
index efbe6bfaf..000000000
--- a/vendor/github.com/sourcegraph/conc/iter/map.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package iter
-
-import (
- "sync"
-
- "github.com/sourcegraph/conc/internal/multierror"
-)
-
-// Mapper is an Iterator with a result type R. It can be used to configure
-// the behaviour of Map and MapErr. The zero value is safe to use with
-// reasonable defaults.
-//
-// Mapper is also safe for reuse and concurrent use.
-type Mapper[T, R any] Iterator[T]
-
-// Map applies f to each element of input, returning the mapped result.
-//
-// Map always uses at most runtime.GOMAXPROCS goroutines. For a configurable
-// goroutine limit, use a custom Mapper.
-func Map[T, R any](input []T, f func(*T) R) []R {
- return Mapper[T, R]{}.Map(input, f)
-}
-
-// Map applies f to each element of input, returning the mapped result.
-//
-// Map uses up to the configured Mapper's maximum number of goroutines.
-func (m Mapper[T, R]) Map(input []T, f func(*T) R) []R {
- res := make([]R, len(input))
- Iterator[T](m).ForEachIdx(input, func(i int, t *T) {
- res[i] = f(t)
- })
- return res
-}
-
-// MapErr applies f to each element of the input, returning the mapped result
-// and a combined error of all returned errors.
-//
-// Map always uses at most runtime.GOMAXPROCS goroutines. For a configurable
-// goroutine limit, use a custom Mapper.
-func MapErr[T, R any](input []T, f func(*T) (R, error)) ([]R, error) {
- return Mapper[T, R]{}.MapErr(input, f)
-}
-
-// MapErr applies f to each element of the input, returning the mapped result
-// and a combined error of all returned errors.
-//
-// Map uses up to the configured Mapper's maximum number of goroutines.
-func (m Mapper[T, R]) MapErr(input []T, f func(*T) (R, error)) ([]R, error) {
- var (
- res = make([]R, len(input))
- errMux sync.Mutex
- errs error
- )
- Iterator[T](m).ForEachIdx(input, func(i int, t *T) {
- var err error
- res[i], err = f(t)
- if err != nil {
- errMux.Lock()
- // TODO: use stdlib errors once multierrors land in go 1.20
- errs = multierror.Join(errs, err)
- errMux.Unlock()
- }
- })
- return res, errs
-}
diff --git a/vendor/github.com/sourcegraph/conc/pool/context_pool.go b/vendor/github.com/sourcegraph/conc/pool/context_pool.go
new file mode 100644
index 000000000..85c34e5ae
--- /dev/null
+++ b/vendor/github.com/sourcegraph/conc/pool/context_pool.go
@@ -0,0 +1,104 @@
+package pool
+
+import (
+ "context"
+)
+
+// ContextPool is a pool that runs tasks that take a context.
+// A new ContextPool should be created with `New().WithContext(ctx)`.
+//
+// The configuration methods (With*) will panic if they are used after calling
+// Go() for the first time.
+type ContextPool struct {
+ errorPool ErrorPool
+
+ ctx context.Context
+ cancel context.CancelFunc
+
+ cancelOnError bool
+}
+
+// Go submits a task. If it returns an error, the error will be
+// collected and returned by Wait(). If all goroutines in the pool
+// are busy, a call to Go() will block until the task can be started.
+func (p *ContextPool) Go(f func(ctx context.Context) error) {
+ p.errorPool.Go(func() error {
+ if p.cancelOnError {
+ // If we are cancelling on error, then we also want to cancel if a
+ // panic is raised. To do this, we need to recover, cancel, and then
+ // re-throw the caught panic.
+ defer func() {
+ if r := recover(); r != nil {
+ p.cancel()
+ panic(r)
+ }
+ }()
+ }
+
+ err := f(p.ctx)
+ if err != nil && p.cancelOnError {
+ // Leaky abstraction warning: We add the error directly because
+ // otherwise, canceling could cause another goroutine to exit and
+ // return an error before this error was added, which breaks the
+ // expectations of WithFirstError().
+ p.errorPool.addErr(err)
+ p.cancel()
+ return nil
+ }
+ return err
+ })
+}
+
+// Wait cleans up all spawned goroutines, propagates any panics, and
+// returns an error if any of the tasks errored.
+func (p *ContextPool) Wait() error {
+ // Make sure we call cancel after pool is done to avoid memory leakage.
+ defer p.cancel()
+ return p.errorPool.Wait()
+}
+
+// WithFirstError configures the pool to only return the first error
+// returned by a task. By default, Wait() will return a combined error.
+// This is particularly useful for (*ContextPool).WithCancelOnError(),
+// where all errors after the first are likely to be context.Canceled.
+func (p *ContextPool) WithFirstError() *ContextPool {
+ p.panicIfInitialized()
+ p.errorPool.WithFirstError()
+ return p
+}
+
+// WithCancelOnError configures the pool to cancel its context as soon as
+// any task returns an error or panics. By default, the pool's context is not
+// canceled until the parent context is canceled.
+//
+// In this case, all errors returned from the pool after the first will
+// likely be context.Canceled - you may want to also use
+// (*ContextPool).WithFirstError() to configure the pool to only return
+// the first error.
+func (p *ContextPool) WithCancelOnError() *ContextPool {
+ p.panicIfInitialized()
+ p.cancelOnError = true
+ return p
+}
+
+// WithFailFast is an alias for the combination of WithFirstError and
+// WithCancelOnError. By default, the errors from all tasks are returned and
+// the pool's context is not canceled until the parent context is canceled.
+func (p *ContextPool) WithFailFast() *ContextPool {
+ p.panicIfInitialized()
+ p.WithFirstError()
+ p.WithCancelOnError()
+ return p
+}
+
+// WithMaxGoroutines limits the number of goroutines in a pool.
+// Defaults to unlimited. Panics if n < 1.
+func (p *ContextPool) WithMaxGoroutines(n int) *ContextPool {
+ p.panicIfInitialized()
+ p.errorPool.WithMaxGoroutines(n)
+ return p
+}
+
+func (p *ContextPool) panicIfInitialized() {
+ p.errorPool.panicIfInitialized()
+}
diff --git a/vendor/github.com/sourcegraph/conc/pool/error_pool.go b/vendor/github.com/sourcegraph/conc/pool/error_pool.go
new file mode 100644
index 000000000..e1789e61b
--- /dev/null
+++ b/vendor/github.com/sourcegraph/conc/pool/error_pool.go
@@ -0,0 +1,100 @@
+package pool
+
+import (
+ "context"
+ "errors"
+ "sync"
+)
+
+// ErrorPool is a pool that runs tasks that may return an error.
+// Errors are collected and returned by Wait().
+//
+// The configuration methods (With*) will panic if they are used after calling
+// Go() for the first time.
+//
+// A new ErrorPool should be created using `New().WithErrors()`.
+type ErrorPool struct {
+ pool Pool
+
+ onlyFirstError bool
+
+ mu sync.Mutex
+ errs []error
+}
+
+// Go submits a task to the pool. If all goroutines in the pool
+// are busy, a call to Go() will block until the task can be started.
+func (p *ErrorPool) Go(f func() error) {
+ p.pool.Go(func() {
+ p.addErr(f())
+ })
+}
+
+// Wait cleans up any spawned goroutines, propagating any panics and
+// returning any errors from tasks.
+func (p *ErrorPool) Wait() error {
+ p.pool.Wait()
+
+ errs := p.errs
+ p.errs = nil // reset errs
+
+ if len(errs) == 0 {
+ return nil
+ } else if p.onlyFirstError {
+ return errs[0]
+ } else {
+ return errors.Join(errs...)
+ }
+}
+
+// WithContext converts the pool to a ContextPool for tasks that should
+// run under the same context, such that they each respect shared cancellation.
+// For example, WithCancelOnError can be configured on the returned pool to
+// signal that all goroutines should be cancelled upon the first error.
+func (p *ErrorPool) WithContext(ctx context.Context) *ContextPool {
+ p.panicIfInitialized()
+ ctx, cancel := context.WithCancel(ctx)
+ return &ContextPool{
+ errorPool: p.deref(),
+ ctx: ctx,
+ cancel: cancel,
+ }
+}
+
+// WithFirstError configures the pool to only return the first error
+// returned by a task. By default, Wait() will return a combined error.
+func (p *ErrorPool) WithFirstError() *ErrorPool {
+ p.panicIfInitialized()
+ p.onlyFirstError = true
+ return p
+}
+
+// WithMaxGoroutines limits the number of goroutines in a pool.
+// Defaults to unlimited. Panics if n < 1.
+func (p *ErrorPool) WithMaxGoroutines(n int) *ErrorPool {
+ p.panicIfInitialized()
+ p.pool.WithMaxGoroutines(n)
+ return p
+}
+
+// deref is a helper that creates a shallow copy of the pool with the same
+// settings. We don't want to just dereference the pointer because that makes
+// the copylock lint angry.
+func (p *ErrorPool) deref() ErrorPool {
+ return ErrorPool{
+ pool: p.pool.deref(),
+ onlyFirstError: p.onlyFirstError,
+ }
+}
+
+func (p *ErrorPool) panicIfInitialized() {
+ p.pool.panicIfInitialized()
+}
+
+func (p *ErrorPool) addErr(err error) {
+ if err != nil {
+ p.mu.Lock()
+ p.errs = append(p.errs, err)
+ p.mu.Unlock()
+ }
+}
diff --git a/vendor/github.com/sourcegraph/conc/pool/pool.go b/vendor/github.com/sourcegraph/conc/pool/pool.go
new file mode 100644
index 000000000..8f4494efb
--- /dev/null
+++ b/vendor/github.com/sourcegraph/conc/pool/pool.go
@@ -0,0 +1,174 @@
+package pool
+
+import (
+ "context"
+ "sync"
+
+ "github.com/sourcegraph/conc"
+)
+
+// New creates a new Pool.
+func New() *Pool {
+ return &Pool{}
+}
+
+// Pool is a pool of goroutines used to execute tasks concurrently.
+//
+// Tasks are submitted with Go(). Once all your tasks have been submitted, you
+// must call Wait() to clean up any spawned goroutines and propagate any
+// panics.
+//
+// Goroutines are started lazily, so creating a new pool is cheap. There will
+// never be more goroutines spawned than there are tasks submitted.
+//
+// The configuration methods (With*) will panic if they are used after calling
+// Go() for the first time.
+//
+// Pool is efficient, but not zero cost. It should not be used for very short
+// tasks. Startup and teardown come with an overhead of around 1µs, and each
+// task has an overhead of around 300ns.
+type Pool struct {
+ handle conc.WaitGroup
+ limiter limiter
+ tasks chan func()
+ initOnce sync.Once
+}
+
+// Go submits a task to be run in the pool. If all goroutines in the pool
+// are busy, a call to Go() will block until the task can be started.
+func (p *Pool) Go(f func()) {
+ p.init()
+
+ if p.limiter == nil {
+ // No limit on the number of goroutines.
+ select {
+ case p.tasks <- f:
+ // A goroutine was available to handle the task.
+ default:
+ // No goroutine was available to handle the task.
+ // Spawn a new one and send it the task.
+ p.handle.Go(func() {
+ p.worker(f)
+ })
+ }
+ } else {
+ select {
+ case p.limiter <- struct{}{}:
+ // If we are below our limit, spawn a new worker rather
+ // than waiting for one to become available.
+ p.handle.Go(func() {
+ p.worker(f)
+ })
+ case p.tasks <- f:
+ // A worker is available and has accepted the task.
+ return
+ }
+ }
+
+}
+
+// Wait cleans up spawned goroutines, propagating any panics that were
+// raised by a tasks.
+func (p *Pool) Wait() {
+ p.init()
+
+ close(p.tasks)
+
+ // After Wait() returns, reset the struct so tasks will be reinitialized on
+ // next use. This better matches the behavior of sync.WaitGroup
+ defer func() { p.initOnce = sync.Once{} }()
+
+ p.handle.Wait()
+}
+
+// MaxGoroutines returns the maximum size of the pool.
+func (p *Pool) MaxGoroutines() int {
+ return p.limiter.limit()
+}
+
+// WithMaxGoroutines limits the number of goroutines in a pool.
+// Defaults to unlimited. Panics if n < 1.
+func (p *Pool) WithMaxGoroutines(n int) *Pool {
+ p.panicIfInitialized()
+ if n < 1 {
+ panic("max goroutines in a pool must be greater than zero")
+ }
+ p.limiter = make(limiter, n)
+ return p
+}
+
+// init ensures that the pool is initialized before use. This makes the
+// zero value of the pool usable.
+func (p *Pool) init() {
+ p.initOnce.Do(func() {
+ p.tasks = make(chan func())
+ })
+}
+
+// panicIfInitialized will trigger a panic if a configuration method is called
+// after the pool has started any goroutines for the first time. In the case that
+// new settings are needed, a new pool should be created.
+func (p *Pool) panicIfInitialized() {
+ if p.tasks != nil {
+ panic("pool can not be reconfigured after calling Go() for the first time")
+ }
+}
+
+// WithErrors converts the pool to an ErrorPool so the submitted tasks can
+// return errors.
+func (p *Pool) WithErrors() *ErrorPool {
+ p.panicIfInitialized()
+ return &ErrorPool{
+ pool: p.deref(),
+ }
+}
+
+// deref is a helper that creates a shallow copy of the pool with the same
+// settings. We don't want to just dereference the pointer because that makes
+// the copylock lint angry.
+func (p *Pool) deref() Pool {
+ p.panicIfInitialized()
+ return Pool{
+ limiter: p.limiter,
+ }
+}
+
+// WithContext converts the pool to a ContextPool for tasks that should
+// run under the same context, such that they each respect shared cancellation.
+// For example, WithCancelOnError can be configured on the returned pool to
+// signal that all goroutines should be cancelled upon the first error.
+func (p *Pool) WithContext(ctx context.Context) *ContextPool {
+ p.panicIfInitialized()
+ ctx, cancel := context.WithCancel(ctx)
+ return &ContextPool{
+ errorPool: p.WithErrors().deref(),
+ ctx: ctx,
+ cancel: cancel,
+ }
+}
+
+func (p *Pool) worker(initialFunc func()) {
+ // The only time this matters is if the task panics.
+ // This makes it possible to spin up new workers in that case.
+ defer p.limiter.release()
+
+ if initialFunc != nil {
+ initialFunc()
+ }
+
+ for f := range p.tasks {
+ f()
+ }
+}
+
+type limiter chan struct{}
+
+func (l limiter) limit() int {
+ return cap(l)
+}
+
+func (l limiter) release() {
+ if l != nil {
+ <-l
+ }
+}
diff --git a/vendor/github.com/sourcegraph/conc/pool/result_context_pool.go b/vendor/github.com/sourcegraph/conc/pool/result_context_pool.go
new file mode 100644
index 000000000..6bc30dd63
--- /dev/null
+++ b/vendor/github.com/sourcegraph/conc/pool/result_context_pool.go
@@ -0,0 +1,85 @@
+package pool
+
+import (
+ "context"
+)
+
+// ResultContextPool is a pool that runs tasks that take a context and return a
+// result. The context passed to the task will be canceled if any of the tasks
+// return an error, which makes its functionality different than just capturing
+// a context with the task closure.
+//
+// The configuration methods (With*) will panic if they are used after calling
+// Go() for the first time.
+type ResultContextPool[T any] struct {
+ contextPool ContextPool
+ agg resultAggregator[T]
+ collectErrored bool
+}
+
+// Go submits a task to the pool. If all goroutines in the pool
+// are busy, a call to Go() will block until the task can be started.
+func (p *ResultContextPool[T]) Go(f func(context.Context) (T, error)) {
+ idx := p.agg.nextIndex()
+ p.contextPool.Go(func(ctx context.Context) error {
+ res, err := f(ctx)
+ p.agg.save(idx, res, err != nil)
+ return err
+ })
+}
+
+// Wait cleans up all spawned goroutines, propagates any panics, and
+// returns an error if any of the tasks errored.
+func (p *ResultContextPool[T]) Wait() ([]T, error) {
+ err := p.contextPool.Wait()
+ results := p.agg.collect(p.collectErrored)
+ p.agg = resultAggregator[T]{}
+ return results, err
+}
+
+// WithCollectErrored configures the pool to still collect the result of a task
+// even if the task returned an error. By default, the result of tasks that errored
+// are ignored and only the error is collected.
+func (p *ResultContextPool[T]) WithCollectErrored() *ResultContextPool[T] {
+ p.panicIfInitialized()
+ p.collectErrored = true
+ return p
+}
+
+// WithFirstError configures the pool to only return the first error
+// returned by a task. By default, Wait() will return a combined error.
+func (p *ResultContextPool[T]) WithFirstError() *ResultContextPool[T] {
+ p.panicIfInitialized()
+ p.contextPool.WithFirstError()
+ return p
+}
+
+// WithCancelOnError configures the pool to cancel its context as soon as
+// any task returns an error. By default, the pool's context is not
+// canceled until the parent context is canceled.
+func (p *ResultContextPool[T]) WithCancelOnError() *ResultContextPool[T] {
+ p.panicIfInitialized()
+ p.contextPool.WithCancelOnError()
+ return p
+}
+
+// WithFailFast is an alias for the combination of WithFirstError and
+// WithCancelOnError. By default, the errors from all tasks are returned and
+// the pool's context is not canceled until the parent context is canceled.
+func (p *ResultContextPool[T]) WithFailFast() *ResultContextPool[T] {
+ p.panicIfInitialized()
+ p.contextPool.WithFailFast()
+ return p
+}
+
+// WithMaxGoroutines limits the number of goroutines in a pool.
+// Defaults to unlimited. Panics if n < 1.
+func (p *ResultContextPool[T]) WithMaxGoroutines(n int) *ResultContextPool[T] {
+ p.panicIfInitialized()
+ p.contextPool.WithMaxGoroutines(n)
+ return p
+}
+
+func (p *ResultContextPool[T]) panicIfInitialized() {
+ p.contextPool.panicIfInitialized()
+}
diff --git a/vendor/github.com/sourcegraph/conc/pool/result_error_pool.go b/vendor/github.com/sourcegraph/conc/pool/result_error_pool.go
new file mode 100644
index 000000000..832cd9bb4
--- /dev/null
+++ b/vendor/github.com/sourcegraph/conc/pool/result_error_pool.go
@@ -0,0 +1,80 @@
+package pool
+
+import (
+ "context"
+)
+
+// ResultErrorPool is a pool that executes tasks that return a generic result
+// type and an error. Tasks are executed in the pool with Go(), then the
+// results of the tasks are returned by Wait().
+//
+// The order of the results is guaranteed to be the same as the order the
+// tasks were submitted.
+//
+// The configuration methods (With*) will panic if they are used after calling
+// Go() for the first time.
+type ResultErrorPool[T any] struct {
+ errorPool ErrorPool
+ agg resultAggregator[T]
+ collectErrored bool
+}
+
+// Go submits a task to the pool. If all goroutines in the pool
+// are busy, a call to Go() will block until the task can be started.
+func (p *ResultErrorPool[T]) Go(f func() (T, error)) {
+ idx := p.agg.nextIndex()
+ p.errorPool.Go(func() error {
+ res, err := f()
+ p.agg.save(idx, res, err != nil)
+ return err
+ })
+}
+
+// Wait cleans up any spawned goroutines, propagating any panics and
+// returning the results and any errors from tasks.
+func (p *ResultErrorPool[T]) Wait() ([]T, error) {
+ err := p.errorPool.Wait()
+ results := p.agg.collect(p.collectErrored)
+ p.agg = resultAggregator[T]{} // reset for reuse
+ return results, err
+}
+
+// WithCollectErrored configures the pool to still collect the result of a task
+// even if the task returned an error. By default, the result of tasks that errored
+// are ignored and only the error is collected.
+func (p *ResultErrorPool[T]) WithCollectErrored() *ResultErrorPool[T] {
+ p.panicIfInitialized()
+ p.collectErrored = true
+ return p
+}
+
+// WithContext converts the pool to a ResultContextPool for tasks that should
+// run under the same context, such that they each respect shared cancellation.
+// For example, WithCancelOnError can be configured on the returned pool to
+// signal that all goroutines should be cancelled upon the first error.
+func (p *ResultErrorPool[T]) WithContext(ctx context.Context) *ResultContextPool[T] {
+ p.panicIfInitialized()
+ return &ResultContextPool[T]{
+ contextPool: *p.errorPool.WithContext(ctx),
+ }
+}
+
+// WithFirstError configures the pool to only return the first error
+// returned by a task. By default, Wait() will return a combined error.
+func (p *ResultErrorPool[T]) WithFirstError() *ResultErrorPool[T] {
+ p.panicIfInitialized()
+ p.errorPool.WithFirstError()
+ return p
+}
+
+// WithMaxGoroutines limits the number of goroutines in a pool.
+// Defaults to unlimited. Panics if n < 1.
+func (p *ResultErrorPool[T]) WithMaxGoroutines(n int) *ResultErrorPool[T] {
+ p.panicIfInitialized()
+ p.errorPool.WithMaxGoroutines(n)
+ return p
+}
+
+func (p *ResultErrorPool[T]) panicIfInitialized() {
+ p.errorPool.panicIfInitialized()
+}
diff --git a/vendor/github.com/sourcegraph/conc/pool/result_pool.go b/vendor/github.com/sourcegraph/conc/pool/result_pool.go
new file mode 100644
index 000000000..f73a77261
--- /dev/null
+++ b/vendor/github.com/sourcegraph/conc/pool/result_pool.go
@@ -0,0 +1,142 @@
+package pool
+
+import (
+ "context"
+ "sort"
+ "sync"
+)
+
+// NewWithResults creates a new ResultPool for tasks with a result of type T.
+//
+// The configuration methods (With*) will panic if they are used after calling
+// Go() for the first time.
+func NewWithResults[T any]() *ResultPool[T] {
+ return &ResultPool[T]{
+ pool: *New(),
+ }
+}
+
+// ResultPool is a pool that executes tasks that return a generic result type.
+// Tasks are executed in the pool with Go(), then the results of the tasks are
+// returned by Wait().
+//
+// The order of the results is guaranteed to be the same as the order the
+// tasks were submitted.
+type ResultPool[T any] struct {
+ pool Pool
+ agg resultAggregator[T]
+}
+
+// Go submits a task to the pool. If all goroutines in the pool
+// are busy, a call to Go() will block until the task can be started.
+func (p *ResultPool[T]) Go(f func() T) {
+ idx := p.agg.nextIndex()
+ p.pool.Go(func() {
+ p.agg.save(idx, f(), false)
+ })
+}
+
+// Wait cleans up all spawned goroutines, propagating any panics, and returning
+// a slice of results from tasks that did not panic.
+func (p *ResultPool[T]) Wait() []T {
+ p.pool.Wait()
+ results := p.agg.collect(true)
+ p.agg = resultAggregator[T]{} // reset for reuse
+ return results
+}
+
+// MaxGoroutines returns the maximum size of the pool.
+func (p *ResultPool[T]) MaxGoroutines() int {
+ return p.pool.MaxGoroutines()
+}
+
+// WithErrors converts the pool to an ResultErrorPool so the submitted tasks
+// can return errors.
+func (p *ResultPool[T]) WithErrors() *ResultErrorPool[T] {
+ p.panicIfInitialized()
+ return &ResultErrorPool[T]{
+ errorPool: *p.pool.WithErrors(),
+ }
+}
+
+// WithContext converts the pool to a ResultContextPool for tasks that should
+// run under the same context, such that they each respect shared cancellation.
+// For example, WithCancelOnError can be configured on the returned pool to
+// signal that all goroutines should be cancelled upon the first error.
+func (p *ResultPool[T]) WithContext(ctx context.Context) *ResultContextPool[T] {
+ p.panicIfInitialized()
+ return &ResultContextPool[T]{
+ contextPool: *p.pool.WithContext(ctx),
+ }
+}
+
+// WithMaxGoroutines limits the number of goroutines in a pool.
+// Defaults to unlimited. Panics if n < 1.
+func (p *ResultPool[T]) WithMaxGoroutines(n int) *ResultPool[T] {
+ p.panicIfInitialized()
+ p.pool.WithMaxGoroutines(n)
+ return p
+}
+
+func (p *ResultPool[T]) panicIfInitialized() {
+ p.pool.panicIfInitialized()
+}
+
+// resultAggregator is a utility type that lets us safely append from multiple
+// goroutines. The zero value is valid and ready to use.
+type resultAggregator[T any] struct {
+ mu sync.Mutex
+ len int
+ results []T
+ errored []int
+}
+
+// nextIndex reserves a slot for a result. The returned value should be passed
+// to save() when adding a result to the aggregator.
+func (r *resultAggregator[T]) nextIndex() int {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ nextIdx := r.len
+ r.len += 1
+ return nextIdx
+}
+
+func (r *resultAggregator[T]) save(i int, res T, errored bool) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ if i >= len(r.results) {
+ old := r.results
+ r.results = make([]T, r.len)
+ copy(r.results, old)
+ }
+
+ r.results[i] = res
+
+ if errored {
+ r.errored = append(r.errored, i)
+ }
+}
+
+// collect returns the set of aggregated results.
+func (r *resultAggregator[T]) collect(collectErrored bool) []T {
+ if !r.mu.TryLock() {
+ panic("collect should not be called until all goroutines have exited")
+ }
+
+ if collectErrored || len(r.errored) == 0 {
+ return r.results
+ }
+
+ filtered := r.results[:0]
+ sort.Ints(r.errored)
+ for i, e := range r.errored {
+ if i == 0 {
+ filtered = append(filtered, r.results[:e]...)
+ } else {
+ filtered = append(filtered, r.results[r.errored[i-1]+1:e]...)
+ }
+ }
+ return filtered
+}
diff --git a/vendor/github.com/spf13/afero/.editorconfig b/vendor/github.com/spf13/afero/.editorconfig
index 4492e9f9f..a85749f19 100644
--- a/vendor/github.com/spf13/afero/.editorconfig
+++ b/vendor/github.com/spf13/afero/.editorconfig
@@ -10,3 +10,6 @@ trim_trailing_whitespace = true
[*.go]
indent_style = tab
+
+[{*.yml,*.yaml}]
+indent_size = 2
diff --git a/vendor/github.com/spf13/afero/.golangci.yaml b/vendor/github.com/spf13/afero/.golangci.yaml
index 806289a25..4f359b81a 100644
--- a/vendor/github.com/spf13/afero/.golangci.yaml
+++ b/vendor/github.com/spf13/afero/.golangci.yaml
@@ -1,18 +1,48 @@
-linters-settings:
- gci:
- sections:
- - standard
- - default
- - prefix(github.com/spf13/afero)
+version: "2"
+
+run:
+ timeout: 10m
linters:
- disable-all: true
- enable:
- - gci
- - gofmt
- - gofumpt
- - staticcheck
-
-issues:
- exclude-dirs:
- - gcsfs/internal/stiface
+ enable:
+ - govet
+ - ineffassign
+ - misspell
+ - nolintlint
+ # - revive
+ - staticcheck
+ - unused
+
+ disable:
+ - errcheck
+ # - staticcheck
+
+ settings:
+ misspell:
+ locale: US
+ nolintlint:
+ allow-unused: false # report any unused nolint directives
+ require-specific: false # don't require nolint directives to be specific about which linter is being skipped
+
+ exclusions:
+ paths:
+ - gcsfs/internal/stiface
+
+formatters:
+ enable:
+ - gci
+ - gofmt
+ - gofumpt
+ - goimports
+ - golines
+
+ settings:
+ gci:
+ sections:
+ - standard
+ - default
+ - localmodule
+
+ exclusions:
+ paths:
+ - gcsfs/internal/stiface
diff --git a/vendor/github.com/spf13/afero/README.md b/vendor/github.com/spf13/afero/README.md
index 619af574f..ef67e9a77 100644
--- a/vendor/github.com/spf13/afero/README.md
+++ b/vendor/github.com/spf13/afero/README.md
@@ -1,442 +1,474 @@
-
+
-A FileSystem Abstraction System for Go
-[](https://github.com/spf13/afero/actions/workflows/test.yml) [](https://godoc.org/github.com/spf13/afero) [](https://gitter.im/spf13/afero?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+[](https://github.com/spf13/afero/actions?query=workflow%3ACI)
+[](https://pkg.go.dev/mod/github.com/spf13/afero)
+[](https://goreportcard.com/report/github.com/spf13/afero)
+
-# Overview
-Afero is a filesystem framework providing a simple, uniform and universal API
-interacting with any filesystem, as an abstraction layer providing interfaces,
-types and methods. Afero has an exceptionally clean interface and simple design
-without needless constructors or initialization methods.
+# Afero: The Universal Filesystem Abstraction for Go
-Afero is also a library providing a base set of interoperable backend
-filesystems that make it easy to work with, while retaining all the power
-and benefit of the os and ioutil packages.
+Afero is a powerful and extensible filesystem abstraction system for Go. It provides a single, unified API for interacting with diverse filesystems—including the local disk, memory, archives, and network storage.
-Afero provides significant improvements over using the os package alone, most
-notably the ability to create mock and testing filesystems without relying on the disk.
+Afero acts as a drop-in replacement for the standard `os` package, enabling you to write modular code that is agnostic to the underlying storage, dramatically simplifies testing, and allows for sophisticated architectural patterns through filesystem composition.
-It is suitable for use in any situation where you would consider using the OS
-package as it provides an additional abstraction that makes it easy to use a
-memory backed file system during testing. It also adds support for the http
-filesystem for full interoperability.
+## Why Afero?
+Afero elevates filesystem interaction beyond simple file reading and writing, offering solutions for testability, flexibility, and advanced architecture.
-## Afero Features
+🔑 **Key Features:**
-* A single consistent API for accessing a variety of filesystems
-* Interoperation between a variety of file system types
-* A set of interfaces to encourage and enforce interoperability between backends
-* An atomic cross platform memory backed file system
-* Support for compositional (union) file systems by combining multiple file systems acting as one
-* Specialized backends which modify existing filesystems (Read Only, Regexp filtered)
-* A set of utility functions ported from io, ioutil & hugo to be afero aware
-* Wrapper for go 1.16 filesystem abstraction `io/fs.FS`
+* **Universal API:** Write your code once. Run it against the local OS, in-memory storage, ZIP/TAR archives, or remote systems (SFTP, GCS).
+* **Ultimate Testability:** Utilize `MemMapFs`, a fully concurrent-safe, read/write in-memory filesystem. Write fast, isolated, and reliable unit tests without touching the physical disk or worrying about cleanup.
+* **Powerful Composition:** Afero's hidden superpower. Layer filesystems on top of each other to create sophisticated behaviors:
+ * **Sandboxing:** Use `CopyOnWriteFs` to create temporary scratch spaces that isolate changes from the base filesystem.
+ * **Caching:** Use `CacheOnReadFs` to automatically layer a fast cache (like memory) over a slow backend (like a network drive).
+ * **Security Jails:** Use `BasePathFs` to restrict application access to a specific subdirectory (chroot).
+* **`os` Package Compatibility:** Afero mirrors the functions in the standard `os` package, making adoption and refactoring seamless.
+* **`io/fs` Compatibility:** Fully compatible with the Go standard library's `io/fs` interfaces.
-# Using Afero
+## Installation
-Afero is easy to use and easier to adopt.
-
-A few different ways you could use Afero:
-
-* Use the interfaces alone to define your own file system.
-* Wrapper for the OS packages.
-* Define different filesystems for different parts of your application.
-* Use Afero for mock filesystems while testing
-
-## Step 1: Install Afero
-
-First use go get to install the latest version of the library.
-
- $ go get github.com/spf13/afero
+```bash
+go get github.com/spf13/afero
+```
-Next include Afero in your application.
```go
import "github.com/spf13/afero"
```
-## Step 2: Declare a backend
+## Quick Start: The Power of Abstraction
+
+The core of Afero is the `afero.Fs` interface. By designing your functions to accept this interface rather than calling `os.*` functions directly, your code instantly becomes more flexible and testable.
+
+### 1. Refactor Your Code
+
+Change functions that rely on the `os` package to accept `afero.Fs`.
-First define a package variable and set it to a pointer to a filesystem.
```go
-var AppFs = afero.NewMemMapFs()
+// Before: Coupled to the OS and difficult to test
+// func ProcessConfiguration(path string) error {
+// data, err := os.ReadFile(path)
+// ...
+// }
-or
+import "github.com/spf13/afero"
-var AppFs = afero.NewOsFs()
+// After: Decoupled, flexible, and testable
+func ProcessConfiguration(fs afero.Fs, path string) error {
+ // Use Afero utility functions which mirror os/ioutil
+ data, err := afero.ReadFile(fs, path)
+ // ... process the data
+ return err
+}
```
-It is important to note that if you repeat the composite literal you
-will be using a completely new and isolated filesystem. In the case of
-OsFs it will still use the same underlying filesystem but will reduce
-the ability to drop in other filesystems as desired.
-## Step 3: Use it like you would the OS package
+### 2. Usage in Production
-Throughout your application use any function and method like you normally
-would.
+In your production environment, inject the `OsFs` backend, which wraps the standard operating system calls.
-So if my application before had:
```go
-os.Open("/tmp/foo")
-```
-We would replace it with:
-```go
-AppFs.Open("/tmp/foo")
+func main() {
+ // Use the real OS filesystem
+ AppFs := afero.NewOsFs()
+ ProcessConfiguration(AppFs, "/etc/myapp.conf")
+}
```
-`AppFs` being the variable we defined above.
-
+### 3. Usage in Testing
-## List of all available functions
+In your tests, inject `MemMapFs`. This provides a blazing-fast, isolated, in-memory filesystem that requires no disk I/O and no cleanup.
-File System Methods Available:
```go
-Chmod(name string, mode os.FileMode) : error
-Chown(name string, uid, gid int) : error
-Chtimes(name string, atime time.Time, mtime time.Time) : error
-Create(name string) : File, error
-Mkdir(name string, perm os.FileMode) : error
-MkdirAll(path string, perm os.FileMode) : error
-Name() : string
-Open(name string) : File, error
-OpenFile(name string, flag int, perm os.FileMode) : File, error
-Remove(name string) : error
-RemoveAll(path string) : error
-Rename(oldname, newname string) : error
-Stat(name string) : os.FileInfo, error
-```
-File Interfaces and Methods Available:
-```go
-io.Closer
-io.Reader
-io.ReaderAt
-io.Seeker
-io.Writer
-io.WriterAt
-
-Name() : string
-Readdir(count int) : []os.FileInfo, error
-Readdirnames(n int) : []string, error
-Stat() : os.FileInfo, error
-Sync() : error
-Truncate(size int64) : error
-WriteString(s string) : ret int, err error
+func TestProcessConfiguration(t *testing.T) {
+ // Use the in-memory filesystem
+ AppFs := afero.NewMemMapFs()
+
+ // Pre-populate the memory filesystem for the test
+ configPath := "/test/config.json"
+ afero.WriteFile(AppFs, configPath, []byte(`{"feature": true}`), 0644)
+
+ // Run the test entirely in memory
+ err := ProcessConfiguration(AppFs, configPath)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
```
-In some applications it may make sense to define a new package that
-simply exports the file system variable for easy access from anywhere.
-## Using Afero's utility functions
+## Afero's Superpower: Composition
-Afero provides a set of functions to make it easier to use the underlying file systems.
-These functions have been primarily ported from io & ioutil with some developed for Hugo.
+Afero's most unique feature is its ability to combine filesystems. This allows you to build complex behaviors out of simple components, keeping your application logic clean.
-The afero utilities support all afero compatible backends.
+### Example 1: Sandboxing with Copy-on-Write
-The list of utilities includes:
+Create a temporary environment where an application can "modify" system files without affecting the actual disk.
```go
-DirExists(path string) (bool, error)
-Exists(path string) (bool, error)
-FileContainsBytes(filename string, subslice []byte) (bool, error)
-GetTempDir(subPath string) string
-IsDir(path string) (bool, error)
-IsEmpty(path string) (bool, error)
-ReadDir(dirname string) ([]os.FileInfo, error)
-ReadFile(filename string) ([]byte, error)
-SafeWriteReader(path string, r io.Reader) (err error)
-TempDir(dir, prefix string) (name string, err error)
-TempFile(dir, prefix string) (f File, err error)
-Walk(root string, walkFn filepath.WalkFunc) error
-WriteFile(filename string, data []byte, perm os.FileMode) error
-WriteReader(path string, r io.Reader) (err error)
-```
-For a complete list see [Afero's GoDoc](https://godoc.org/github.com/spf13/afero)
+// 1. The base layer is the real OS, made read-only for safety.
+baseFs := afero.NewReadOnlyFs(afero.NewOsFs())
-They are available under two different approaches to use. You can either call
-them directly where the first parameter of each function will be the file
-system, or you can declare a new `Afero`, a custom type used to bind these
-functions as methods to a given filesystem.
+// 2. The overlay layer is a temporary in-memory filesystem for changes.
+overlayFs := afero.NewMemMapFs()
-### Calling utilities directly
+// 3. Combine them. Reads fall through to the base; writes only hit the overlay.
+sandboxFs := afero.NewCopyOnWriteFs(baseFs, overlayFs)
-```go
-fs := new(afero.MemMapFs)
-f, err := afero.TempFile(fs,"", "ioutil-test")
+// The application can now "modify" /etc/hosts, but the changes are isolated in memory.
+afero.WriteFile(sandboxFs, "/etc/hosts", []byte("127.0.0.1 sandboxed-app"), 0644)
+// The real /etc/hosts on disk is untouched.
```
-### Calling via Afero
+### Example 2: Caching a Slow Filesystem
-```go
-fs := afero.NewMemMapFs()
-afs := &afero.Afero{Fs: fs}
-f, err := afs.TempFile("", "ioutil-test")
-```
+Improve performance by layering a fast cache (like memory) over a slow backend (like a network drive or cloud storage).
-## Using Afero for Testing
+```go
+import "time"
-There is a large benefit to using a mock filesystem for testing. It has a
-completely blank state every time it is initialized and can be easily
-reproducible regardless of OS. You could create files to your heart’s content
-and the file access would be fast while also saving you from all the annoying
-issues with deleting temporary files, Windows file locking, etc. The MemMapFs
-backend is perfect for testing.
+// Assume 'remoteFs' is a slow backend (e.g., SFTP or GCS)
+var remoteFs afero.Fs
-* Much faster than performing I/O operations on disk
-* Avoid security issues and permissions
-* Far more control. 'rm -rf /' with confidence
-* Test setup is far more easier to do
-* No test cleanup needed
+// 'cacheFs' is a fast in-memory backend
+cacheFs := afero.NewMemMapFs()
-One way to accomplish this is to define a variable as mentioned above.
-In your application this will be set to afero.NewOsFs() during testing you
-can set it to afero.NewMemMapFs().
+// Create the caching layer. Cache items for 5 minutes upon first read.
+cachedFs := afero.NewCacheOnReadFs(remoteFs, cacheFs, 5*time.Minute)
-It wouldn't be uncommon to have each test initialize a blank slate memory
-backend. To do this I would define my `appFS = afero.NewOsFs()` somewhere
-appropriate in my application code. This approach ensures that Tests are order
-independent, with no test relying on the state left by an earlier test.
+// The first read is slow (fetches from remote, then caches)
+data1, _ := afero.ReadFile(cachedFs, "data.json")
-Then in my tests I would initialize a new MemMapFs for each test:
-```go
-func TestExist(t *testing.T) {
- appFS := afero.NewMemMapFs()
- // create test files and directories
- appFS.MkdirAll("src/a", 0755)
- afero.WriteFile(appFS, "src/a/b", []byte("file b"), 0644)
- afero.WriteFile(appFS, "src/c", []byte("file c"), 0644)
- name := "src/c"
- _, err := appFS.Stat(name)
- if os.IsNotExist(err) {
- t.Errorf("file \"%s\" does not exist.\n", name)
- }
-}
+// The second read is instant (serves from memory cache)
+data2, _ := afero.ReadFile(cachedFs, "data.json")
```
-# Available Backends
+### Example 3: Security Jails (chroot)
-## Operating System Native
+Restrict an application component's access to a specific subdirectory.
-### OsFs
+```go
+osFs := afero.NewOsFs()
-The first is simply a wrapper around the native OS calls. This makes it
-very easy to use as all of the calls are the same as the existing OS
-calls. It also makes it trivial to have your code use the OS during
-operation and a mock filesystem during testing or as needed.
+// Create a filesystem rooted at /home/user/public
+// The application cannot access anything above this directory.
+jailedFs := afero.NewBasePathFs(osFs, "/home/user/public")
-```go
-appfs := afero.NewOsFs()
-appfs.MkdirAll("src/a", 0755)
+// To the application, this is reading "/"
+// In reality, it's reading "/home/user/public/"
+dirInfo, err := afero.ReadDir(jailedFs, "/")
+
+// Attempts to access parent directories fail
+_, err = jailedFs.Open("../secrets.txt") // Returns an error
```
-## Memory Backed Storage
+## Real-World Use Cases
-### MemMapFs
+### Build Cloud-Agnostic Applications
-Afero also provides a fully atomic memory backed filesystem perfect for use in
-mocking and to speed up unnecessary disk io when persistence isn’t
-necessary. It is fully concurrent and will work within go routines
-safely.
+Write applications that seamlessly work with different storage backends:
```go
-mm := afero.NewMemMapFs()
-mm.MkdirAll("src/a", 0755)
-```
+type DocumentProcessor struct {
+ fs afero.Fs
+}
-#### InMemoryFile
+func NewDocumentProcessor(fs afero.Fs) *DocumentProcessor {
+ return &DocumentProcessor{fs: fs}
+}
-As part of MemMapFs, Afero also provides an atomic, fully concurrent memory
-backed file implementation. This can be used in other memory backed file
-systems with ease. Plans are to add a radix tree memory stored file
-system using InMemoryFile.
+func (p *DocumentProcessor) Process(inputPath, outputPath string) error {
+ // This code works whether fs is local disk, cloud storage, or memory
+ content, err := afero.ReadFile(p.fs, inputPath)
+ if err != nil {
+ return err
+ }
+
+ processed := processContent(content)
+ return afero.WriteFile(p.fs, outputPath, processed, 0644)
+}
-## Network Interfaces
+// Use with local filesystem
+processor := NewDocumentProcessor(afero.NewOsFs())
-### SftpFs
+// Use with Google Cloud Storage
+processor := NewDocumentProcessor(gcsFS)
-Afero has experimental support for secure file transfer protocol (sftp). Which can
-be used to perform file operations over a encrypted channel.
+// Use with in-memory filesystem for testing
+processor := NewDocumentProcessor(afero.NewMemMapFs())
+```
-### GCSFs
+### Treating Archives as Filesystems
-Afero has experimental support for Google Cloud Storage (GCS). You can either set the
-`GOOGLE_APPLICATION_CREDENTIALS_JSON` env variable to your JSON credentials or use `opts` in
-`NewGcsFS` to configure access to your GCS bucket.
+Read files directly from `.zip` or `.tar` archives without unpacking them to disk first.
-Some known limitations of the existing implementation:
-* No Chmod support - The GCS ACL could probably be mapped to *nix style permissions but that would add another level of complexity and is ignored in this version.
-* No Chtimes support - Could be simulated with attributes (gcs a/m-times are set implicitly) but that's is left for another version.
-* Not thread safe - Also assumes all file operations are done through the same instance of the GcsFs. File operations between different GcsFs instances are not guaranteed to be consistent.
+```go
+import (
+ "archive/zip"
+ "github.com/spf13/afero/zipfs"
+)
+
+// Assume 'zipReader' is a *zip.Reader initialized from a file or memory
+var zipReader *zip.Reader
+// Create a read-only ZipFs
+archiveFS := zipfs.New(zipReader)
-## Filtering Backends
+// Read a file from within the archive using the standard Afero API
+content, err := afero.ReadFile(archiveFS, "/docs/readme.md")
+```
-### BasePathFs
+### Serving Any Filesystem over HTTP
-The BasePathFs restricts all operations to a given path within an Fs.
-The given file name to the operations on this Fs will be prepended with
-the base path before calling the source Fs.
+Use `HttpFs` to expose any Afero filesystem—even one created dynamically in memory—through a standard Go web server.
```go
-bp := afero.NewBasePathFs(afero.NewOsFs(), "/base/path")
-```
+import (
+ "net/http"
+ "github.com/spf13/afero"
+)
-### ReadOnlyFs
+func main() {
+ memFS := afero.NewMemMapFs()
+ afero.WriteFile(memFS, "index.html", []byte("
Hello from Memory!
"), 0644)
-A thin wrapper around the source Fs providing a read only view.
+ // Wrap the memory filesystem to make it compatible with http.FileServer.
+ httpFS := afero.NewHttpFs(memFS)
-```go
-fs := afero.NewReadOnlyFs(afero.NewOsFs())
-_, err := fs.Create("/file.txt")
-// err = syscall.EPERM
+ http.Handle("/", http.FileServer(httpFS.Dir("/")))
+ http.ListenAndServe(":8080", nil)
+}
```
-# RegexpFs
+### Testing Made Simple
-A filtered view on file names, any file NOT matching
-the passed regexp will be treated as non-existing.
-Files not matching the regexp provided will not be created.
-Directories are not filtered.
+One of Afero's greatest strengths is making filesystem-dependent code easily testable:
```go
-fs := afero.NewRegexpFs(afero.NewMemMapFs(), regexp.MustCompile(`\.txt$`))
-_, err := fs.Create("/file.html")
-// err = syscall.ENOENT
-```
+func SaveUserData(fs afero.Fs, userID string, data []byte) error {
+ filename := fmt.Sprintf("users/%s.json", userID)
+ return afero.WriteFile(fs, filename, data, 0644)
+}
-### HttpFs
+func TestSaveUserData(t *testing.T) {
+ // Create a clean, fast, in-memory filesystem for testing
+ testFS := afero.NewMemMapFs()
+
+ userData := []byte(`{"name": "John", "email": "john@example.com"}`)
+ err := SaveUserData(testFS, "123", userData)
+
+ if err != nil {
+ t.Fatalf("SaveUserData failed: %v", err)
+ }
+
+ // Verify the file was saved correctly
+ saved, err := afero.ReadFile(testFS, "users/123.json")
+ if err != nil {
+ t.Fatalf("Failed to read saved file: %v", err)
+ }
+
+ if string(saved) != string(userData) {
+ t.Errorf("Data mismatch: got %s, want %s", saved, userData)
+ }
+}
+```
-Afero provides an http compatible backend which can wrap any of the existing
-backends.
+**Benefits of testing with Afero:**
+- ⚡ **Fast** - No disk I/O, tests run in memory
+- 🔄 **Reliable** - Each test starts with a clean slate
+- 🧹 **No cleanup** - Memory is automatically freed
+- 🔒 **Safe** - Can't accidentally modify real files
+- 🏃 **Parallel** - Tests can run concurrently without conflicts
+
+## Backend Reference
+
+| Type | Backend | Constructor | Description | Status |
+| :--- | :--- | :--- | :--- | :--- |
+| **Core** | **OsFs** | `afero.NewOsFs()` | Interacts with the real operating system filesystem. Use in production. | ✅ Official |
+| | **MemMapFs** | `afero.NewMemMapFs()` | A fast, atomic, concurrent-safe, in-memory filesystem. Ideal for testing. | ✅ Official |
+| **Composition** | **CopyOnWriteFs**| `afero.NewCopyOnWriteFs(base, overlay)` | A read-only base with a writable overlay. Ideal for sandboxing. | ✅ Official |
+| | **CacheOnReadFs**| `afero.NewCacheOnReadFs(base, cache, ttl)` | Lazily caches files from a slow base into a fast layer on first read. | ✅ Official |
+| | **BasePathFs** | `afero.NewBasePathFs(source, path)` | Restricts operations to a subdirectory (chroot/jail). | ✅ Official |
+| | **ReadOnlyFs** | `afero.NewReadOnlyFs(source)` | Provides a read-only view, preventing any modifications. | ✅ Official |
+| | **RegexpFs** | `afero.NewRegexpFs(source, regexp)` | Filters a filesystem, only showing files that match a regex. | ✅ Official |
+| **Utility** | **HttpFs** | `afero.NewHttpFs(source)` | Wraps any Afero filesystem to be served via `http.FileServer`. | ✅ Official |
+| **Archives** | **ZipFs** | `zipfs.New(zipReader)` | Read-only access to files within a ZIP archive. | ✅ Official |
+| | **TarFs** | `tarfs.New(tarReader)` | Read-only access to files within a TAR archive. | ✅ Official |
+| **Network** | **GcsFs** | `gcsfs.NewGcsFs(...)` | Google Cloud Storage backend. | ⚡ Experimental |
+| | **SftpFs** | `sftpfs.New(...)` | SFTP backend. | ⚡ Experimental |
+| **3rd Party Cloud** | **S3Fs** | [`fclairamb/afero-s3`](https://github.com/fclairamb/afero-s3) | Production-ready S3 backend built on official AWS SDK. | 🔹 3rd Party |
+| | **MinioFs** | [`cpyun/afero-minio`](https://github.com/cpyun/afero-minio) | MinIO object storage backend with S3 compatibility. | 🔹 3rd Party |
+| | **DriveFs** | [`fclairamb/afero-gdrive`](https://github.com/fclairamb/afero-gdrive) | Google Drive backend with streaming support. | 🔹 3rd Party |
+| | **DropboxFs** | [`fclairamb/afero-dropbox`](https://github.com/fclairamb/afero-dropbox) | Dropbox backend with streaming support. | 🔹 3rd Party |
+| **3rd Party Specialized** | **GitFs** | [`tobiash/go-gitfs`](https://github.com/tobiash/go-gitfs) | Git repository filesystem (read-only, Afero compatible). | 🔹 3rd Party |
+| | **DockerFs** | [`unmango/aferox`](https://github.com/unmango/aferox) | Docker container filesystem access. | 🔹 3rd Party |
+| | **GitHubFs** | [`unmango/aferox`](https://github.com/unmango/aferox) | GitHub repository and releases filesystem. | 🔹 3rd Party |
+| | **FilterFs** | [`unmango/aferox`](https://github.com/unmango/aferox) | Filesystem filtering with predicates. | 🔹 3rd Party |
+| | **IgnoreFs** | [`unmango/aferox`](https://github.com/unmango/aferox) | .gitignore-aware filtering filesystem. | 🔹 3rd Party |
+| | **FUSEFs** | [`JakWai01/sile-fystem`](https://github.com/JakWai01/sile-fystem) | Generic FUSE implementation using any Afero backend. | 🔹 3rd Party |
+
+## Afero vs. `io/fs` (Go 1.16+)
+
+Go 1.16 introduced the `io/fs` package, which provides a standard abstraction for **read-only** filesystems.
+
+Afero complements `io/fs` by focusing on different needs:
+
+* **Use `io/fs` when:** You only need to read files and want to conform strictly to the standard library interfaces.
+* **Use Afero when:**
+ * Your application needs to **create, write, modify, or delete** files.
+ * You need to test complex read/write interactions (e.g., renaming, concurrent writes).
+ * You need advanced compositional features (Copy-on-Write, Caching, etc.).
+
+Afero is fully compatible with `io/fs`. You can wrap any Afero filesystem to satisfy the `fs.FS` interface using `afero.NewIOFS`:
-The Http package requires a slightly specific version of Open which
-returns an http.File type.
+```go
+import "io/fs"
-Afero provides an httpFs file system which satisfies this requirement.
-Any Afero FileSystem can be used as an httpFs.
+// Create an Afero filesystem (writable)
+var myAferoFs afero.Fs = afero.NewMemMapFs()
-```go
-httpFs := afero.NewHttpFs()
-fileserver := http.FileServer(httpFs.Dir())
-http.Handle("/", fileserver)
+// Convert it to a standard library fs.FS (read-only view)
+var myIoFs fs.FS = afero.NewIOFS(myAferoFs)
```
-## Composite Backends
+## Third-Party Backends & Ecosystem
-Afero provides the ability have two filesystems (or more) act as a single
-file system.
+The Afero community has developed numerous backends and tools that extend the library's capabilities. Below are curated, well-maintained options organized by maturity and reliability.
-### CacheOnReadFs
+### Featured Community Backends
-The CacheOnReadFs will lazily make copies of any accessed files from the base
-layer into the overlay. Subsequent reads will be pulled from the overlay
-directly permitting the request is within the cache duration of when it was
-created in the overlay.
+These are mature, reliable backends that we can confidently recommend for production use:
-If the base filesystem is writeable, any changes to files will be
-done first to the base, then to the overlay layer. Write calls to open file
-handles like `Write()` or `Truncate()` to the overlay first.
+#### **Amazon S3** - [`fclairamb/afero-s3`](https://github.com/fclairamb/afero-s3)
+Production-ready S3 backend built on the official AWS SDK for Go.
-To writing files to the overlay only, you can use the overlay Fs directly (not
-via the union Fs).
+```go
+import "github.com/fclairamb/afero-s3"
-Cache files in the layer for the given time.Duration, a cache duration of 0
-means "forever" meaning the file will not be re-requested from the base ever.
+s3fs := s3.NewFs(bucket, session)
+```
-A read-only base will make the overlay also read-only but still copy files
-from the base to the overlay when they're not present (or outdated) in the
-caching layer.
+#### **MinIO** - [`cpyun/afero-minio`](https://github.com/cpyun/afero-minio)
+MinIO object storage backend providing S3-compatible object storage with deduplication and optimization features.
```go
-base := afero.NewOsFs()
-layer := afero.NewMemMapFs()
-ufs := afero.NewCacheOnReadFs(base, layer, 100 * time.Second)
+import "github.com/cpyun/afero-minio"
+
+minioFs := miniofs.NewMinioFs(ctx, "minio://endpoint/bucket")
```
-### CopyOnWriteFs()
+### Community & Specialized Backends
-The CopyOnWriteFs is a read only base file system with a potentially
-writeable layer on top.
+#### Cloud Storage
-Read operations will first look in the overlay and if not found there, will
-serve the file from the base.
+- **Google Drive** - [`fclairamb/afero-gdrive`](https://github.com/fclairamb/afero-gdrive)
+ Streaming support; no write-seeking or POSIX permissions; no files listing cache
-Changes to the file system will only be made in the overlay.
+- **Dropbox** - [`fclairamb/afero-dropbox`](https://github.com/fclairamb/afero-dropbox)
+ Streaming support; no write-seeking or POSIX permissions
-Any attempt to modify a file found only in the base will copy the file to the
-overlay layer before modification (including opening a file with a writable
-handle).
+#### Version Control Systems
-Removing and Renaming files present only in the base layer is not currently
-permitted. If a file is present in the base layer and the overlay, only the
-overlay will be removed/renamed.
+- **Git Repositories** - [`tobiash/go-gitfs`](https://github.com/tobiash/go-gitfs)
+ Read-only filesystem abstraction for Git repositories. Works with bare repositories and provides filesystem view of any git reference. Uses go-git for repository access.
-```go
- base := afero.NewOsFs()
- roBase := afero.NewReadOnlyFs(base)
- ufs := afero.NewCopyOnWriteFs(roBase, afero.NewMemMapFs())
+#### Container and Remote Systems
- fh, _ = ufs.Create("/home/test/file2.txt")
- fh.WriteString("This is a test")
- fh.Close()
-```
+- **Docker Containers** - [`unmango/aferox`](https://github.com/unmango/aferox)
+ Access Docker container filesystems as if they were local filesystems
-In this example all write operations will only occur in memory (MemMapFs)
-leaving the base filesystem (OsFs) untouched.
+- **GitHub API** - [`unmango/aferox`](https://github.com/unmango/aferox)
+ Turn GitHub repositories, releases, and assets into browsable filesystems
+#### FUSE Integration
-## Desired/possible backends
+- **Generic FUSE** - [`JakWai01/sile-fystem`](https://github.com/JakWai01/sile-fystem)
+ Mount any Afero filesystem as a FUSE filesystem, allowing any Afero backend to be used as a real mounted filesystem
-The following is a short list of possible backends we hope someone will
-implement:
+#### Specialized Filesystems
-* SSH
-* S3
+- **FAT32 Support** - [`aligator/GoFAT`](https://github.com/aligator/GoFAT)
+ Pure Go FAT filesystem implementation (currently read-only)
-# About the project
+### Interface Adapters & Utilities
-## What's in the name
+**Cross-Interface Compatibility:**
+- [`jfontan/go-billy-desfacer`](https://github.com/jfontan/go-billy-desfacer) - Adapter between Afero and go-billy interfaces (for go-git compatibility)
+- [`Maldris/go-billy-afero`](https://github.com/Maldris/go-billy-afero) - Alternative wrapper for using Afero with go-billy
+- [`c4milo/afero2billy`](https://github.com/c4milo/afero2billy) - Another Afero to billy filesystem adapter
-Afero comes from the latin roots Ad-Facere.
+**Working Directory Management:**
+- [`carolynvs/aferox`](https://github.com/carolynvs/aferox) - Working directory-aware filesystem wrapper
-**"Ad"** is a prefix meaning "to".
+**Advanced Filtering:**
+- [`unmango/aferox`](https://github.com/unmango/aferox) includes multiple specialized filesystems:
+ - **FilterFs** - Predicate-based file filtering
+ - **IgnoreFs** - .gitignore-aware filtering
+ - **WriterFs** - Dump writes to io.Writer for debugging
-**"Facere"** is a form of the root "faciō" making "make or do".
+#### Developer Tools & Utilities
-The literal meaning of afero is "to make" or "to do" which seems very fitting
-for a library that allows one to make files and directories and do things with them.
+**nhatthm Utility Suite** - Essential tools for Afero development:
+- [`nhatthm/aferocopy`](https://github.com/nhatthm/aferocopy) - Copy files between any Afero filesystems
+- [`nhatthm/aferomock`](https://github.com/nhatthm/aferomock) - Mocking toolkit for testing
+- [`nhatthm/aferoassert`](https://github.com/nhatthm/aferoassert) - Assertion helpers for filesystem testing
-The English word that shares the same roots as Afero is "affair". Affair shares
-the same concept but as a noun it means "something that is made or done" or "an
-object of a particular type".
+### Ecosystem Showcase
-It's also nice that unlike some of my other libraries (hugo, cobra, viper) it
-Googles very well.
+**Windows Virtual Drives** - [`balazsgrill/potatodrive`](https://github.com/balazsgrill/potatodrive)
+Mount any Afero filesystem as a Windows drive letter. Brilliant demonstration of Afero's power!
-## Release Notes
+### Modern Asset Embedding (Go 1.16+)
-See the [Releases Page](https://github.com/spf13/afero/releases).
+Instead of third-party tools, use Go's native `//go:embed` with Afero:
+
+```go
+import (
+ "embed"
+ "github.com/spf13/afero"
+)
+
+//go:embed assets/*
+var assetsFS embed.FS
+
+func main() {
+ // Convert embedded files to Afero filesystem
+ fs := afero.FromIOFS(assetsFS)
+
+ // Use like any other Afero filesystem
+ content, _ := afero.ReadFile(fs, "assets/config.json")
+}
+```
## Contributing
-1. Fork it
+We welcome contributions! The project is mature, but we are actively looking for contributors to help implement and stabilize network/cloud backends.
+
+* 🔥 **Microsoft Azure Blob Storage**
+* 🔒 **Modern Encryption Backend** - Built on secure, contemporary crypto (not legacy EncFS)
+* 🐙 **Canonical go-git Adapter** - Unified solution for Git integration
+* 📡 **SSH/SCP Backend** - Secure remote file operations
+* Stabilization of existing experimental backends (GCS, SFTP)
+
+To contribute:
+1. Fork the repository
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
-5. Create new Pull Request
+5. Create a new Pull Request
+
+## 📄 License
-## Contributors
+Afero is released under the Apache 2.0 license. See [LICENSE.txt](https://github.com/spf13/afero/blob/master/LICENSE.txt) for details.
-Names in no particular order:
+## 🔗 Additional Resources
-* [spf13](https://github.com/spf13)
-* [jaqx0r](https://github.com/jaqx0r)
-* [mbertschler](https://github.com/mbertschler)
-* [xor-gate](https://github.com/xor-gate)
+- [📖 Full API Documentation](https://pkg.go.dev/github.com/spf13/afero)
+- [🎯 Examples Repository](https://github.com/spf13/afero/tree/master/examples)
+- [📋 Release Notes](https://github.com/spf13/afero/releases)
+- [❓ GitHub Discussions](https://github.com/spf13/afero/discussions)
-## License
+---
-Afero is released under the Apache 2.0 license. See
-[LICENSE.txt](https://github.com/spf13/afero/blob/master/LICENSE.txt)
+*Afero comes from the Latin roots Ad-Facere, meaning "to make" or "to do" - fitting for a library that empowers you to make and do amazing things with filesystems.*
diff --git a/vendor/github.com/spf13/afero/copyOnWriteFs.go b/vendor/github.com/spf13/afero/copyOnWriteFs.go
index 184d6dd70..aba2879eb 100644
--- a/vendor/github.com/spf13/afero/copyOnWriteFs.go
+++ b/vendor/github.com/spf13/afero/copyOnWriteFs.go
@@ -34,7 +34,8 @@ func (u *CopyOnWriteFs) isBaseFile(name string) (bool, error) {
_, err := u.base.Stat(name)
if err != nil {
if oerr, ok := err.(*os.PathError); ok {
- if oerr.Err == os.ErrNotExist || oerr.Err == syscall.ENOENT || oerr.Err == syscall.ENOTDIR {
+ if oerr.Err == os.ErrNotExist || oerr.Err == syscall.ENOENT ||
+ oerr.Err == syscall.ENOTDIR {
return false, nil
}
}
@@ -237,7 +238,11 @@ func (u *CopyOnWriteFs) OpenFile(name string, flag int, perm os.FileMode) (File,
return u.layer.OpenFile(name, flag, perm)
}
- return nil, &os.PathError{Op: "open", Path: name, Err: syscall.ENOTDIR} // ...or os.ErrNotExist?
+ return nil, &os.PathError{
+ Op: "open",
+ Path: name,
+ Err: syscall.ENOTDIR,
+ } // ...or os.ErrNotExist?
}
if b {
return u.base.OpenFile(name, flag, perm)
diff --git a/vendor/github.com/spf13/afero/iofs.go b/vendor/github.com/spf13/afero/iofs.go
index b13155ca4..57ba5673e 100644
--- a/vendor/github.com/spf13/afero/iofs.go
+++ b/vendor/github.com/spf13/afero/iofs.go
@@ -137,7 +137,7 @@ type readDirFile struct {
var _ fs.ReadDirFile = readDirFile{}
func (r readDirFile) ReadDir(n int) ([]fs.DirEntry, error) {
- items, err := r.File.Readdir(n)
+ items, err := r.Readdir(n)
if err != nil {
return nil, err
}
@@ -161,7 +161,12 @@ var _ Fs = FromIOFS{}
func (f FromIOFS) Create(name string) (File, error) { return nil, notImplemented("create", name) }
-func (f FromIOFS) Mkdir(name string, perm os.FileMode) error { return notImplemented("mkdir", name) }
+func (f FromIOFS) Mkdir(
+ name string,
+ perm os.FileMode,
+) error {
+ return notImplemented("mkdir", name)
+}
func (f FromIOFS) MkdirAll(path string, perm os.FileMode) error {
return notImplemented("mkdirall", path)
diff --git a/vendor/github.com/spf13/afero/lstater.go b/vendor/github.com/spf13/afero/lstater.go
index 89c1bfc0a..2dcbdb1f0 100644
--- a/vendor/github.com/spf13/afero/lstater.go
+++ b/vendor/github.com/spf13/afero/lstater.go
@@ -19,9 +19,9 @@ import (
// Lstater is an optional interface in Afero. It is only implemented by the
// filesystems saying so.
-// It will call Lstat if the filesystem iself is, or it delegates to, the os filesystem.
+// It will call Lstat if the filesystem itself is, or it delegates to, the os filesystem.
// Else it will call Stat.
-// In addtion to the FileInfo, it will return a boolean telling whether Lstat was called or not.
+// In addition to the FileInfo, it will return a boolean telling whether Lstat was called or not.
type Lstater interface {
LstatIfPossible(name string) (os.FileInfo, bool, error)
}
diff --git a/vendor/github.com/spf13/afero/mem/file.go b/vendor/github.com/spf13/afero/mem/file.go
index 62fe4498e..c77fcd40e 100644
--- a/vendor/github.com/spf13/afero/mem/file.go
+++ b/vendor/github.com/spf13/afero/mem/file.go
@@ -150,7 +150,11 @@ func (f *File) Sync() error {
func (f *File) Readdir(count int) (res []os.FileInfo, err error) {
if !f.fileData.dir {
- return nil, &os.PathError{Op: "readdir", Path: f.fileData.name, Err: errors.New("not a dir")}
+ return nil, &os.PathError{
+ Op: "readdir",
+ Path: f.fileData.name,
+ Err: errors.New("not a dir"),
+ }
}
var outLength int64
@@ -236,7 +240,11 @@ func (f *File) Truncate(size int64) error {
return ErrFileClosed
}
if f.readOnly {
- return &os.PathError{Op: "truncate", Path: f.fileData.name, Err: errors.New("file handle is read only")}
+ return &os.PathError{
+ Op: "truncate",
+ Path: f.fileData.name,
+ Err: errors.New("file handle is read only"),
+ }
}
if size < 0 {
return ErrOutOfRange
@@ -273,7 +281,11 @@ func (f *File) Write(b []byte) (n int, err error) {
return 0, ErrFileClosed
}
if f.readOnly {
- return 0, &os.PathError{Op: "write", Path: f.fileData.name, Err: errors.New("file handle is read only")}
+ return 0, &os.PathError{
+ Op: "write",
+ Path: f.fileData.name,
+ Err: errors.New("file handle is read only"),
+ }
}
n = len(b)
cur := atomic.LoadInt64(&f.at)
@@ -285,7 +297,9 @@ func (f *File) Write(b []byte) (n int, err error) {
tail = f.fileData.data[n+int(cur):]
}
if diff > 0 {
- f.fileData.data = append(f.fileData.data, append(bytes.Repeat([]byte{0o0}, int(diff)), b...)...)
+ f.fileData.data = append(
+ f.fileData.data,
+ append(bytes.Repeat([]byte{0o0}, int(diff)), b...)...)
f.fileData.data = append(f.fileData.data, tail...)
} else {
f.fileData.data = append(f.fileData.data[:cur], b...)
diff --git a/vendor/github.com/spf13/afero/unionFile.go b/vendor/github.com/spf13/afero/unionFile.go
index 62dd6c93c..2e2253f55 100644
--- a/vendor/github.com/spf13/afero/unionFile.go
+++ b/vendor/github.com/spf13/afero/unionFile.go
@@ -92,7 +92,8 @@ func (f *UnionFile) Seek(o int64, w int) (pos int64, err error) {
func (f *UnionFile) Write(s []byte) (n int, err error) {
if f.Layer != nil {
n, err = f.Layer.Write(s)
- if err == nil && f.Base != nil { // hmm, do we have fixed size files where a write may hit the EOF mark?
+ if err == nil &&
+ f.Base != nil { // hmm, do we have fixed size files where a write may hit the EOF mark?
_, err = f.Base.Write(s)
}
return n, err
@@ -157,7 +158,7 @@ var defaultUnionMergeDirsFn = func(lofi, bofi []os.FileInfo) ([]os.FileInfo, err
// return a single view of the overlayed directories.
// At the end of the directory view, the error is io.EOF if c > 0.
func (f *UnionFile) Readdir(c int) (ofi []os.FileInfo, err error) {
- var merge DirsMerger = f.Merger
+ merge := f.Merger
if merge == nil {
merge = defaultUnionMergeDirsFn
}
diff --git a/vendor/github.com/spf13/afero/util.go b/vendor/github.com/spf13/afero/util.go
index 9e4cba274..231768838 100644
--- a/vendor/github.com/spf13/afero/util.go
+++ b/vendor/github.com/spf13/afero/util.go
@@ -113,11 +113,11 @@ func GetTempDir(fs Fs, subPath string) string {
if subPath != "" {
// preserve windows backslash :-(
if FilePathSeparator == "\\" {
- subPath = strings.Replace(subPath, "\\", "____", -1)
+ subPath = strings.ReplaceAll(subPath, "\\", "____")
}
dir = dir + UnicodeSanitize((subPath))
if FilePathSeparator == "\\" {
- dir = strings.Replace(dir, "____", "\\", -1)
+ dir = strings.ReplaceAll(dir, "____", "\\")
}
if exists, _ := Exists(fs, dir); exists {
diff --git a/vendor/github.com/spf13/cast/.editorconfig b/vendor/github.com/spf13/cast/.editorconfig
new file mode 100644
index 000000000..a85749f19
--- /dev/null
+++ b/vendor/github.com/spf13/cast/.editorconfig
@@ -0,0 +1,15 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 4
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.go]
+indent_style = tab
+
+[{*.yml,*.yaml}]
+indent_size = 2
diff --git a/vendor/github.com/spf13/cast/.golangci.yaml b/vendor/github.com/spf13/cast/.golangci.yaml
new file mode 100644
index 000000000..e00fd47aa
--- /dev/null
+++ b/vendor/github.com/spf13/cast/.golangci.yaml
@@ -0,0 +1,39 @@
+version: "2"
+
+run:
+ timeout: 10m
+
+linters:
+ enable:
+ - errcheck
+ - govet
+ - ineffassign
+ - misspell
+ - nolintlint
+ # - revive
+ - unused
+
+ disable:
+ - staticcheck
+
+ settings:
+ misspell:
+ locale: US
+ nolintlint:
+ allow-unused: false # report any unused nolint directives
+ require-specific: false # don't require nolint directives to be specific about which linter is being skipped
+
+formatters:
+ enable:
+ - gci
+ - gofmt
+ # - gofumpt
+ - goimports
+ # - golines
+
+ settings:
+ gci:
+ sections:
+ - standard
+ - default
+ - localmodule
diff --git a/vendor/github.com/spf13/cast/README.md b/vendor/github.com/spf13/cast/README.md
index 1be666a45..c58eccb3f 100644
--- a/vendor/github.com/spf13/cast/README.md
+++ b/vendor/github.com/spf13/cast/README.md
@@ -1,9 +1,9 @@
# cast
-[](https://github.com/spf13/cast/actions/workflows/test.yaml)
-[](https://pkg.go.dev/mod/github.com/spf13/cast)
-
-[](https://goreportcard.com/report/github.com/spf13/cast)
+[](https://github.com/spf13/cast/actions/workflows/ci.yaml)
+[](https://pkg.go.dev/mod/github.com/spf13/cast)
+
+[](https://deps.dev/go/github.com%252Fspf13%252Fcast)
Easy and safe casting from one type to another in Go
@@ -73,3 +73,7 @@ the code for a complete set.
var eight interface{} = 8
cast.ToInt(eight) // 8
cast.ToInt(nil) // 0
+
+## License
+
+The project is licensed under the [MIT License](LICENSE).
diff --git a/vendor/github.com/spf13/cast/alias.go b/vendor/github.com/spf13/cast/alias.go
new file mode 100644
index 000000000..855d60005
--- /dev/null
+++ b/vendor/github.com/spf13/cast/alias.go
@@ -0,0 +1,69 @@
+// Copyright © 2014 Steve Francia .
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+package cast
+
+import (
+ "reflect"
+ "slices"
+)
+
+var kindNames = []string{
+ reflect.String: "string",
+ reflect.Bool: "bool",
+ reflect.Int: "int",
+ reflect.Int8: "int8",
+ reflect.Int16: "int16",
+ reflect.Int32: "int32",
+ reflect.Int64: "int64",
+ reflect.Uint: "uint",
+ reflect.Uint8: "uint8",
+ reflect.Uint16: "uint16",
+ reflect.Uint32: "uint32",
+ reflect.Uint64: "uint64",
+ reflect.Float32: "float32",
+ reflect.Float64: "float64",
+}
+
+var kinds = map[reflect.Kind]func(reflect.Value) any{
+ reflect.String: func(v reflect.Value) any { return v.String() },
+ reflect.Bool: func(v reflect.Value) any { return v.Bool() },
+ reflect.Int: func(v reflect.Value) any { return int(v.Int()) },
+ reflect.Int8: func(v reflect.Value) any { return int8(v.Int()) },
+ reflect.Int16: func(v reflect.Value) any { return int16(v.Int()) },
+ reflect.Int32: func(v reflect.Value) any { return int32(v.Int()) },
+ reflect.Int64: func(v reflect.Value) any { return v.Int() },
+ reflect.Uint: func(v reflect.Value) any { return uint(v.Uint()) },
+ reflect.Uint8: func(v reflect.Value) any { return uint8(v.Uint()) },
+ reflect.Uint16: func(v reflect.Value) any { return uint16(v.Uint()) },
+ reflect.Uint32: func(v reflect.Value) any { return uint32(v.Uint()) },
+ reflect.Uint64: func(v reflect.Value) any { return v.Uint() },
+ reflect.Float32: func(v reflect.Value) any { return float32(v.Float()) },
+ reflect.Float64: func(v reflect.Value) any { return v.Float() },
+}
+
+// resolveAlias attempts to resolve a named type to its underlying basic type (if possible).
+//
+// Pointers are expected to be indirected by this point.
+func resolveAlias(i any) (any, bool) {
+ if i == nil {
+ return nil, false
+ }
+
+ t := reflect.TypeOf(i)
+
+ // Not a named type
+ if t.Name() == "" || slices.Contains(kindNames, t.Name()) {
+ return i, false
+ }
+
+ resolve, ok := kinds[t.Kind()]
+ if !ok { // Not a supported kind
+ return i, false
+ }
+
+ v := reflect.ValueOf(i)
+
+ return resolve(v), true
+}
diff --git a/vendor/github.com/spf13/cast/basic.go b/vendor/github.com/spf13/cast/basic.go
new file mode 100644
index 000000000..fa330e207
--- /dev/null
+++ b/vendor/github.com/spf13/cast/basic.go
@@ -0,0 +1,131 @@
+// Copyright © 2014 Steve Francia .
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+package cast
+
+import (
+ "encoding/json"
+ "fmt"
+ "html/template"
+ "strconv"
+ "time"
+)
+
+// ToBoolE casts any value to a bool type.
+func ToBoolE(i any) (bool, error) {
+ i, _ = indirect(i)
+
+ switch b := i.(type) {
+ case bool:
+ return b, nil
+ case nil:
+ return false, nil
+ case int:
+ return b != 0, nil
+ case int8:
+ return b != 0, nil
+ case int16:
+ return b != 0, nil
+ case int32:
+ return b != 0, nil
+ case int64:
+ return b != 0, nil
+ case uint:
+ return b != 0, nil
+ case uint8:
+ return b != 0, nil
+ case uint16:
+ return b != 0, nil
+ case uint32:
+ return b != 0, nil
+ case uint64:
+ return b != 0, nil
+ case float32:
+ return b != 0, nil
+ case float64:
+ return b != 0, nil
+ case time.Duration:
+ return b != 0, nil
+ case string:
+ return strconv.ParseBool(b)
+ case json.Number:
+ v, err := ToInt64E(b)
+ if err == nil {
+ return v != 0, nil
+ }
+
+ return false, fmt.Errorf(errorMsg, i, i, false)
+ default:
+ if i, ok := resolveAlias(i); ok {
+ return ToBoolE(i)
+ }
+
+ return false, fmt.Errorf(errorMsg, i, i, false)
+ }
+}
+
+// ToStringE casts any value to a string type.
+func ToStringE(i any) (string, error) {
+ switch s := i.(type) {
+ case string:
+ return s, nil
+ case bool:
+ return strconv.FormatBool(s), nil
+ case float64:
+ return strconv.FormatFloat(s, 'f', -1, 64), nil
+ case float32:
+ return strconv.FormatFloat(float64(s), 'f', -1, 32), nil
+ case int:
+ return strconv.Itoa(s), nil
+ case int8:
+ return strconv.FormatInt(int64(s), 10), nil
+ case int16:
+ return strconv.FormatInt(int64(s), 10), nil
+ case int32:
+ return strconv.FormatInt(int64(s), 10), nil
+ case int64:
+ return strconv.FormatInt(s, 10), nil
+ case uint:
+ return strconv.FormatUint(uint64(s), 10), nil
+ case uint8:
+ return strconv.FormatUint(uint64(s), 10), nil
+ case uint16:
+ return strconv.FormatUint(uint64(s), 10), nil
+ case uint32:
+ return strconv.FormatUint(uint64(s), 10), nil
+ case uint64:
+ return strconv.FormatUint(s, 10), nil
+ case json.Number:
+ return s.String(), nil
+ case []byte:
+ return string(s), nil
+ case template.HTML:
+ return string(s), nil
+ case template.URL:
+ return string(s), nil
+ case template.JS:
+ return string(s), nil
+ case template.CSS:
+ return string(s), nil
+ case template.HTMLAttr:
+ return string(s), nil
+ case nil:
+ return "", nil
+ case fmt.Stringer:
+ return s.String(), nil
+ case error:
+ return s.Error(), nil
+ default:
+ if i, ok := indirect(i); ok {
+ return ToStringE(i)
+ }
+
+ if i, ok := resolveAlias(i); ok {
+ return ToStringE(i)
+ }
+
+ return "", fmt.Errorf(errorMsg, i, i, "")
+ }
+}
diff --git a/vendor/github.com/spf13/cast/cast.go b/vendor/github.com/spf13/cast/cast.go
index 0cfe9418d..8d85539b3 100644
--- a/vendor/github.com/spf13/cast/cast.go
+++ b/vendor/github.com/spf13/cast/cast.go
@@ -8,169 +8,77 @@ package cast
import "time"
-// ToBool casts an interface to a bool type.
-func ToBool(i interface{}) bool {
- v, _ := ToBoolE(i)
- return v
-}
-
-// ToTime casts an interface to a time.Time type.
-func ToTime(i interface{}) time.Time {
- v, _ := ToTimeE(i)
- return v
-}
-
-func ToTimeInDefaultLocation(i interface{}, location *time.Location) time.Time {
- v, _ := ToTimeInDefaultLocationE(i, location)
- return v
-}
-
-// ToDuration casts an interface to a time.Duration type.
-func ToDuration(i interface{}) time.Duration {
- v, _ := ToDurationE(i)
- return v
-}
-
-// ToFloat64 casts an interface to a float64 type.
-func ToFloat64(i interface{}) float64 {
- v, _ := ToFloat64E(i)
- return v
-}
-
-// ToFloat32 casts an interface to a float32 type.
-func ToFloat32(i interface{}) float32 {
- v, _ := ToFloat32E(i)
- return v
-}
-
-// ToInt64 casts an interface to an int64 type.
-func ToInt64(i interface{}) int64 {
- v, _ := ToInt64E(i)
- return v
-}
-
-// ToInt32 casts an interface to an int32 type.
-func ToInt32(i interface{}) int32 {
- v, _ := ToInt32E(i)
- return v
-}
-
-// ToInt16 casts an interface to an int16 type.
-func ToInt16(i interface{}) int16 {
- v, _ := ToInt16E(i)
- return v
-}
-
-// ToInt8 casts an interface to an int8 type.
-func ToInt8(i interface{}) int8 {
- v, _ := ToInt8E(i)
- return v
-}
-
-// ToInt casts an interface to an int type.
-func ToInt(i interface{}) int {
- v, _ := ToIntE(i)
- return v
-}
-
-// ToUint casts an interface to a uint type.
-func ToUint(i interface{}) uint {
- v, _ := ToUintE(i)
- return v
-}
-
-// ToUint64 casts an interface to a uint64 type.
-func ToUint64(i interface{}) uint64 {
- v, _ := ToUint64E(i)
- return v
-}
-
-// ToUint32 casts an interface to a uint32 type.
-func ToUint32(i interface{}) uint32 {
- v, _ := ToUint32E(i)
- return v
-}
-
-// ToUint16 casts an interface to a uint16 type.
-func ToUint16(i interface{}) uint16 {
- v, _ := ToUint16E(i)
- return v
-}
-
-// ToUint8 casts an interface to a uint8 type.
-func ToUint8(i interface{}) uint8 {
- v, _ := ToUint8E(i)
- return v
-}
-
-// ToString casts an interface to a string type.
-func ToString(i interface{}) string {
- v, _ := ToStringE(i)
- return v
-}
-
-// ToStringMapString casts an interface to a map[string]string type.
-func ToStringMapString(i interface{}) map[string]string {
- v, _ := ToStringMapStringE(i)
- return v
-}
-
-// ToStringMapStringSlice casts an interface to a map[string][]string type.
-func ToStringMapStringSlice(i interface{}) map[string][]string {
- v, _ := ToStringMapStringSliceE(i)
- return v
-}
-
-// ToStringMapBool casts an interface to a map[string]bool type.
-func ToStringMapBool(i interface{}) map[string]bool {
- v, _ := ToStringMapBoolE(i)
- return v
-}
-
-// ToStringMapInt casts an interface to a map[string]int type.
-func ToStringMapInt(i interface{}) map[string]int {
- v, _ := ToStringMapIntE(i)
- return v
-}
-
-// ToStringMapInt64 casts an interface to a map[string]int64 type.
-func ToStringMapInt64(i interface{}) map[string]int64 {
- v, _ := ToStringMapInt64E(i)
- return v
-}
-
-// ToStringMap casts an interface to a map[string]interface{} type.
-func ToStringMap(i interface{}) map[string]interface{} {
- v, _ := ToStringMapE(i)
- return v
-}
-
-// ToSlice casts an interface to a []interface{} type.
-func ToSlice(i interface{}) []interface{} {
- v, _ := ToSliceE(i)
- return v
-}
-
-// ToBoolSlice casts an interface to a []bool type.
-func ToBoolSlice(i interface{}) []bool {
- v, _ := ToBoolSliceE(i)
- return v
-}
-
-// ToStringSlice casts an interface to a []string type.
-func ToStringSlice(i interface{}) []string {
- v, _ := ToStringSliceE(i)
- return v
-}
+const errorMsg = "unable to cast %#v of type %T to %T"
+const errorMsgWith = "unable to cast %#v of type %T to %T: %w"
-// ToIntSlice casts an interface to a []int type.
-func ToIntSlice(i interface{}) []int {
- v, _ := ToIntSliceE(i)
- return v
-}
+// Basic is a type parameter constraint for functions accepting basic types.
+//
+// It represents the supported basic types this package can cast to.
+type Basic interface {
+ string | bool | Number | time.Time | time.Duration
+}
+
+// ToE casts any value to a [Basic] type.
+func ToE[T Basic](i any) (T, error) {
+ var t T
+
+ var v any
+ var err error
+
+ switch any(t).(type) {
+ case string:
+ v, err = ToStringE(i)
+ case bool:
+ v, err = ToBoolE(i)
+ case int:
+ v, err = toNumberE[int](i, parseInt[int])
+ case int8:
+ v, err = toNumberE[int8](i, parseInt[int8])
+ case int16:
+ v, err = toNumberE[int16](i, parseInt[int16])
+ case int32:
+ v, err = toNumberE[int32](i, parseInt[int32])
+ case int64:
+ v, err = toNumberE[int64](i, parseInt[int64])
+ case uint:
+ v, err = toUnsignedNumberE[uint](i, parseUint[uint])
+ case uint8:
+ v, err = toUnsignedNumberE[uint8](i, parseUint[uint8])
+ case uint16:
+ v, err = toUnsignedNumberE[uint16](i, parseUint[uint16])
+ case uint32:
+ v, err = toUnsignedNumberE[uint32](i, parseUint[uint32])
+ case uint64:
+ v, err = toUnsignedNumberE[uint64](i, parseUint[uint64])
+ case float32:
+ v, err = toNumberE[float32](i, parseFloat[float32])
+ case float64:
+ v, err = toNumberE[float64](i, parseFloat[float64])
+ case time.Time:
+ v, err = ToTimeE(i)
+ case time.Duration:
+ v, err = ToDurationE(i)
+ }
+
+ if err != nil {
+ return t, err
+ }
+
+ return v.(T), nil
+}
+
+// Must is a helper that wraps a call to a cast function and panics if the error is non-nil.
+func Must[T any](i any, err error) T {
+ if err != nil {
+ panic(err)
+ }
+
+ return i.(T)
+}
+
+// To casts any value to a [Basic] type.
+func To[T Basic](i any) T {
+ v, _ := ToE[T](i)
-// ToDurationSlice casts an interface to a []time.Duration type.
-func ToDurationSlice(i interface{}) []time.Duration {
- v, _ := ToDurationSliceE(i)
return v
}
diff --git a/vendor/github.com/spf13/cast/caste.go b/vendor/github.com/spf13/cast/caste.go
deleted file mode 100644
index 4181a2e75..000000000
--- a/vendor/github.com/spf13/cast/caste.go
+++ /dev/null
@@ -1,1510 +0,0 @@
-// Copyright © 2014 Steve Francia .
-//
-// Use of this source code is governed by an MIT-style
-// license that can be found in the LICENSE file.
-
-package cast
-
-import (
- "encoding/json"
- "errors"
- "fmt"
- "html/template"
- "reflect"
- "strconv"
- "strings"
- "time"
-)
-
-var errNegativeNotAllowed = errors.New("unable to cast negative value")
-
-type float64EProvider interface {
- Float64() (float64, error)
-}
-
-type float64Provider interface {
- Float64() float64
-}
-
-// ToTimeE casts an interface to a time.Time type.
-func ToTimeE(i interface{}) (tim time.Time, err error) {
- return ToTimeInDefaultLocationE(i, time.UTC)
-}
-
-// ToTimeInDefaultLocationE casts an empty interface to time.Time,
-// interpreting inputs without a timezone to be in the given location,
-// or the local timezone if nil.
-func ToTimeInDefaultLocationE(i interface{}, location *time.Location) (tim time.Time, err error) {
- i = indirect(i)
-
- switch v := i.(type) {
- case time.Time:
- return v, nil
- case string:
- return StringToDateInDefaultLocation(v, location)
- case json.Number:
- s, err1 := ToInt64E(v)
- if err1 != nil {
- return time.Time{}, fmt.Errorf("unable to cast %#v of type %T to Time", i, i)
- }
- return time.Unix(s, 0), nil
- case int:
- return time.Unix(int64(v), 0), nil
- case int64:
- return time.Unix(v, 0), nil
- case int32:
- return time.Unix(int64(v), 0), nil
- case uint:
- return time.Unix(int64(v), 0), nil
- case uint64:
- return time.Unix(int64(v), 0), nil
- case uint32:
- return time.Unix(int64(v), 0), nil
- default:
- return time.Time{}, fmt.Errorf("unable to cast %#v of type %T to Time", i, i)
- }
-}
-
-// ToDurationE casts an interface to a time.Duration type.
-func ToDurationE(i interface{}) (d time.Duration, err error) {
- i = indirect(i)
-
- switch s := i.(type) {
- case time.Duration:
- return s, nil
- case int, int64, int32, int16, int8, uint, uint64, uint32, uint16, uint8:
- d = time.Duration(ToInt64(s))
- return
- case float32, float64:
- d = time.Duration(ToFloat64(s))
- return
- case string:
- if strings.ContainsAny(s, "nsuµmh") {
- d, err = time.ParseDuration(s)
- } else {
- d, err = time.ParseDuration(s + "ns")
- }
- return
- case float64EProvider:
- var v float64
- v, err = s.Float64()
- d = time.Duration(v)
- return
- case float64Provider:
- d = time.Duration(s.Float64())
- return
- default:
- err = fmt.Errorf("unable to cast %#v of type %T to Duration", i, i)
- return
- }
-}
-
-// ToBoolE casts an interface to a bool type.
-func ToBoolE(i interface{}) (bool, error) {
- i = indirect(i)
-
- switch b := i.(type) {
- case bool:
- return b, nil
- case nil:
- return false, nil
- case int:
- return b != 0, nil
- case int64:
- return b != 0, nil
- case int32:
- return b != 0, nil
- case int16:
- return b != 0, nil
- case int8:
- return b != 0, nil
- case uint:
- return b != 0, nil
- case uint64:
- return b != 0, nil
- case uint32:
- return b != 0, nil
- case uint16:
- return b != 0, nil
- case uint8:
- return b != 0, nil
- case float64:
- return b != 0, nil
- case float32:
- return b != 0, nil
- case time.Duration:
- return b != 0, nil
- case string:
- return strconv.ParseBool(i.(string))
- case json.Number:
- v, err := ToInt64E(b)
- if err == nil {
- return v != 0, nil
- }
- return false, fmt.Errorf("unable to cast %#v of type %T to bool", i, i)
- default:
- return false, fmt.Errorf("unable to cast %#v of type %T to bool", i, i)
- }
-}
-
-// ToFloat64E casts an interface to a float64 type.
-func ToFloat64E(i interface{}) (float64, error) {
- i = indirect(i)
-
- intv, ok := toInt(i)
- if ok {
- return float64(intv), nil
- }
-
- switch s := i.(type) {
- case float64:
- return s, nil
- case float32:
- return float64(s), nil
- case int64:
- return float64(s), nil
- case int32:
- return float64(s), nil
- case int16:
- return float64(s), nil
- case int8:
- return float64(s), nil
- case uint:
- return float64(s), nil
- case uint64:
- return float64(s), nil
- case uint32:
- return float64(s), nil
- case uint16:
- return float64(s), nil
- case uint8:
- return float64(s), nil
- case string:
- v, err := strconv.ParseFloat(s, 64)
- if err == nil {
- return v, nil
- }
- return 0, fmt.Errorf("unable to cast %#v of type %T to float64", i, i)
- case float64EProvider:
- v, err := s.Float64()
- if err == nil {
- return v, nil
- }
- return 0, fmt.Errorf("unable to cast %#v of type %T to float64", i, i)
- case float64Provider:
- return s.Float64(), nil
- case bool:
- if s {
- return 1, nil
- }
- return 0, nil
- case nil:
- return 0, nil
- default:
- return 0, fmt.Errorf("unable to cast %#v of type %T to float64", i, i)
- }
-}
-
-// ToFloat32E casts an interface to a float32 type.
-func ToFloat32E(i interface{}) (float32, error) {
- i = indirect(i)
-
- intv, ok := toInt(i)
- if ok {
- return float32(intv), nil
- }
-
- switch s := i.(type) {
- case float64:
- return float32(s), nil
- case float32:
- return s, nil
- case int64:
- return float32(s), nil
- case int32:
- return float32(s), nil
- case int16:
- return float32(s), nil
- case int8:
- return float32(s), nil
- case uint:
- return float32(s), nil
- case uint64:
- return float32(s), nil
- case uint32:
- return float32(s), nil
- case uint16:
- return float32(s), nil
- case uint8:
- return float32(s), nil
- case string:
- v, err := strconv.ParseFloat(s, 32)
- if err == nil {
- return float32(v), nil
- }
- return 0, fmt.Errorf("unable to cast %#v of type %T to float32", i, i)
- case float64EProvider:
- v, err := s.Float64()
- if err == nil {
- return float32(v), nil
- }
- return 0, fmt.Errorf("unable to cast %#v of type %T to float32", i, i)
- case float64Provider:
- return float32(s.Float64()), nil
- case bool:
- if s {
- return 1, nil
- }
- return 0, nil
- case nil:
- return 0, nil
- default:
- return 0, fmt.Errorf("unable to cast %#v of type %T to float32", i, i)
- }
-}
-
-// ToInt64E casts an interface to an int64 type.
-func ToInt64E(i interface{}) (int64, error) {
- i = indirect(i)
-
- intv, ok := toInt(i)
- if ok {
- return int64(intv), nil
- }
-
- switch s := i.(type) {
- case int64:
- return s, nil
- case int32:
- return int64(s), nil
- case int16:
- return int64(s), nil
- case int8:
- return int64(s), nil
- case uint:
- return int64(s), nil
- case uint64:
- return int64(s), nil
- case uint32:
- return int64(s), nil
- case uint16:
- return int64(s), nil
- case uint8:
- return int64(s), nil
- case float64:
- return int64(s), nil
- case float32:
- return int64(s), nil
- case string:
- v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
- if err == nil {
- return v, nil
- }
- return 0, fmt.Errorf("unable to cast %#v of type %T to int64", i, i)
- case json.Number:
- return ToInt64E(string(s))
- case bool:
- if s {
- return 1, nil
- }
- return 0, nil
- case nil:
- return 0, nil
- default:
- return 0, fmt.Errorf("unable to cast %#v of type %T to int64", i, i)
- }
-}
-
-// ToInt32E casts an interface to an int32 type.
-func ToInt32E(i interface{}) (int32, error) {
- i = indirect(i)
-
- intv, ok := toInt(i)
- if ok {
- return int32(intv), nil
- }
-
- switch s := i.(type) {
- case int64:
- return int32(s), nil
- case int32:
- return s, nil
- case int16:
- return int32(s), nil
- case int8:
- return int32(s), nil
- case uint:
- return int32(s), nil
- case uint64:
- return int32(s), nil
- case uint32:
- return int32(s), nil
- case uint16:
- return int32(s), nil
- case uint8:
- return int32(s), nil
- case float64:
- return int32(s), nil
- case float32:
- return int32(s), nil
- case string:
- v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
- if err == nil {
- return int32(v), nil
- }
- return 0, fmt.Errorf("unable to cast %#v of type %T to int32", i, i)
- case json.Number:
- return ToInt32E(string(s))
- case bool:
- if s {
- return 1, nil
- }
- return 0, nil
- case nil:
- return 0, nil
- default:
- return 0, fmt.Errorf("unable to cast %#v of type %T to int32", i, i)
- }
-}
-
-// ToInt16E casts an interface to an int16 type.
-func ToInt16E(i interface{}) (int16, error) {
- i = indirect(i)
-
- intv, ok := toInt(i)
- if ok {
- return int16(intv), nil
- }
-
- switch s := i.(type) {
- case int64:
- return int16(s), nil
- case int32:
- return int16(s), nil
- case int16:
- return s, nil
- case int8:
- return int16(s), nil
- case uint:
- return int16(s), nil
- case uint64:
- return int16(s), nil
- case uint32:
- return int16(s), nil
- case uint16:
- return int16(s), nil
- case uint8:
- return int16(s), nil
- case float64:
- return int16(s), nil
- case float32:
- return int16(s), nil
- case string:
- v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
- if err == nil {
- return int16(v), nil
- }
- return 0, fmt.Errorf("unable to cast %#v of type %T to int16", i, i)
- case json.Number:
- return ToInt16E(string(s))
- case bool:
- if s {
- return 1, nil
- }
- return 0, nil
- case nil:
- return 0, nil
- default:
- return 0, fmt.Errorf("unable to cast %#v of type %T to int16", i, i)
- }
-}
-
-// ToInt8E casts an interface to an int8 type.
-func ToInt8E(i interface{}) (int8, error) {
- i = indirect(i)
-
- intv, ok := toInt(i)
- if ok {
- return int8(intv), nil
- }
-
- switch s := i.(type) {
- case int64:
- return int8(s), nil
- case int32:
- return int8(s), nil
- case int16:
- return int8(s), nil
- case int8:
- return s, nil
- case uint:
- return int8(s), nil
- case uint64:
- return int8(s), nil
- case uint32:
- return int8(s), nil
- case uint16:
- return int8(s), nil
- case uint8:
- return int8(s), nil
- case float64:
- return int8(s), nil
- case float32:
- return int8(s), nil
- case string:
- v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
- if err == nil {
- return int8(v), nil
- }
- return 0, fmt.Errorf("unable to cast %#v of type %T to int8", i, i)
- case json.Number:
- return ToInt8E(string(s))
- case bool:
- if s {
- return 1, nil
- }
- return 0, nil
- case nil:
- return 0, nil
- default:
- return 0, fmt.Errorf("unable to cast %#v of type %T to int8", i, i)
- }
-}
-
-// ToIntE casts an interface to an int type.
-func ToIntE(i interface{}) (int, error) {
- i = indirect(i)
-
- intv, ok := toInt(i)
- if ok {
- return intv, nil
- }
-
- switch s := i.(type) {
- case int64:
- return int(s), nil
- case int32:
- return int(s), nil
- case int16:
- return int(s), nil
- case int8:
- return int(s), nil
- case uint:
- return int(s), nil
- case uint64:
- return int(s), nil
- case uint32:
- return int(s), nil
- case uint16:
- return int(s), nil
- case uint8:
- return int(s), nil
- case float64:
- return int(s), nil
- case float32:
- return int(s), nil
- case string:
- v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
- if err == nil {
- return int(v), nil
- }
- return 0, fmt.Errorf("unable to cast %#v of type %T to int64", i, i)
- case json.Number:
- return ToIntE(string(s))
- case bool:
- if s {
- return 1, nil
- }
- return 0, nil
- case nil:
- return 0, nil
- default:
- return 0, fmt.Errorf("unable to cast %#v of type %T to int", i, i)
- }
-}
-
-// ToUintE casts an interface to a uint type.
-func ToUintE(i interface{}) (uint, error) {
- i = indirect(i)
-
- intv, ok := toInt(i)
- if ok {
- if intv < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint(intv), nil
- }
-
- switch s := i.(type) {
- case string:
- v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
- if err == nil {
- if v < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint(v), nil
- }
- return 0, fmt.Errorf("unable to cast %#v of type %T to uint", i, i)
- case json.Number:
- return ToUintE(string(s))
- case int64:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint(s), nil
- case int32:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint(s), nil
- case int16:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint(s), nil
- case int8:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint(s), nil
- case uint:
- return s, nil
- case uint64:
- return uint(s), nil
- case uint32:
- return uint(s), nil
- case uint16:
- return uint(s), nil
- case uint8:
- return uint(s), nil
- case float64:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint(s), nil
- case float32:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint(s), nil
- case bool:
- if s {
- return 1, nil
- }
- return 0, nil
- case nil:
- return 0, nil
- default:
- return 0, fmt.Errorf("unable to cast %#v of type %T to uint", i, i)
- }
-}
-
-// ToUint64E casts an interface to a uint64 type.
-func ToUint64E(i interface{}) (uint64, error) {
- i = indirect(i)
-
- intv, ok := toInt(i)
- if ok {
- if intv < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint64(intv), nil
- }
-
- switch s := i.(type) {
- case string:
- v, err := strconv.ParseUint(trimZeroDecimal(s), 0, 0)
- if err == nil {
- if v < 0 {
- return 0, errNegativeNotAllowed
- }
- return v, nil
- }
- return 0, fmt.Errorf("unable to cast %#v of type %T to uint64", i, i)
- case json.Number:
- return ToUint64E(string(s))
- case int64:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint64(s), nil
- case int32:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint64(s), nil
- case int16:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint64(s), nil
- case int8:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint64(s), nil
- case uint:
- return uint64(s), nil
- case uint64:
- return s, nil
- case uint32:
- return uint64(s), nil
- case uint16:
- return uint64(s), nil
- case uint8:
- return uint64(s), nil
- case float32:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint64(s), nil
- case float64:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint64(s), nil
- case bool:
- if s {
- return 1, nil
- }
- return 0, nil
- case nil:
- return 0, nil
- default:
- return 0, fmt.Errorf("unable to cast %#v of type %T to uint64", i, i)
- }
-}
-
-// ToUint32E casts an interface to a uint32 type.
-func ToUint32E(i interface{}) (uint32, error) {
- i = indirect(i)
-
- intv, ok := toInt(i)
- if ok {
- if intv < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint32(intv), nil
- }
-
- switch s := i.(type) {
- case string:
- v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
- if err == nil {
- if v < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint32(v), nil
- }
- return 0, fmt.Errorf("unable to cast %#v of type %T to uint32", i, i)
- case json.Number:
- return ToUint32E(string(s))
- case int64:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint32(s), nil
- case int32:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint32(s), nil
- case int16:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint32(s), nil
- case int8:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint32(s), nil
- case uint:
- return uint32(s), nil
- case uint64:
- return uint32(s), nil
- case uint32:
- return s, nil
- case uint16:
- return uint32(s), nil
- case uint8:
- return uint32(s), nil
- case float64:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint32(s), nil
- case float32:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint32(s), nil
- case bool:
- if s {
- return 1, nil
- }
- return 0, nil
- case nil:
- return 0, nil
- default:
- return 0, fmt.Errorf("unable to cast %#v of type %T to uint32", i, i)
- }
-}
-
-// ToUint16E casts an interface to a uint16 type.
-func ToUint16E(i interface{}) (uint16, error) {
- i = indirect(i)
-
- intv, ok := toInt(i)
- if ok {
- if intv < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint16(intv), nil
- }
-
- switch s := i.(type) {
- case string:
- v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
- if err == nil {
- if v < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint16(v), nil
- }
- return 0, fmt.Errorf("unable to cast %#v of type %T to uint16", i, i)
- case json.Number:
- return ToUint16E(string(s))
- case int64:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint16(s), nil
- case int32:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint16(s), nil
- case int16:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint16(s), nil
- case int8:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint16(s), nil
- case uint:
- return uint16(s), nil
- case uint64:
- return uint16(s), nil
- case uint32:
- return uint16(s), nil
- case uint16:
- return s, nil
- case uint8:
- return uint16(s), nil
- case float64:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint16(s), nil
- case float32:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint16(s), nil
- case bool:
- if s {
- return 1, nil
- }
- return 0, nil
- case nil:
- return 0, nil
- default:
- return 0, fmt.Errorf("unable to cast %#v of type %T to uint16", i, i)
- }
-}
-
-// ToUint8E casts an interface to a uint type.
-func ToUint8E(i interface{}) (uint8, error) {
- i = indirect(i)
-
- intv, ok := toInt(i)
- if ok {
- if intv < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint8(intv), nil
- }
-
- switch s := i.(type) {
- case string:
- v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
- if err == nil {
- if v < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint8(v), nil
- }
- return 0, fmt.Errorf("unable to cast %#v of type %T to uint8", i, i)
- case json.Number:
- return ToUint8E(string(s))
- case int64:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint8(s), nil
- case int32:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint8(s), nil
- case int16:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint8(s), nil
- case int8:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint8(s), nil
- case uint:
- return uint8(s), nil
- case uint64:
- return uint8(s), nil
- case uint32:
- return uint8(s), nil
- case uint16:
- return uint8(s), nil
- case uint8:
- return s, nil
- case float64:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint8(s), nil
- case float32:
- if s < 0 {
- return 0, errNegativeNotAllowed
- }
- return uint8(s), nil
- case bool:
- if s {
- return 1, nil
- }
- return 0, nil
- case nil:
- return 0, nil
- default:
- return 0, fmt.Errorf("unable to cast %#v of type %T to uint8", i, i)
- }
-}
-
-// From html/template/content.go
-// Copyright 2011 The Go Authors. All rights reserved.
-// indirect returns the value, after dereferencing as many times
-// as necessary to reach the base type (or nil).
-func indirect(a interface{}) interface{} {
- if a == nil {
- return nil
- }
- if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr {
- // Avoid creating a reflect.Value if it's not a pointer.
- return a
- }
- v := reflect.ValueOf(a)
- for v.Kind() == reflect.Ptr && !v.IsNil() {
- v = v.Elem()
- }
- return v.Interface()
-}
-
-// From html/template/content.go
-// Copyright 2011 The Go Authors. All rights reserved.
-// indirectToStringerOrError returns the value, after dereferencing as many times
-// as necessary to reach the base type (or nil) or an implementation of fmt.Stringer
-// or error,
-func indirectToStringerOrError(a interface{}) interface{} {
- if a == nil {
- return nil
- }
-
- errorType := reflect.TypeOf((*error)(nil)).Elem()
- fmtStringerType := reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
-
- v := reflect.ValueOf(a)
- for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Ptr && !v.IsNil() {
- v = v.Elem()
- }
- return v.Interface()
-}
-
-// ToStringE casts an interface to a string type.
-func ToStringE(i interface{}) (string, error) {
- i = indirectToStringerOrError(i)
-
- switch s := i.(type) {
- case string:
- return s, nil
- case bool:
- return strconv.FormatBool(s), nil
- case float64:
- return strconv.FormatFloat(s, 'f', -1, 64), nil
- case float32:
- return strconv.FormatFloat(float64(s), 'f', -1, 32), nil
- case int:
- return strconv.Itoa(s), nil
- case int64:
- return strconv.FormatInt(s, 10), nil
- case int32:
- return strconv.Itoa(int(s)), nil
- case int16:
- return strconv.FormatInt(int64(s), 10), nil
- case int8:
- return strconv.FormatInt(int64(s), 10), nil
- case uint:
- return strconv.FormatUint(uint64(s), 10), nil
- case uint64:
- return strconv.FormatUint(uint64(s), 10), nil
- case uint32:
- return strconv.FormatUint(uint64(s), 10), nil
- case uint16:
- return strconv.FormatUint(uint64(s), 10), nil
- case uint8:
- return strconv.FormatUint(uint64(s), 10), nil
- case json.Number:
- return s.String(), nil
- case []byte:
- return string(s), nil
- case template.HTML:
- return string(s), nil
- case template.URL:
- return string(s), nil
- case template.JS:
- return string(s), nil
- case template.CSS:
- return string(s), nil
- case template.HTMLAttr:
- return string(s), nil
- case nil:
- return "", nil
- case fmt.Stringer:
- return s.String(), nil
- case error:
- return s.Error(), nil
- default:
- return "", fmt.Errorf("unable to cast %#v of type %T to string", i, i)
- }
-}
-
-// ToStringMapStringE casts an interface to a map[string]string type.
-func ToStringMapStringE(i interface{}) (map[string]string, error) {
- m := map[string]string{}
-
- switch v := i.(type) {
- case map[string]string:
- return v, nil
- case map[string]interface{}:
- for k, val := range v {
- m[ToString(k)] = ToString(val)
- }
- return m, nil
- case map[interface{}]string:
- for k, val := range v {
- m[ToString(k)] = ToString(val)
- }
- return m, nil
- case map[interface{}]interface{}:
- for k, val := range v {
- m[ToString(k)] = ToString(val)
- }
- return m, nil
- case string:
- err := jsonStringToObject(v, &m)
- return m, err
- default:
- return m, fmt.Errorf("unable to cast %#v of type %T to map[string]string", i, i)
- }
-}
-
-// ToStringMapStringSliceE casts an interface to a map[string][]string type.
-func ToStringMapStringSliceE(i interface{}) (map[string][]string, error) {
- m := map[string][]string{}
-
- switch v := i.(type) {
- case map[string][]string:
- return v, nil
- case map[string][]interface{}:
- for k, val := range v {
- m[ToString(k)] = ToStringSlice(val)
- }
- return m, nil
- case map[string]string:
- for k, val := range v {
- m[ToString(k)] = []string{val}
- }
- case map[string]interface{}:
- for k, val := range v {
- switch vt := val.(type) {
- case []interface{}:
- m[ToString(k)] = ToStringSlice(vt)
- case []string:
- m[ToString(k)] = vt
- default:
- m[ToString(k)] = []string{ToString(val)}
- }
- }
- return m, nil
- case map[interface{}][]string:
- for k, val := range v {
- m[ToString(k)] = ToStringSlice(val)
- }
- return m, nil
- case map[interface{}]string:
- for k, val := range v {
- m[ToString(k)] = ToStringSlice(val)
- }
- return m, nil
- case map[interface{}][]interface{}:
- for k, val := range v {
- m[ToString(k)] = ToStringSlice(val)
- }
- return m, nil
- case map[interface{}]interface{}:
- for k, val := range v {
- key, err := ToStringE(k)
- if err != nil {
- return m, fmt.Errorf("unable to cast %#v of type %T to map[string][]string", i, i)
- }
- value, err := ToStringSliceE(val)
- if err != nil {
- return m, fmt.Errorf("unable to cast %#v of type %T to map[string][]string", i, i)
- }
- m[key] = value
- }
- case string:
- err := jsonStringToObject(v, &m)
- return m, err
- default:
- return m, fmt.Errorf("unable to cast %#v of type %T to map[string][]string", i, i)
- }
- return m, nil
-}
-
-// ToStringMapBoolE casts an interface to a map[string]bool type.
-func ToStringMapBoolE(i interface{}) (map[string]bool, error) {
- m := map[string]bool{}
-
- switch v := i.(type) {
- case map[interface{}]interface{}:
- for k, val := range v {
- m[ToString(k)] = ToBool(val)
- }
- return m, nil
- case map[string]interface{}:
- for k, val := range v {
- m[ToString(k)] = ToBool(val)
- }
- return m, nil
- case map[string]bool:
- return v, nil
- case string:
- err := jsonStringToObject(v, &m)
- return m, err
- default:
- return m, fmt.Errorf("unable to cast %#v of type %T to map[string]bool", i, i)
- }
-}
-
-// ToStringMapE casts an interface to a map[string]interface{} type.
-func ToStringMapE(i interface{}) (map[string]interface{}, error) {
- m := map[string]interface{}{}
-
- switch v := i.(type) {
- case map[interface{}]interface{}:
- for k, val := range v {
- m[ToString(k)] = val
- }
- return m, nil
- case map[string]interface{}:
- return v, nil
- case string:
- err := jsonStringToObject(v, &m)
- return m, err
- default:
- return m, fmt.Errorf("unable to cast %#v of type %T to map[string]interface{}", i, i)
- }
-}
-
-// ToStringMapIntE casts an interface to a map[string]int{} type.
-func ToStringMapIntE(i interface{}) (map[string]int, error) {
- m := map[string]int{}
- if i == nil {
- return m, fmt.Errorf("unable to cast %#v of type %T to map[string]int", i, i)
- }
-
- switch v := i.(type) {
- case map[interface{}]interface{}:
- for k, val := range v {
- m[ToString(k)] = ToInt(val)
- }
- return m, nil
- case map[string]interface{}:
- for k, val := range v {
- m[k] = ToInt(val)
- }
- return m, nil
- case map[string]int:
- return v, nil
- case string:
- err := jsonStringToObject(v, &m)
- return m, err
- }
-
- if reflect.TypeOf(i).Kind() != reflect.Map {
- return m, fmt.Errorf("unable to cast %#v of type %T to map[string]int", i, i)
- }
-
- mVal := reflect.ValueOf(m)
- v := reflect.ValueOf(i)
- for _, keyVal := range v.MapKeys() {
- val, err := ToIntE(v.MapIndex(keyVal).Interface())
- if err != nil {
- return m, fmt.Errorf("unable to cast %#v of type %T to map[string]int", i, i)
- }
- mVal.SetMapIndex(keyVal, reflect.ValueOf(val))
- }
- return m, nil
-}
-
-// ToStringMapInt64E casts an interface to a map[string]int64{} type.
-func ToStringMapInt64E(i interface{}) (map[string]int64, error) {
- m := map[string]int64{}
- if i == nil {
- return m, fmt.Errorf("unable to cast %#v of type %T to map[string]int64", i, i)
- }
-
- switch v := i.(type) {
- case map[interface{}]interface{}:
- for k, val := range v {
- m[ToString(k)] = ToInt64(val)
- }
- return m, nil
- case map[string]interface{}:
- for k, val := range v {
- m[k] = ToInt64(val)
- }
- return m, nil
- case map[string]int64:
- return v, nil
- case string:
- err := jsonStringToObject(v, &m)
- return m, err
- }
-
- if reflect.TypeOf(i).Kind() != reflect.Map {
- return m, fmt.Errorf("unable to cast %#v of type %T to map[string]int64", i, i)
- }
- mVal := reflect.ValueOf(m)
- v := reflect.ValueOf(i)
- for _, keyVal := range v.MapKeys() {
- val, err := ToInt64E(v.MapIndex(keyVal).Interface())
- if err != nil {
- return m, fmt.Errorf("unable to cast %#v of type %T to map[string]int64", i, i)
- }
- mVal.SetMapIndex(keyVal, reflect.ValueOf(val))
- }
- return m, nil
-}
-
-// ToSliceE casts an interface to a []interface{} type.
-func ToSliceE(i interface{}) ([]interface{}, error) {
- var s []interface{}
-
- switch v := i.(type) {
- case []interface{}:
- return append(s, v...), nil
- case []map[string]interface{}:
- for _, u := range v {
- s = append(s, u)
- }
- return s, nil
- default:
- return s, fmt.Errorf("unable to cast %#v of type %T to []interface{}", i, i)
- }
-}
-
-// ToBoolSliceE casts an interface to a []bool type.
-func ToBoolSliceE(i interface{}) ([]bool, error) {
- if i == nil {
- return []bool{}, fmt.Errorf("unable to cast %#v of type %T to []bool", i, i)
- }
-
- switch v := i.(type) {
- case []bool:
- return v, nil
- }
-
- kind := reflect.TypeOf(i).Kind()
- switch kind {
- case reflect.Slice, reflect.Array:
- s := reflect.ValueOf(i)
- a := make([]bool, s.Len())
- for j := 0; j < s.Len(); j++ {
- val, err := ToBoolE(s.Index(j).Interface())
- if err != nil {
- return []bool{}, fmt.Errorf("unable to cast %#v of type %T to []bool", i, i)
- }
- a[j] = val
- }
- return a, nil
- default:
- return []bool{}, fmt.Errorf("unable to cast %#v of type %T to []bool", i, i)
- }
-}
-
-// ToStringSliceE casts an interface to a []string type.
-func ToStringSliceE(i interface{}) ([]string, error) {
- var a []string
-
- switch v := i.(type) {
- case []interface{}:
- for _, u := range v {
- a = append(a, ToString(u))
- }
- return a, nil
- case []string:
- return v, nil
- case []int8:
- for _, u := range v {
- a = append(a, ToString(u))
- }
- return a, nil
- case []int:
- for _, u := range v {
- a = append(a, ToString(u))
- }
- return a, nil
- case []int32:
- for _, u := range v {
- a = append(a, ToString(u))
- }
- return a, nil
- case []int64:
- for _, u := range v {
- a = append(a, ToString(u))
- }
- return a, nil
- case []float32:
- for _, u := range v {
- a = append(a, ToString(u))
- }
- return a, nil
- case []float64:
- for _, u := range v {
- a = append(a, ToString(u))
- }
- return a, nil
- case string:
- return strings.Fields(v), nil
- case []error:
- for _, err := range i.([]error) {
- a = append(a, err.Error())
- }
- return a, nil
- case interface{}:
- str, err := ToStringE(v)
- if err != nil {
- return a, fmt.Errorf("unable to cast %#v of type %T to []string", i, i)
- }
- return []string{str}, nil
- default:
- return a, fmt.Errorf("unable to cast %#v of type %T to []string", i, i)
- }
-}
-
-// ToIntSliceE casts an interface to a []int type.
-func ToIntSliceE(i interface{}) ([]int, error) {
- if i == nil {
- return []int{}, fmt.Errorf("unable to cast %#v of type %T to []int", i, i)
- }
-
- switch v := i.(type) {
- case []int:
- return v, nil
- }
-
- kind := reflect.TypeOf(i).Kind()
- switch kind {
- case reflect.Slice, reflect.Array:
- s := reflect.ValueOf(i)
- a := make([]int, s.Len())
- for j := 0; j < s.Len(); j++ {
- val, err := ToIntE(s.Index(j).Interface())
- if err != nil {
- return []int{}, fmt.Errorf("unable to cast %#v of type %T to []int", i, i)
- }
- a[j] = val
- }
- return a, nil
- default:
- return []int{}, fmt.Errorf("unable to cast %#v of type %T to []int", i, i)
- }
-}
-
-// ToDurationSliceE casts an interface to a []time.Duration type.
-func ToDurationSliceE(i interface{}) ([]time.Duration, error) {
- if i == nil {
- return []time.Duration{}, fmt.Errorf("unable to cast %#v of type %T to []time.Duration", i, i)
- }
-
- switch v := i.(type) {
- case []time.Duration:
- return v, nil
- }
-
- kind := reflect.TypeOf(i).Kind()
- switch kind {
- case reflect.Slice, reflect.Array:
- s := reflect.ValueOf(i)
- a := make([]time.Duration, s.Len())
- for j := 0; j < s.Len(); j++ {
- val, err := ToDurationE(s.Index(j).Interface())
- if err != nil {
- return []time.Duration{}, fmt.Errorf("unable to cast %#v of type %T to []time.Duration", i, i)
- }
- a[j] = val
- }
- return a, nil
- default:
- return []time.Duration{}, fmt.Errorf("unable to cast %#v of type %T to []time.Duration", i, i)
- }
-}
-
-// StringToDate attempts to parse a string into a time.Time type using a
-// predefined list of formats. If no suitable format is found, an error is
-// returned.
-func StringToDate(s string) (time.Time, error) {
- return parseDateWith(s, time.UTC, timeFormats)
-}
-
-// StringToDateInDefaultLocation casts an empty interface to a time.Time,
-// interpreting inputs without a timezone to be in the given location,
-// or the local timezone if nil.
-func StringToDateInDefaultLocation(s string, location *time.Location) (time.Time, error) {
- return parseDateWith(s, location, timeFormats)
-}
-
-type timeFormatType int
-
-const (
- timeFormatNoTimezone timeFormatType = iota
- timeFormatNamedTimezone
- timeFormatNumericTimezone
- timeFormatNumericAndNamedTimezone
- timeFormatTimeOnly
-)
-
-type timeFormat struct {
- format string
- typ timeFormatType
-}
-
-func (f timeFormat) hasTimezone() bool {
- // We don't include the formats with only named timezones, see
- // https://github.com/golang/go/issues/19694#issuecomment-289103522
- return f.typ >= timeFormatNumericTimezone && f.typ <= timeFormatNumericAndNamedTimezone
-}
-
-var timeFormats = []timeFormat{
- // Keep common formats at the top.
- {"2006-01-02", timeFormatNoTimezone},
- {time.RFC3339, timeFormatNumericTimezone},
- {"2006-01-02T15:04:05", timeFormatNoTimezone}, // iso8601 without timezone
- {time.RFC1123Z, timeFormatNumericTimezone},
- {time.RFC1123, timeFormatNamedTimezone},
- {time.RFC822Z, timeFormatNumericTimezone},
- {time.RFC822, timeFormatNamedTimezone},
- {time.RFC850, timeFormatNamedTimezone},
- {"2006-01-02 15:04:05.999999999 -0700 MST", timeFormatNumericAndNamedTimezone}, // Time.String()
- {"2006-01-02T15:04:05-0700", timeFormatNumericTimezone}, // RFC3339 without timezone hh:mm colon
- {"2006-01-02 15:04:05Z0700", timeFormatNumericTimezone}, // RFC3339 without T or timezone hh:mm colon
- {"2006-01-02 15:04:05", timeFormatNoTimezone},
- {time.ANSIC, timeFormatNoTimezone},
- {time.UnixDate, timeFormatNamedTimezone},
- {time.RubyDate, timeFormatNumericTimezone},
- {"2006-01-02 15:04:05Z07:00", timeFormatNumericTimezone},
- {"02 Jan 2006", timeFormatNoTimezone},
- {"2006-01-02 15:04:05 -07:00", timeFormatNumericTimezone},
- {"2006-01-02 15:04:05 -0700", timeFormatNumericTimezone},
- {time.Kitchen, timeFormatTimeOnly},
- {time.Stamp, timeFormatTimeOnly},
- {time.StampMilli, timeFormatTimeOnly},
- {time.StampMicro, timeFormatTimeOnly},
- {time.StampNano, timeFormatTimeOnly},
-}
-
-func parseDateWith(s string, location *time.Location, formats []timeFormat) (d time.Time, e error) {
- for _, format := range formats {
- if d, e = time.Parse(format.format, s); e == nil {
-
- // Some time formats have a zone name, but no offset, so it gets
- // put in that zone name (not the default one passed in to us), but
- // without that zone's offset. So set the location manually.
- if format.typ <= timeFormatNamedTimezone {
- if location == nil {
- location = time.Local
- }
- year, month, day := d.Date()
- hour, min, sec := d.Clock()
- d = time.Date(year, month, day, hour, min, sec, d.Nanosecond(), location)
- }
-
- return
- }
- }
- return d, fmt.Errorf("unable to parse date: %s", s)
-}
-
-// jsonStringToObject attempts to unmarshall a string as JSON into
-// the object passed as pointer.
-func jsonStringToObject(s string, v interface{}) error {
- data := []byte(s)
- return json.Unmarshal(data, v)
-}
-
-// toInt returns the int value of v if v or v's underlying type
-// is an int.
-// Note that this will return false for int64 etc. types.
-func toInt(v interface{}) (int, bool) {
- switch v := v.(type) {
- case int:
- return v, true
- case time.Weekday:
- return int(v), true
- case time.Month:
- return int(v), true
- default:
- return 0, false
- }
-}
-
-func trimZeroDecimal(s string) string {
- var foundZero bool
- for i := len(s); i > 0; i-- {
- switch s[i-1] {
- case '.':
- if foundZero {
- return s[:i-1]
- }
- case '0':
- foundZero = true
- default:
- return s
- }
- }
- return s
-}
diff --git a/vendor/github.com/spf13/cast/indirect.go b/vendor/github.com/spf13/cast/indirect.go
new file mode 100644
index 000000000..093345f73
--- /dev/null
+++ b/vendor/github.com/spf13/cast/indirect.go
@@ -0,0 +1,37 @@
+// Copyright © 2014 Steve Francia .
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+package cast
+
+import (
+ "reflect"
+)
+
+// From html/template/content.go
+// Copyright 2011 The Go Authors. All rights reserved.
+// indirect returns the value, after dereferencing as many times
+// as necessary to reach the base type (or nil).
+func indirect(i any) (any, bool) {
+ if i == nil {
+ return nil, false
+ }
+
+ if t := reflect.TypeOf(i); t.Kind() != reflect.Ptr {
+ // Avoid creating a reflect.Value if it's not a pointer.
+ return i, false
+ }
+
+ v := reflect.ValueOf(i)
+
+ for v.Kind() == reflect.Ptr || (v.Kind() == reflect.Interface && v.Elem().Kind() == reflect.Ptr) {
+ if v.IsNil() {
+ return nil, true
+ }
+
+ v = v.Elem()
+ }
+
+ return v.Interface(), true
+}
diff --git a/vendor/github.com/spf13/cast/internal/time.go b/vendor/github.com/spf13/cast/internal/time.go
new file mode 100644
index 000000000..906e9aece
--- /dev/null
+++ b/vendor/github.com/spf13/cast/internal/time.go
@@ -0,0 +1,79 @@
+package internal
+
+import (
+ "fmt"
+ "time"
+)
+
+//go:generate stringer -type=TimeFormatType
+
+type TimeFormatType int
+
+const (
+ TimeFormatNoTimezone TimeFormatType = iota
+ TimeFormatNamedTimezone
+ TimeFormatNumericTimezone
+ TimeFormatNumericAndNamedTimezone
+ TimeFormatTimeOnly
+)
+
+type TimeFormat struct {
+ Format string
+ Typ TimeFormatType
+}
+
+func (f TimeFormat) HasTimezone() bool {
+ // We don't include the formats with only named timezones, see
+ // https://github.com/golang/go/issues/19694#issuecomment-289103522
+ return f.Typ >= TimeFormatNumericTimezone && f.Typ <= TimeFormatNumericAndNamedTimezone
+}
+
+var TimeFormats = []TimeFormat{
+ // Keep common formats at the top.
+ {"2006-01-02", TimeFormatNoTimezone},
+ {time.RFC3339, TimeFormatNumericTimezone},
+ {"2006-01-02T15:04:05", TimeFormatNoTimezone}, // iso8601 without timezone
+ {time.RFC1123Z, TimeFormatNumericTimezone},
+ {time.RFC1123, TimeFormatNamedTimezone},
+ {time.RFC822Z, TimeFormatNumericTimezone},
+ {time.RFC822, TimeFormatNamedTimezone},
+ {time.RFC850, TimeFormatNamedTimezone},
+ {"2006-01-02 15:04:05.999999999 -0700 MST", TimeFormatNumericAndNamedTimezone}, // Time.String()
+ {"2006-01-02T15:04:05-0700", TimeFormatNumericTimezone}, // RFC3339 without timezone hh:mm colon
+ {"2006-01-02 15:04:05Z0700", TimeFormatNumericTimezone}, // RFC3339 without T or timezone hh:mm colon
+ {"2006-01-02 15:04:05", TimeFormatNoTimezone},
+ {time.ANSIC, TimeFormatNoTimezone},
+ {time.UnixDate, TimeFormatNamedTimezone},
+ {time.RubyDate, TimeFormatNumericTimezone},
+ {"2006-01-02 15:04:05Z07:00", TimeFormatNumericTimezone},
+ {"02 Jan 2006", TimeFormatNoTimezone},
+ {"2006-01-02 15:04:05 -07:00", TimeFormatNumericTimezone},
+ {"2006-01-02 15:04:05 -0700", TimeFormatNumericTimezone},
+ {time.Kitchen, TimeFormatTimeOnly},
+ {time.Stamp, TimeFormatTimeOnly},
+ {time.StampMilli, TimeFormatTimeOnly},
+ {time.StampMicro, TimeFormatTimeOnly},
+ {time.StampNano, TimeFormatTimeOnly},
+}
+
+func ParseDateWith(s string, location *time.Location, formats []TimeFormat) (d time.Time, e error) {
+ for _, format := range formats {
+ if d, e = time.Parse(format.Format, s); e == nil {
+
+ // Some time formats have a zone name, but no offset, so it gets
+ // put in that zone name (not the default one passed in to us), but
+ // without that zone's offset. So set the location manually.
+ if format.Typ <= TimeFormatNamedTimezone {
+ if location == nil {
+ location = time.Local
+ }
+ year, month, day := d.Date()
+ hour, min, sec := d.Clock()
+ d = time.Date(year, month, day, hour, min, sec, d.Nanosecond(), location)
+ }
+
+ return
+ }
+ }
+ return d, fmt.Errorf("unable to parse date: %s", s)
+}
diff --git a/vendor/github.com/spf13/cast/internal/timeformattype_string.go b/vendor/github.com/spf13/cast/internal/timeformattype_string.go
new file mode 100644
index 000000000..60a29a862
--- /dev/null
+++ b/vendor/github.com/spf13/cast/internal/timeformattype_string.go
@@ -0,0 +1,27 @@
+// Code generated by "stringer -type=TimeFormatType"; DO NOT EDIT.
+
+package internal
+
+import "strconv"
+
+func _() {
+ // An "invalid array index" compiler error signifies that the constant values have changed.
+ // Re-run the stringer command to generate them again.
+ var x [1]struct{}
+ _ = x[TimeFormatNoTimezone-0]
+ _ = x[TimeFormatNamedTimezone-1]
+ _ = x[TimeFormatNumericTimezone-2]
+ _ = x[TimeFormatNumericAndNamedTimezone-3]
+ _ = x[TimeFormatTimeOnly-4]
+}
+
+const _TimeFormatType_name = "TimeFormatNoTimezoneTimeFormatNamedTimezoneTimeFormatNumericTimezoneTimeFormatNumericAndNamedTimezoneTimeFormatTimeOnly"
+
+var _TimeFormatType_index = [...]uint8{0, 20, 43, 68, 101, 119}
+
+func (i TimeFormatType) String() string {
+ if i < 0 || i >= TimeFormatType(len(_TimeFormatType_index)-1) {
+ return "TimeFormatType(" + strconv.FormatInt(int64(i), 10) + ")"
+ }
+ return _TimeFormatType_name[_TimeFormatType_index[i]:_TimeFormatType_index[i+1]]
+}
diff --git a/vendor/github.com/spf13/cast/map.go b/vendor/github.com/spf13/cast/map.go
new file mode 100644
index 000000000..858d4ee43
--- /dev/null
+++ b/vendor/github.com/spf13/cast/map.go
@@ -0,0 +1,212 @@
+// Copyright © 2014 Steve Francia .
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+package cast
+
+import (
+ "encoding/json"
+ "fmt"
+ "reflect"
+)
+
+func toMapE[K comparable, V any](i any, keyFn func(any) K, valFn func(any) V) (map[K]V, error) {
+ m := map[K]V{}
+
+ if i == nil {
+ return m, fmt.Errorf(errorMsg, i, i, m)
+ }
+
+ switch v := i.(type) {
+ case map[K]V:
+ return v, nil
+
+ case map[K]any:
+ for k, val := range v {
+ m[k] = valFn(val)
+ }
+
+ return m, nil
+
+ case map[any]V:
+ for k, val := range v {
+ m[keyFn(k)] = val
+ }
+
+ return m, nil
+
+ case map[any]any:
+ for k, val := range v {
+ m[keyFn(k)] = valFn(val)
+ }
+
+ return m, nil
+
+ case string:
+ err := jsonStringToObject(v, &m)
+ return m, err
+
+ default:
+ return m, fmt.Errorf(errorMsg, i, i, m)
+ }
+}
+
+func toStringMapE[T any](i any, fn func(any) T) (map[string]T, error) {
+ return toMapE(i, ToString, fn)
+}
+
+// ToStringMapStringE casts any value to a map[string]string type.
+func ToStringMapStringE(i any) (map[string]string, error) {
+ return toStringMapE(i, ToString)
+}
+
+// ToStringMapStringSliceE casts any value to a map[string][]string type.
+func ToStringMapStringSliceE(i any) (map[string][]string, error) {
+ m := map[string][]string{}
+
+ switch v := i.(type) {
+ case map[string][]string:
+ return v, nil
+ case map[string][]any:
+ for k, val := range v {
+ m[ToString(k)] = ToStringSlice(val)
+ }
+ return m, nil
+ case map[string]string:
+ for k, val := range v {
+ m[ToString(k)] = []string{val}
+ }
+ case map[string]any:
+ for k, val := range v {
+ switch vt := val.(type) {
+ case []any:
+ m[ToString(k)] = ToStringSlice(vt)
+ case []string:
+ m[ToString(k)] = vt
+ default:
+ m[ToString(k)] = []string{ToString(val)}
+ }
+ }
+ return m, nil
+ case map[any][]string:
+ for k, val := range v {
+ m[ToString(k)] = ToStringSlice(val)
+ }
+ return m, nil
+ case map[any]string:
+ for k, val := range v {
+ m[ToString(k)] = ToStringSlice(val)
+ }
+ return m, nil
+ case map[any][]any:
+ for k, val := range v {
+ m[ToString(k)] = ToStringSlice(val)
+ }
+ return m, nil
+ case map[any]any:
+ for k, val := range v {
+ key, err := ToStringE(k)
+ if err != nil {
+ return m, fmt.Errorf(errorMsg, i, i, m)
+ }
+ value, err := ToStringSliceE(val)
+ if err != nil {
+ return m, fmt.Errorf(errorMsg, i, i, m)
+ }
+ m[key] = value
+ }
+ case string:
+ err := jsonStringToObject(v, &m)
+ return m, err
+ default:
+ return m, fmt.Errorf(errorMsg, i, i, m)
+ }
+
+ return m, nil
+}
+
+// ToStringMapBoolE casts any value to a map[string]bool type.
+func ToStringMapBoolE(i any) (map[string]bool, error) {
+ return toStringMapE(i, ToBool)
+}
+
+// ToStringMapE casts any value to a map[string]any type.
+func ToStringMapE(i any) (map[string]any, error) {
+ fn := func(i any) any { return i }
+
+ return toStringMapE(i, fn)
+}
+
+func toStringMapIntE[T int | int64](i any, fn func(any) T, fnE func(any) (T, error)) (map[string]T, error) {
+ m := map[string]T{}
+
+ if i == nil {
+ return nil, fmt.Errorf(errorMsg, i, i, m)
+ }
+
+ switch v := i.(type) {
+ case map[string]T:
+ return v, nil
+
+ case map[string]any:
+ for k, val := range v {
+ m[k] = fn(val)
+ }
+
+ return m, nil
+
+ case map[any]T:
+ for k, val := range v {
+ m[ToString(k)] = val
+ }
+
+ return m, nil
+
+ case map[any]any:
+ for k, val := range v {
+ m[ToString(k)] = fn(val)
+ }
+
+ return m, nil
+
+ case string:
+ err := jsonStringToObject(v, &m)
+ return m, err
+ }
+
+ if reflect.TypeOf(i).Kind() != reflect.Map {
+ return m, fmt.Errorf(errorMsg, i, i, m)
+ }
+
+ mVal := reflect.ValueOf(m)
+ v := reflect.ValueOf(i)
+
+ for _, keyVal := range v.MapKeys() {
+ val, err := fnE(v.MapIndex(keyVal).Interface())
+ if err != nil {
+ return m, fmt.Errorf(errorMsg, i, i, m)
+ }
+
+ mVal.SetMapIndex(keyVal, reflect.ValueOf(val))
+ }
+
+ return m, nil
+}
+
+// ToStringMapIntE casts any value to a map[string]int type.
+func ToStringMapIntE(i any) (map[string]int, error) {
+ return toStringMapIntE(i, ToInt, ToIntE)
+}
+
+// ToStringMapInt64E casts any value to a map[string]int64 type.
+func ToStringMapInt64E(i any) (map[string]int64, error) {
+ return toStringMapIntE(i, ToInt64, ToInt64E)
+}
+
+// jsonStringToObject attempts to unmarshall a string as JSON into
+// the object passed as pointer.
+func jsonStringToObject(s string, v any) error {
+ data := []byte(s)
+ return json.Unmarshal(data, v)
+}
diff --git a/vendor/github.com/spf13/cast/number.go b/vendor/github.com/spf13/cast/number.go
new file mode 100644
index 000000000..a58dc4d1e
--- /dev/null
+++ b/vendor/github.com/spf13/cast/number.go
@@ -0,0 +1,549 @@
+// Copyright © 2014 Steve Francia .
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+package cast
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+)
+
+var errNegativeNotAllowed = errors.New("unable to cast negative value")
+
+type float64EProvider interface {
+ Float64() (float64, error)
+}
+
+type float64Provider interface {
+ Float64() float64
+}
+
+// Number is a type parameter constraint for functions accepting number types.
+//
+// It represents the supported number types this package can cast to.
+type Number interface {
+ int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | float32 | float64
+}
+
+type integer interface {
+ int | int8 | int16 | int32 | int64
+}
+
+type unsigned interface {
+ uint | uint8 | uint16 | uint32 | uint64
+}
+
+type float interface {
+ float32 | float64
+}
+
+// ToNumberE casts any value to a [Number] type.
+func ToNumberE[T Number](i any) (T, error) {
+ var t T
+
+ switch any(t).(type) {
+ case int:
+ return toNumberE[T](i, parseNumber[T])
+ case int8:
+ return toNumberE[T](i, parseNumber[T])
+ case int16:
+ return toNumberE[T](i, parseNumber[T])
+ case int32:
+ return toNumberE[T](i, parseNumber[T])
+ case int64:
+ return toNumberE[T](i, parseNumber[T])
+ case uint:
+ return toUnsignedNumberE[T](i, parseNumber[T])
+ case uint8:
+ return toUnsignedNumberE[T](i, parseNumber[T])
+ case uint16:
+ return toUnsignedNumberE[T](i, parseNumber[T])
+ case uint32:
+ return toUnsignedNumberE[T](i, parseNumber[T])
+ case uint64:
+ return toUnsignedNumberE[T](i, parseNumber[T])
+ case float32:
+ return toNumberE[T](i, parseNumber[T])
+ case float64:
+ return toNumberE[T](i, parseNumber[T])
+ default:
+ return 0, fmt.Errorf("unknown number type: %T", t)
+ }
+}
+
+// ToNumber casts any value to a [Number] type.
+func ToNumber[T Number](i any) T {
+ v, _ := ToNumberE[T](i)
+
+ return v
+}
+
+// toNumber's semantics differ from other "to" functions.
+// It returns false as the second parameter if the conversion fails.
+// This is to signal other callers that they should proceed with their own conversions.
+func toNumber[T Number](i any) (T, bool) {
+ i, _ = indirect(i)
+
+ switch s := i.(type) {
+ case T:
+ return s, true
+ case int:
+ return T(s), true
+ case int8:
+ return T(s), true
+ case int16:
+ return T(s), true
+ case int32:
+ return T(s), true
+ case int64:
+ return T(s), true
+ case uint:
+ return T(s), true
+ case uint8:
+ return T(s), true
+ case uint16:
+ return T(s), true
+ case uint32:
+ return T(s), true
+ case uint64:
+ return T(s), true
+ case float32:
+ return T(s), true
+ case float64:
+ return T(s), true
+ case bool:
+ if s {
+ return 1, true
+ }
+
+ return 0, true
+ case nil:
+ return 0, true
+ case time.Weekday:
+ return T(s), true
+ case time.Month:
+ return T(s), true
+ }
+
+ return 0, false
+}
+
+func toNumberE[T Number](i any, parseFn func(string) (T, error)) (T, error) {
+ n, ok := toNumber[T](i)
+ if ok {
+ return n, nil
+ }
+
+ i, _ = indirect(i)
+
+ switch s := i.(type) {
+ case string:
+ if s == "" {
+ return 0, nil
+ }
+
+ v, err := parseFn(s)
+ if err != nil {
+ return 0, fmt.Errorf(errorMsgWith, i, i, n, err)
+ }
+
+ return v, nil
+ case json.Number:
+ if s == "" {
+ return 0, nil
+ }
+
+ v, err := parseFn(string(s))
+ if err != nil {
+ return 0, fmt.Errorf(errorMsgWith, i, i, n, err)
+ }
+
+ return v, nil
+ case float64EProvider:
+ if _, ok := any(n).(float64); !ok {
+ return 0, fmt.Errorf(errorMsg, i, i, n)
+ }
+
+ v, err := s.Float64()
+ if err != nil {
+ return 0, fmt.Errorf(errorMsg, i, i, n)
+ }
+
+ return T(v), nil
+ case float64Provider:
+ if _, ok := any(n).(float64); !ok {
+ return 0, fmt.Errorf(errorMsg, i, i, n)
+ }
+
+ return T(s.Float64()), nil
+ default:
+ if i, ok := resolveAlias(i); ok {
+ return toNumberE(i, parseFn)
+ }
+
+ return 0, fmt.Errorf(errorMsg, i, i, n)
+ }
+}
+
+func toUnsignedNumber[T Number](i any) (T, bool, bool) {
+ i, _ = indirect(i)
+
+ switch s := i.(type) {
+ case T:
+ return s, true, true
+ case int:
+ if s < 0 {
+ return 0, false, false
+ }
+
+ return T(s), true, true
+ case int8:
+ if s < 0 {
+ return 0, false, false
+ }
+
+ return T(s), true, true
+ case int16:
+ if s < 0 {
+ return 0, false, false
+ }
+
+ return T(s), true, true
+ case int32:
+ if s < 0 {
+ return 0, false, false
+ }
+
+ return T(s), true, true
+ case int64:
+ if s < 0 {
+ return 0, false, false
+ }
+
+ return T(s), true, true
+ case uint:
+ return T(s), true, true
+ case uint8:
+ return T(s), true, true
+ case uint16:
+ return T(s), true, true
+ case uint32:
+ return T(s), true, true
+ case uint64:
+ return T(s), true, true
+ case float32:
+ if s < 0 {
+ return 0, false, false
+ }
+
+ return T(s), true, true
+ case float64:
+ if s < 0 {
+ return 0, false, false
+ }
+
+ return T(s), true, true
+ case bool:
+ if s {
+ return 1, true, true
+ }
+
+ return 0, true, true
+ case nil:
+ return 0, true, true
+ case time.Weekday:
+ if s < 0 {
+ return 0, false, false
+ }
+
+ return T(s), true, true
+ case time.Month:
+ if s < 0 {
+ return 0, false, false
+ }
+
+ return T(s), true, true
+ }
+
+ return 0, true, false
+}
+
+func toUnsignedNumberE[T Number](i any, parseFn func(string) (T, error)) (T, error) {
+ n, valid, ok := toUnsignedNumber[T](i)
+ if ok {
+ return n, nil
+ }
+
+ i, _ = indirect(i)
+
+ if !valid {
+ return 0, errNegativeNotAllowed
+ }
+
+ switch s := i.(type) {
+ case string:
+ if s == "" {
+ return 0, nil
+ }
+
+ v, err := parseFn(s)
+ if err != nil {
+ return 0, fmt.Errorf(errorMsgWith, i, i, n, err)
+ }
+
+ return v, nil
+ case json.Number:
+ if s == "" {
+ return 0, nil
+ }
+
+ v, err := parseFn(string(s))
+ if err != nil {
+ return 0, fmt.Errorf(errorMsgWith, i, i, n, err)
+ }
+
+ return v, nil
+ case float64EProvider:
+ if _, ok := any(n).(float64); !ok {
+ return 0, fmt.Errorf(errorMsg, i, i, n)
+ }
+
+ v, err := s.Float64()
+ if err != nil {
+ return 0, fmt.Errorf(errorMsg, i, i, n)
+ }
+
+ if v < 0 {
+ return 0, errNegativeNotAllowed
+ }
+
+ return T(v), nil
+ case float64Provider:
+ if _, ok := any(n).(float64); !ok {
+ return 0, fmt.Errorf(errorMsg, i, i, n)
+ }
+
+ v := s.Float64()
+
+ if v < 0 {
+ return 0, errNegativeNotAllowed
+ }
+
+ return T(v), nil
+ default:
+ if i, ok := resolveAlias(i); ok {
+ return toUnsignedNumberE(i, parseFn)
+ }
+
+ return 0, fmt.Errorf(errorMsg, i, i, n)
+ }
+}
+
+func parseNumber[T Number](s string) (T, error) {
+ var t T
+
+ switch any(t).(type) {
+ case int:
+ v, err := parseInt[int](s)
+
+ return T(v), err
+ case int8:
+ v, err := parseInt[int8](s)
+
+ return T(v), err
+ case int16:
+ v, err := parseInt[int16](s)
+
+ return T(v), err
+ case int32:
+ v, err := parseInt[int32](s)
+
+ return T(v), err
+ case int64:
+ v, err := parseInt[int64](s)
+
+ return T(v), err
+ case uint:
+ v, err := parseUint[uint](s)
+
+ return T(v), err
+ case uint8:
+ v, err := parseUint[uint8](s)
+
+ return T(v), err
+ case uint16:
+ v, err := parseUint[uint16](s)
+
+ return T(v), err
+ case uint32:
+ v, err := parseUint[uint32](s)
+
+ return T(v), err
+ case uint64:
+ v, err := parseUint[uint64](s)
+
+ return T(v), err
+ case float32:
+ v, err := strconv.ParseFloat(s, 32)
+
+ return T(v), err
+ case float64:
+ v, err := strconv.ParseFloat(s, 64)
+
+ return T(v), err
+
+ default:
+ return 0, fmt.Errorf("unknown number type: %T", t)
+ }
+}
+
+func parseInt[T integer](s string) (T, error) {
+ v, err := strconv.ParseInt(trimDecimal(s), 0, 0)
+ if err != nil {
+ return 0, err
+ }
+
+ return T(v), nil
+}
+
+func parseUint[T unsigned](s string) (T, error) {
+ v, err := strconv.ParseUint(strings.TrimLeft(trimDecimal(s), "+"), 0, 0)
+ if err != nil {
+ return 0, err
+ }
+
+ return T(v), nil
+}
+
+func parseFloat[T float](s string) (T, error) {
+ var t T
+
+ var v any
+ var err error
+
+ switch any(t).(type) {
+ case float32:
+ n, e := strconv.ParseFloat(s, 32)
+
+ v = float32(n)
+ err = e
+ case float64:
+ n, e := strconv.ParseFloat(s, 64)
+
+ v = float64(n)
+ err = e
+ }
+
+ return v.(T), err
+}
+
+// ToFloat64E casts an interface to a float64 type.
+func ToFloat64E(i any) (float64, error) {
+ return toNumberE[float64](i, parseFloat[float64])
+}
+
+// ToFloat32E casts an interface to a float32 type.
+func ToFloat32E(i any) (float32, error) {
+ return toNumberE[float32](i, parseFloat[float32])
+}
+
+// ToInt64E casts an interface to an int64 type.
+func ToInt64E(i any) (int64, error) {
+ return toNumberE[int64](i, parseInt[int64])
+}
+
+// ToInt32E casts an interface to an int32 type.
+func ToInt32E(i any) (int32, error) {
+ return toNumberE[int32](i, parseInt[int32])
+}
+
+// ToInt16E casts an interface to an int16 type.
+func ToInt16E(i any) (int16, error) {
+ return toNumberE[int16](i, parseInt[int16])
+}
+
+// ToInt8E casts an interface to an int8 type.
+func ToInt8E(i any) (int8, error) {
+ return toNumberE[int8](i, parseInt[int8])
+}
+
+// ToIntE casts an interface to an int type.
+func ToIntE(i any) (int, error) {
+ return toNumberE[int](i, parseInt[int])
+}
+
+// ToUintE casts an interface to a uint type.
+func ToUintE(i any) (uint, error) {
+ return toUnsignedNumberE[uint](i, parseUint[uint])
+}
+
+// ToUint64E casts an interface to a uint64 type.
+func ToUint64E(i any) (uint64, error) {
+ return toUnsignedNumberE[uint64](i, parseUint[uint64])
+}
+
+// ToUint32E casts an interface to a uint32 type.
+func ToUint32E(i any) (uint32, error) {
+ return toUnsignedNumberE[uint32](i, parseUint[uint32])
+}
+
+// ToUint16E casts an interface to a uint16 type.
+func ToUint16E(i any) (uint16, error) {
+ return toUnsignedNumberE[uint16](i, parseUint[uint16])
+}
+
+// ToUint8E casts an interface to a uint type.
+func ToUint8E(i any) (uint8, error) {
+ return toUnsignedNumberE[uint8](i, parseUint[uint8])
+}
+
+func trimZeroDecimal(s string) string {
+ var foundZero bool
+ for i := len(s); i > 0; i-- {
+ switch s[i-1] {
+ case '.':
+ if foundZero {
+ return s[:i-1]
+ }
+ case '0':
+ foundZero = true
+ default:
+ return s
+ }
+ }
+ return s
+}
+
+var stringNumberRe = regexp.MustCompile(`^([-+]?\d*)(\.\d*)?$`)
+
+// see [BenchmarkDecimal] for details about the implementation
+func trimDecimal(s string) string {
+ if !strings.Contains(s, ".") {
+ return s
+ }
+
+ matches := stringNumberRe.FindStringSubmatch(s)
+ if matches != nil {
+ // matches[1] is the captured integer part with sign
+ s = matches[1]
+
+ // handle special cases
+ switch s {
+ case "-", "+":
+ s += "0"
+ case "":
+ s = "0"
+ }
+
+ return s
+ }
+
+ return s
+}
diff --git a/vendor/github.com/spf13/cast/slice.go b/vendor/github.com/spf13/cast/slice.go
new file mode 100644
index 000000000..e6a8328c6
--- /dev/null
+++ b/vendor/github.com/spf13/cast/slice.go
@@ -0,0 +1,106 @@
+// Copyright © 2014 Steve Francia .
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+package cast
+
+import (
+ "fmt"
+ "reflect"
+ "strings"
+)
+
+// ToSliceE casts any value to a []any type.
+func ToSliceE(i any) ([]any, error) {
+ i, _ = indirect(i)
+
+ var s []any
+
+ switch v := i.(type) {
+ case []any:
+ // TODO: use slices.Clone
+ return append(s, v...), nil
+ case []map[string]any:
+ for _, u := range v {
+ s = append(s, u)
+ }
+
+ return s, nil
+ default:
+ return s, fmt.Errorf(errorMsg, i, i, s)
+ }
+}
+
+func toSliceE[T Basic](i any) ([]T, error) {
+ v, ok, err := toSliceEOk[T](i)
+ if err != nil {
+ return nil, err
+ }
+
+ if !ok {
+ return nil, fmt.Errorf(errorMsg, i, i, []T{})
+ }
+
+ return v, nil
+}
+
+func toSliceEOk[T Basic](i any) ([]T, bool, error) {
+ i, _ = indirect(i)
+ if i == nil {
+ return nil, true, fmt.Errorf(errorMsg, i, i, []T{})
+ }
+
+ switch v := i.(type) {
+ case []T:
+ // TODO: clone slice
+ return v, true, nil
+ }
+
+ kind := reflect.TypeOf(i).Kind()
+ switch kind {
+ case reflect.Slice, reflect.Array:
+ s := reflect.ValueOf(i)
+ a := make([]T, s.Len())
+
+ for j := 0; j < s.Len(); j++ {
+ val, err := ToE[T](s.Index(j).Interface())
+ if err != nil {
+ return nil, true, fmt.Errorf(errorMsg, i, i, []T{})
+ }
+
+ a[j] = val
+ }
+
+ return a, true, nil
+ default:
+ return nil, false, nil
+ }
+}
+
+// ToStringSliceE casts any value to a []string type.
+func ToStringSliceE(i any) ([]string, error) {
+ if a, ok, err := toSliceEOk[string](i); ok {
+ if err != nil {
+ return nil, err
+ }
+
+ return a, nil
+ }
+
+ var a []string
+
+ switch v := i.(type) {
+ case string:
+ return strings.Fields(v), nil
+ case any:
+ str, err := ToStringE(v)
+ if err != nil {
+ return nil, fmt.Errorf(errorMsg, i, i, a)
+ }
+
+ return []string{str}, nil
+ default:
+ return nil, fmt.Errorf(errorMsg, i, i, a)
+ }
+}
diff --git a/vendor/github.com/spf13/cast/time.go b/vendor/github.com/spf13/cast/time.go
new file mode 100644
index 000000000..744cd5acc
--- /dev/null
+++ b/vendor/github.com/spf13/cast/time.go
@@ -0,0 +1,116 @@
+// Copyright © 2014 Steve Francia .
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file.
+
+package cast
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "strings"
+ "time"
+
+ "github.com/spf13/cast/internal"
+)
+
+// ToTimeE any value to a [time.Time] type.
+func ToTimeE(i any) (time.Time, error) {
+ return ToTimeInDefaultLocationE(i, time.UTC)
+}
+
+// ToTimeInDefaultLocationE casts an empty interface to [time.Time],
+// interpreting inputs without a timezone to be in the given location,
+// or the local timezone if nil.
+func ToTimeInDefaultLocationE(i any, location *time.Location) (tim time.Time, err error) {
+ i, _ = indirect(i)
+
+ switch v := i.(type) {
+ case time.Time:
+ return v, nil
+ case string:
+ return StringToDateInDefaultLocation(v, location)
+ case json.Number:
+ // Originally this used ToInt64E, but adding string float conversion broke ToTime.
+ // the behavior of ToTime would have changed if we continued using it.
+ // For now, using json.Number's own Int64 method should be good enough to preserve backwards compatibility.
+ v = json.Number(trimZeroDecimal(string(v)))
+ s, err1 := v.Int64()
+ if err1 != nil {
+ return time.Time{}, fmt.Errorf(errorMsg, i, i, time.Time{})
+ }
+ return time.Unix(s, 0), nil
+ case int:
+ return time.Unix(int64(v), 0), nil
+ case int32:
+ return time.Unix(int64(v), 0), nil
+ case int64:
+ return time.Unix(v, 0), nil
+ case uint:
+ return time.Unix(int64(v), 0), nil
+ case uint32:
+ return time.Unix(int64(v), 0), nil
+ case uint64:
+ return time.Unix(int64(v), 0), nil
+ case nil:
+ return time.Time{}, nil
+ default:
+ return time.Time{}, fmt.Errorf(errorMsg, i, i, time.Time{})
+ }
+}
+
+// ToDurationE casts any value to a [time.Duration] type.
+func ToDurationE(i any) (time.Duration, error) {
+ i, _ = indirect(i)
+
+ switch s := i.(type) {
+ case time.Duration:
+ return s, nil
+ case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
+ v, err := ToInt64E(s)
+ if err != nil {
+ // TODO: once there is better error handling, this should be easier
+ return 0, errors.New(strings.ReplaceAll(err.Error(), " int64", "time.Duration"))
+ }
+
+ return time.Duration(v), nil
+ case float32, float64, float64EProvider, float64Provider:
+ v, err := ToFloat64E(s)
+ if err != nil {
+ // TODO: once there is better error handling, this should be easier
+ return 0, errors.New(strings.ReplaceAll(err.Error(), " float64", "time.Duration"))
+ }
+
+ return time.Duration(v), nil
+ case string:
+ if !strings.ContainsAny(s, "nsuµmh") {
+ return time.ParseDuration(s + "ns")
+ }
+
+ return time.ParseDuration(s)
+ case nil:
+ return time.Duration(0), nil
+ default:
+ if i, ok := resolveAlias(i); ok {
+ return ToDurationE(i)
+ }
+
+ return 0, fmt.Errorf(errorMsg, i, i, time.Duration(0))
+ }
+}
+
+// StringToDate attempts to parse a string into a [time.Time] type using a
+// predefined list of formats.
+//
+// If no suitable format is found, an error is returned.
+func StringToDate(s string) (time.Time, error) {
+ return internal.ParseDateWith(s, time.UTC, internal.TimeFormats)
+}
+
+// StringToDateInDefaultLocation casts an empty interface to a [time.Time],
+// interpreting inputs without a timezone to be in the given location,
+// or the local timezone if nil.
+func StringToDateInDefaultLocation(s string, location *time.Location) (time.Time, error) {
+ return internal.ParseDateWith(s, location, internal.TimeFormats)
+}
diff --git a/vendor/github.com/spf13/cast/timeformattype_string.go b/vendor/github.com/spf13/cast/timeformattype_string.go
deleted file mode 100644
index 1524fc82c..000000000
--- a/vendor/github.com/spf13/cast/timeformattype_string.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// Code generated by "stringer -type timeFormatType"; DO NOT EDIT.
-
-package cast
-
-import "strconv"
-
-func _() {
- // An "invalid array index" compiler error signifies that the constant values have changed.
- // Re-run the stringer command to generate them again.
- var x [1]struct{}
- _ = x[timeFormatNoTimezone-0]
- _ = x[timeFormatNamedTimezone-1]
- _ = x[timeFormatNumericTimezone-2]
- _ = x[timeFormatNumericAndNamedTimezone-3]
- _ = x[timeFormatTimeOnly-4]
-}
-
-const _timeFormatType_name = "timeFormatNoTimezonetimeFormatNamedTimezonetimeFormatNumericTimezonetimeFormatNumericAndNamedTimezonetimeFormatTimeOnly"
-
-var _timeFormatType_index = [...]uint8{0, 20, 43, 68, 101, 119}
-
-func (i timeFormatType) String() string {
- if i < 0 || i >= timeFormatType(len(_timeFormatType_index)-1) {
- return "timeFormatType(" + strconv.FormatInt(int64(i), 10) + ")"
- }
- return _timeFormatType_name[_timeFormatType_index[i]:_timeFormatType_index[i+1]]
-}
diff --git a/vendor/github.com/spf13/cast/zz_generated.go b/vendor/github.com/spf13/cast/zz_generated.go
new file mode 100644
index 000000000..ce3ec0f78
--- /dev/null
+++ b/vendor/github.com/spf13/cast/zz_generated.go
@@ -0,0 +1,261 @@
+// Code generated by cast generator. DO NOT EDIT.
+
+package cast
+
+import "time"
+
+// ToBool casts any value to a(n) bool type.
+func ToBool(i any) bool {
+ v, _ := ToBoolE(i)
+ return v
+}
+
+// ToString casts any value to a(n) string type.
+func ToString(i any) string {
+ v, _ := ToStringE(i)
+ return v
+}
+
+// ToTime casts any value to a(n) time.Time type.
+func ToTime(i any) time.Time {
+ v, _ := ToTimeE(i)
+ return v
+}
+
+// ToTimeInDefaultLocation casts any value to a(n) time.Time type.
+func ToTimeInDefaultLocation(i any, location *time.Location) time.Time {
+ v, _ := ToTimeInDefaultLocationE(i, location)
+ return v
+}
+
+// ToDuration casts any value to a(n) time.Duration type.
+func ToDuration(i any) time.Duration {
+ v, _ := ToDurationE(i)
+ return v
+}
+
+// ToInt casts any value to a(n) int type.
+func ToInt(i any) int {
+ v, _ := ToIntE(i)
+ return v
+}
+
+// ToInt8 casts any value to a(n) int8 type.
+func ToInt8(i any) int8 {
+ v, _ := ToInt8E(i)
+ return v
+}
+
+// ToInt16 casts any value to a(n) int16 type.
+func ToInt16(i any) int16 {
+ v, _ := ToInt16E(i)
+ return v
+}
+
+// ToInt32 casts any value to a(n) int32 type.
+func ToInt32(i any) int32 {
+ v, _ := ToInt32E(i)
+ return v
+}
+
+// ToInt64 casts any value to a(n) int64 type.
+func ToInt64(i any) int64 {
+ v, _ := ToInt64E(i)
+ return v
+}
+
+// ToUint casts any value to a(n) uint type.
+func ToUint(i any) uint {
+ v, _ := ToUintE(i)
+ return v
+}
+
+// ToUint8 casts any value to a(n) uint8 type.
+func ToUint8(i any) uint8 {
+ v, _ := ToUint8E(i)
+ return v
+}
+
+// ToUint16 casts any value to a(n) uint16 type.
+func ToUint16(i any) uint16 {
+ v, _ := ToUint16E(i)
+ return v
+}
+
+// ToUint32 casts any value to a(n) uint32 type.
+func ToUint32(i any) uint32 {
+ v, _ := ToUint32E(i)
+ return v
+}
+
+// ToUint64 casts any value to a(n) uint64 type.
+func ToUint64(i any) uint64 {
+ v, _ := ToUint64E(i)
+ return v
+}
+
+// ToFloat32 casts any value to a(n) float32 type.
+func ToFloat32(i any) float32 {
+ v, _ := ToFloat32E(i)
+ return v
+}
+
+// ToFloat64 casts any value to a(n) float64 type.
+func ToFloat64(i any) float64 {
+ v, _ := ToFloat64E(i)
+ return v
+}
+
+// ToStringMapString casts any value to a(n) map[string]string type.
+func ToStringMapString(i any) map[string]string {
+ v, _ := ToStringMapStringE(i)
+ return v
+}
+
+// ToStringMapStringSlice casts any value to a(n) map[string][]string type.
+func ToStringMapStringSlice(i any) map[string][]string {
+ v, _ := ToStringMapStringSliceE(i)
+ return v
+}
+
+// ToStringMapBool casts any value to a(n) map[string]bool type.
+func ToStringMapBool(i any) map[string]bool {
+ v, _ := ToStringMapBoolE(i)
+ return v
+}
+
+// ToStringMapInt casts any value to a(n) map[string]int type.
+func ToStringMapInt(i any) map[string]int {
+ v, _ := ToStringMapIntE(i)
+ return v
+}
+
+// ToStringMapInt64 casts any value to a(n) map[string]int64 type.
+func ToStringMapInt64(i any) map[string]int64 {
+ v, _ := ToStringMapInt64E(i)
+ return v
+}
+
+// ToStringMap casts any value to a(n) map[string]any type.
+func ToStringMap(i any) map[string]any {
+ v, _ := ToStringMapE(i)
+ return v
+}
+
+// ToSlice casts any value to a(n) []any type.
+func ToSlice(i any) []any {
+ v, _ := ToSliceE(i)
+ return v
+}
+
+// ToBoolSlice casts any value to a(n) []bool type.
+func ToBoolSlice(i any) []bool {
+ v, _ := ToBoolSliceE(i)
+ return v
+}
+
+// ToStringSlice casts any value to a(n) []string type.
+func ToStringSlice(i any) []string {
+ v, _ := ToStringSliceE(i)
+ return v
+}
+
+// ToIntSlice casts any value to a(n) []int type.
+func ToIntSlice(i any) []int {
+ v, _ := ToIntSliceE(i)
+ return v
+}
+
+// ToInt64Slice casts any value to a(n) []int64 type.
+func ToInt64Slice(i any) []int64 {
+ v, _ := ToInt64SliceE(i)
+ return v
+}
+
+// ToUintSlice casts any value to a(n) []uint type.
+func ToUintSlice(i any) []uint {
+ v, _ := ToUintSliceE(i)
+ return v
+}
+
+// ToFloat64Slice casts any value to a(n) []float64 type.
+func ToFloat64Slice(i any) []float64 {
+ v, _ := ToFloat64SliceE(i)
+ return v
+}
+
+// ToDurationSlice casts any value to a(n) []time.Duration type.
+func ToDurationSlice(i any) []time.Duration {
+ v, _ := ToDurationSliceE(i)
+ return v
+}
+
+// ToBoolSliceE casts any value to a(n) []bool type.
+func ToBoolSliceE(i any) ([]bool, error) {
+ return toSliceE[bool](i)
+}
+
+// ToDurationSliceE casts any value to a(n) []time.Duration type.
+func ToDurationSliceE(i any) ([]time.Duration, error) {
+ return toSliceE[time.Duration](i)
+}
+
+// ToIntSliceE casts any value to a(n) []int type.
+func ToIntSliceE(i any) ([]int, error) {
+ return toSliceE[int](i)
+}
+
+// ToInt8SliceE casts any value to a(n) []int8 type.
+func ToInt8SliceE(i any) ([]int8, error) {
+ return toSliceE[int8](i)
+}
+
+// ToInt16SliceE casts any value to a(n) []int16 type.
+func ToInt16SliceE(i any) ([]int16, error) {
+ return toSliceE[int16](i)
+}
+
+// ToInt32SliceE casts any value to a(n) []int32 type.
+func ToInt32SliceE(i any) ([]int32, error) {
+ return toSliceE[int32](i)
+}
+
+// ToInt64SliceE casts any value to a(n) []int64 type.
+func ToInt64SliceE(i any) ([]int64, error) {
+ return toSliceE[int64](i)
+}
+
+// ToUintSliceE casts any value to a(n) []uint type.
+func ToUintSliceE(i any) ([]uint, error) {
+ return toSliceE[uint](i)
+}
+
+// ToUint8SliceE casts any value to a(n) []uint8 type.
+func ToUint8SliceE(i any) ([]uint8, error) {
+ return toSliceE[uint8](i)
+}
+
+// ToUint16SliceE casts any value to a(n) []uint16 type.
+func ToUint16SliceE(i any) ([]uint16, error) {
+ return toSliceE[uint16](i)
+}
+
+// ToUint32SliceE casts any value to a(n) []uint32 type.
+func ToUint32SliceE(i any) ([]uint32, error) {
+ return toSliceE[uint32](i)
+}
+
+// ToUint64SliceE casts any value to a(n) []uint64 type.
+func ToUint64SliceE(i any) ([]uint64, error) {
+ return toSliceE[uint64](i)
+}
+
+// ToFloat32SliceE casts any value to a(n) []float32 type.
+func ToFloat32SliceE(i any) ([]float32, error) {
+ return toSliceE[float32](i)
+}
+
+// ToFloat64SliceE casts any value to a(n) []float64 type.
+func ToFloat64SliceE(i any) ([]float64, error) {
+ return toSliceE[float64](i)
+}
diff --git a/vendor/github.com/spf13/pflag/README.md b/vendor/github.com/spf13/pflag/README.md
index 7eacc5bdb..388c4e5ea 100644
--- a/vendor/github.com/spf13/pflag/README.md
+++ b/vendor/github.com/spf13/pflag/README.md
@@ -284,6 +284,33 @@ func main() {
}
```
+### Using pflag with go test
+`pflag` does not parse the shorthand versions of go test's built-in flags (i.e., those starting with `-test.`).
+For more context, see issues [#63](https://github.com/spf13/pflag/issues/63) and [#238](https://github.com/spf13/pflag/issues/238) for more details.
+
+For example, if you use pflag in your `TestMain` function and call `pflag.Parse()` after defining your custom flags, running a test like this:
+```bash
+go test /your/tests -run ^YourTest -v --your-test-pflags
+```
+will result in the `-v` flag being ignored. This happens because of the way pflag handles flag parsing, skipping over go test's built-in shorthand flags.
+To work around this, you can use the `ParseSkippedFlags` function, which ensures that go test's flags are parsed separately using the standard flag package.
+
+**Example**: You want to parse go test flags that are otherwise ignore by `pflag.Parse()`
+```go
+import (
+ goflag "flag"
+ flag "github.com/spf13/pflag"
+)
+
+var ip *int = flag.Int("flagname", 1234, "help message for flagname")
+
+func main() {
+ flag.CommandLine.AddGoFlagSet(goflag.CommandLine)
+ flag.ParseSkippedFlags(os.Args[1:], goflag.CommandLine)
+ flag.Parse()
+}
+```
+
## More info
You can see the full reference documentation of the pflag package
diff --git a/vendor/github.com/spf13/pflag/bool_func.go b/vendor/github.com/spf13/pflag/bool_func.go
new file mode 100644
index 000000000..83d77afa8
--- /dev/null
+++ b/vendor/github.com/spf13/pflag/bool_func.go
@@ -0,0 +1,40 @@
+package pflag
+
+// -- func Value
+type boolfuncValue func(string) error
+
+func (f boolfuncValue) Set(s string) error { return f(s) }
+
+func (f boolfuncValue) Type() string { return "boolfunc" }
+
+func (f boolfuncValue) String() string { return "" } // same behavior as stdlib 'flag' package
+
+func (f boolfuncValue) IsBoolFlag() bool { return true }
+
+// BoolFunc defines a func flag with specified name, callback function and usage string.
+//
+// The callback function will be called every time "--{name}" (or any form that matches the flag) is parsed
+// on the command line.
+func (f *FlagSet) BoolFunc(name string, usage string, fn func(string) error) {
+ f.BoolFuncP(name, "", usage, fn)
+}
+
+// BoolFuncP is like BoolFunc, but accepts a shorthand letter that can be used after a single dash.
+func (f *FlagSet) BoolFuncP(name, shorthand string, usage string, fn func(string) error) {
+ var val Value = boolfuncValue(fn)
+ flag := f.VarPF(val, name, shorthand, usage)
+ flag.NoOptDefVal = "true"
+}
+
+// BoolFunc defines a func flag with specified name, callback function and usage string.
+//
+// The callback function will be called every time "--{name}" (or any form that matches the flag) is parsed
+// on the command line.
+func BoolFunc(name string, usage string, fn func(string) error) {
+ CommandLine.BoolFuncP(name, "", usage, fn)
+}
+
+// BoolFuncP is like BoolFunc, but accepts a shorthand letter that can be used after a single dash.
+func BoolFuncP(name, shorthand string, usage string, fn func(string) error) {
+ CommandLine.BoolFuncP(name, shorthand, usage, fn)
+}
diff --git a/vendor/github.com/spf13/pflag/count.go b/vendor/github.com/spf13/pflag/count.go
index a0b2679f7..d49c0143c 100644
--- a/vendor/github.com/spf13/pflag/count.go
+++ b/vendor/github.com/spf13/pflag/count.go
@@ -85,7 +85,7 @@ func (f *FlagSet) CountP(name, shorthand string, usage string) *int {
// Count defines a count flag with specified name, default value, and usage string.
// The return value is the address of an int variable that stores the value of the flag.
-// A count flag will add 1 to its value evey time it is found on the command line
+// A count flag will add 1 to its value every time it is found on the command line
func Count(name string, usage string) *int {
return CommandLine.CountP(name, "", usage)
}
diff --git a/vendor/github.com/spf13/pflag/errors.go b/vendor/github.com/spf13/pflag/errors.go
new file mode 100644
index 000000000..ff11b66be
--- /dev/null
+++ b/vendor/github.com/spf13/pflag/errors.go
@@ -0,0 +1,149 @@
+package pflag
+
+import "fmt"
+
+// notExistErrorMessageType specifies which flavor of "flag does not exist"
+// is printed by NotExistError. This allows the related errors to be grouped
+// under a single NotExistError struct without making a breaking change to
+// the error message text.
+type notExistErrorMessageType int
+
+const (
+ flagNotExistMessage notExistErrorMessageType = iota
+ flagNotDefinedMessage
+ flagNoSuchFlagMessage
+ flagUnknownFlagMessage
+ flagUnknownShorthandFlagMessage
+)
+
+// NotExistError is the error returned when trying to access a flag that
+// does not exist in the FlagSet.
+type NotExistError struct {
+ name string
+ specifiedShorthands string
+ messageType notExistErrorMessageType
+}
+
+// Error implements error.
+func (e *NotExistError) Error() string {
+ switch e.messageType {
+ case flagNotExistMessage:
+ return fmt.Sprintf("flag %q does not exist", e.name)
+
+ case flagNotDefinedMessage:
+ return fmt.Sprintf("flag accessed but not defined: %s", e.name)
+
+ case flagNoSuchFlagMessage:
+ return fmt.Sprintf("no such flag -%v", e.name)
+
+ case flagUnknownFlagMessage:
+ return fmt.Sprintf("unknown flag: --%s", e.name)
+
+ case flagUnknownShorthandFlagMessage:
+ c := rune(e.name[0])
+ return fmt.Sprintf("unknown shorthand flag: %q in -%s", c, e.specifiedShorthands)
+ }
+
+ panic(fmt.Errorf("unknown flagNotExistErrorMessageType: %v", e.messageType))
+}
+
+// GetSpecifiedName returns the name of the flag (without dashes) as it
+// appeared in the parsed arguments.
+func (e *NotExistError) GetSpecifiedName() string {
+ return e.name
+}
+
+// GetSpecifiedShortnames returns the group of shorthand arguments
+// (without dashes) that the flag appeared within. If the flag was not in a
+// shorthand group, this will return an empty string.
+func (e *NotExistError) GetSpecifiedShortnames() string {
+ return e.specifiedShorthands
+}
+
+// ValueRequiredError is the error returned when a flag needs an argument but
+// no argument was provided.
+type ValueRequiredError struct {
+ flag *Flag
+ specifiedName string
+ specifiedShorthands string
+}
+
+// Error implements error.
+func (e *ValueRequiredError) Error() string {
+ if len(e.specifiedShorthands) > 0 {
+ c := rune(e.specifiedName[0])
+ return fmt.Sprintf("flag needs an argument: %q in -%s", c, e.specifiedShorthands)
+ }
+
+ return fmt.Sprintf("flag needs an argument: --%s", e.specifiedName)
+}
+
+// GetFlag returns the flag for which the error occurred.
+func (e *ValueRequiredError) GetFlag() *Flag {
+ return e.flag
+}
+
+// GetSpecifiedName returns the name of the flag (without dashes) as it
+// appeared in the parsed arguments.
+func (e *ValueRequiredError) GetSpecifiedName() string {
+ return e.specifiedName
+}
+
+// GetSpecifiedShortnames returns the group of shorthand arguments
+// (without dashes) that the flag appeared within. If the flag was not in a
+// shorthand group, this will return an empty string.
+func (e *ValueRequiredError) GetSpecifiedShortnames() string {
+ return e.specifiedShorthands
+}
+
+// InvalidValueError is the error returned when an invalid value is used
+// for a flag.
+type InvalidValueError struct {
+ flag *Flag
+ value string
+ cause error
+}
+
+// Error implements error.
+func (e *InvalidValueError) Error() string {
+ flag := e.flag
+ var flagName string
+ if flag.Shorthand != "" && flag.ShorthandDeprecated == "" {
+ flagName = fmt.Sprintf("-%s, --%s", flag.Shorthand, flag.Name)
+ } else {
+ flagName = fmt.Sprintf("--%s", flag.Name)
+ }
+ return fmt.Sprintf("invalid argument %q for %q flag: %v", e.value, flagName, e.cause)
+}
+
+// Unwrap implements errors.Unwrap.
+func (e *InvalidValueError) Unwrap() error {
+ return e.cause
+}
+
+// GetFlag returns the flag for which the error occurred.
+func (e *InvalidValueError) GetFlag() *Flag {
+ return e.flag
+}
+
+// GetValue returns the invalid value that was provided.
+func (e *InvalidValueError) GetValue() string {
+ return e.value
+}
+
+// InvalidSyntaxError is the error returned when a bad flag name is passed on
+// the command line.
+type InvalidSyntaxError struct {
+ specifiedFlag string
+}
+
+// Error implements error.
+func (e *InvalidSyntaxError) Error() string {
+ return fmt.Sprintf("bad flag syntax: %s", e.specifiedFlag)
+}
+
+// GetSpecifiedName returns the exact flag (with dashes) as it
+// appeared in the parsed arguments.
+func (e *InvalidSyntaxError) GetSpecifiedFlag() string {
+ return e.specifiedFlag
+}
diff --git a/vendor/github.com/spf13/pflag/flag.go b/vendor/github.com/spf13/pflag/flag.go
index 7c058de37..2fd3c5759 100644
--- a/vendor/github.com/spf13/pflag/flag.go
+++ b/vendor/github.com/spf13/pflag/flag.go
@@ -27,23 +27,32 @@ unaffected.
Define flags using flag.String(), Bool(), Int(), etc.
This declares an integer flag, -flagname, stored in the pointer ip, with type *int.
+
var ip = flag.Int("flagname", 1234, "help message for flagname")
+
If you like, you can bind the flag to a variable using the Var() functions.
+
var flagvar int
func init() {
flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")
}
+
Or you can create custom flags that satisfy the Value interface (with
pointer receivers) and couple them to flag parsing by
+
flag.Var(&flagVal, "name", "help message for flagname")
+
For such flags, the default value is just the initial value of the variable.
After all flags are defined, call
+
flag.Parse()
+
to parse the command line into the defined flags.
Flags may then be used directly. If you're using the flags themselves,
they are all pointers; if you bind to variables, they're values.
+
fmt.Println("ip has value ", *ip)
fmt.Println("flagvar has value ", flagvar)
@@ -54,22 +63,26 @@ The arguments are indexed from 0 through flag.NArg()-1.
The pflag package also defines some new functions that are not in flag,
that give one-letter shorthands for flags. You can use these by appending
'P' to the name of any function that defines a flag.
+
var ip = flag.IntP("flagname", "f", 1234, "help message")
var flagvar bool
func init() {
flag.BoolVarP(&flagvar, "boolname", "b", true, "help message")
}
flag.VarP(&flagval, "varname", "v", "help message")
+
Shorthand letters can be used with single dashes on the command line.
Boolean shorthand flags can be combined with other shorthand flags.
Command line flag syntax:
+
--flag // boolean flags only
--flag=x
Unlike the flag package, a single dash before an option means something
different than a double dash. Single dashes signify a series of shorthand
letters for flags. All but the last shorthand letter must be boolean flags.
+
// boolean flags
-f
-abc
@@ -124,12 +137,17 @@ const (
PanicOnError
)
-// ParseErrorsWhitelist defines the parsing errors that can be ignored
-type ParseErrorsWhitelist struct {
+// ParseErrorsAllowlist defines the parsing errors that can be ignored
+type ParseErrorsAllowlist struct {
// UnknownFlags will ignore unknown flags errors and continue parsing rest of the flags
UnknownFlags bool
}
+// ParseErrorsWhitelist defines the parsing errors that can be ignored.
+//
+// Deprecated: use [ParseErrorsAllowlist] instead. This type will be removed in a future release.
+type ParseErrorsWhitelist = ParseErrorsAllowlist
+
// NormalizedName is a flag name that has been normalized according to rules
// for the FlagSet (e.g. making '-' and '_' equivalent).
type NormalizedName string
@@ -145,8 +163,13 @@ type FlagSet struct {
// help/usage messages.
SortFlags bool
- // ParseErrorsWhitelist is used to configure a whitelist of errors
- ParseErrorsWhitelist ParseErrorsWhitelist
+ // ParseErrorsAllowlist is used to configure an allowlist of errors
+ ParseErrorsAllowlist ParseErrorsAllowlist
+
+ // ParseErrorsAllowlist is used to configure an allowlist of errors.
+ //
+ // Deprecated: use [FlagSet.ParseErrorsAllowlist] instead. This field will be removed in a future release.
+ ParseErrorsWhitelist ParseErrorsAllowlist
name string
parsed bool
@@ -381,7 +404,7 @@ func (f *FlagSet) lookup(name NormalizedName) *Flag {
func (f *FlagSet) getFlagType(name string, ftype string, convFunc func(sval string) (interface{}, error)) (interface{}, error) {
flag := f.Lookup(name)
if flag == nil {
- err := fmt.Errorf("flag accessed but not defined: %s", name)
+ err := &NotExistError{name: name, messageType: flagNotDefinedMessage}
return nil, err
}
@@ -411,7 +434,7 @@ func (f *FlagSet) ArgsLenAtDash() int {
func (f *FlagSet) MarkDeprecated(name string, usageMessage string) error {
flag := f.Lookup(name)
if flag == nil {
- return fmt.Errorf("flag %q does not exist", name)
+ return &NotExistError{name: name, messageType: flagNotExistMessage}
}
if usageMessage == "" {
return fmt.Errorf("deprecated message for flag %q must be set", name)
@@ -427,7 +450,7 @@ func (f *FlagSet) MarkDeprecated(name string, usageMessage string) error {
func (f *FlagSet) MarkShorthandDeprecated(name string, usageMessage string) error {
flag := f.Lookup(name)
if flag == nil {
- return fmt.Errorf("flag %q does not exist", name)
+ return &NotExistError{name: name, messageType: flagNotExistMessage}
}
if usageMessage == "" {
return fmt.Errorf("deprecated message for flag %q must be set", name)
@@ -441,7 +464,7 @@ func (f *FlagSet) MarkShorthandDeprecated(name string, usageMessage string) erro
func (f *FlagSet) MarkHidden(name string) error {
flag := f.Lookup(name)
if flag == nil {
- return fmt.Errorf("flag %q does not exist", name)
+ return &NotExistError{name: name, messageType: flagNotExistMessage}
}
flag.Hidden = true
return nil
@@ -464,18 +487,16 @@ func (f *FlagSet) Set(name, value string) error {
normalName := f.normalizeFlagName(name)
flag, ok := f.formal[normalName]
if !ok {
- return fmt.Errorf("no such flag -%v", name)
+ return &NotExistError{name: name, messageType: flagNoSuchFlagMessage}
}
err := flag.Value.Set(value)
if err != nil {
- var flagName string
- if flag.Shorthand != "" && flag.ShorthandDeprecated == "" {
- flagName = fmt.Sprintf("-%s, --%s", flag.Shorthand, flag.Name)
- } else {
- flagName = fmt.Sprintf("--%s", flag.Name)
+ return &InvalidValueError{
+ flag: flag,
+ value: value,
+ cause: err,
}
- return fmt.Errorf("invalid argument %q for %q flag: %v", value, flagName, err)
}
if !flag.Changed {
@@ -501,7 +522,7 @@ func (f *FlagSet) SetAnnotation(name, key string, values []string) error {
normalName := f.normalizeFlagName(name)
flag, ok := f.formal[normalName]
if !ok {
- return fmt.Errorf("no such flag -%v", name)
+ return &NotExistError{name: name, messageType: flagNoSuchFlagMessage}
}
if flag.Annotations == nil {
flag.Annotations = map[string][]string{}
@@ -538,7 +559,7 @@ func (f *FlagSet) PrintDefaults() {
func (f *Flag) defaultIsZeroValue() bool {
switch f.Value.(type) {
case boolFlag:
- return f.DefValue == "false"
+ return f.DefValue == "false" || f.DefValue == ""
case *durationValue:
// Beginning in Go 1.7, duration zero values are "0s"
return f.DefValue == "0" || f.DefValue == "0s"
@@ -551,7 +572,7 @@ func (f *Flag) defaultIsZeroValue() bool {
case *intSliceValue, *stringSliceValue, *stringArrayValue:
return f.DefValue == "[]"
default:
- switch f.Value.String() {
+ switch f.DefValue {
case "false":
return true
case "":
@@ -588,8 +609,10 @@ func UnquoteUsage(flag *Flag) (name string, usage string) {
name = flag.Value.Type()
switch name {
- case "bool":
+ case "bool", "boolfunc":
name = ""
+ case "func":
+ name = "value"
case "float64":
name = "float"
case "int64":
@@ -707,7 +730,7 @@ func (f *FlagSet) FlagUsagesWrapped(cols int) string {
switch flag.Value.Type() {
case "string":
line += fmt.Sprintf("[=\"%s\"]", flag.NoOptDefVal)
- case "bool":
+ case "bool", "boolfunc":
if flag.NoOptDefVal != "true" {
line += fmt.Sprintf("[=%s]", flag.NoOptDefVal)
}
@@ -911,12 +934,10 @@ func VarP(value Value, name, shorthand, usage string) {
CommandLine.VarP(value, name, shorthand, usage)
}
-// failf prints to standard error a formatted error and usage message and
+// fail prints an error message and usage message to standard error and
// returns the error.
-func (f *FlagSet) failf(format string, a ...interface{}) error {
- err := fmt.Errorf(format, a...)
+func (f *FlagSet) fail(err error) error {
if f.errorHandling != ContinueOnError {
- fmt.Fprintln(f.Output(), err)
f.usage()
}
return err
@@ -934,9 +955,9 @@ func (f *FlagSet) usage() {
}
}
-//--unknown (args will be empty)
-//--unknown --next-flag ... (args will be --next-flag ...)
-//--unknown arg ... (args will be arg ...)
+// --unknown (args will be empty)
+// --unknown --next-flag ... (args will be --next-flag ...)
+// --unknown arg ... (args will be arg ...)
func stripUnknownFlagValue(args []string) []string {
if len(args) == 0 {
//--unknown
@@ -960,7 +981,7 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin
a = args
name := s[2:]
if len(name) == 0 || name[0] == '-' || name[0] == '=' {
- err = f.failf("bad flag syntax: %s", s)
+ err = f.fail(&InvalidSyntaxError{specifiedFlag: s})
return
}
@@ -974,6 +995,8 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin
f.usage()
return a, ErrHelp
case f.ParseErrorsWhitelist.UnknownFlags:
+ fallthrough
+ case f.ParseErrorsAllowlist.UnknownFlags:
// --unknown=unknownval arg ...
// we do not want to lose arg in this case
if len(split) >= 2 {
@@ -982,7 +1005,7 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin
return stripUnknownFlagValue(a), nil
default:
- err = f.failf("unknown flag: --%s", name)
+ err = f.fail(&NotExistError{name: name, messageType: flagUnknownFlagMessage})
return
}
}
@@ -1000,13 +1023,16 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin
a = a[1:]
} else {
// '--flag' (arg was required)
- err = f.failf("flag needs an argument: %s", s)
+ err = f.fail(&ValueRequiredError{
+ flag: flag,
+ specifiedName: name,
+ })
return
}
err = fn(flag, value)
if err != nil {
- f.failf(err.Error())
+ f.fail(err)
}
return
}
@@ -1014,7 +1040,7 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin
func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parseFunc) (outShorts string, outArgs []string, err error) {
outArgs = args
- if strings.HasPrefix(shorthands, "test.") {
+ if isGotestShorthandFlag(shorthands) {
return
}
@@ -1029,6 +1055,8 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse
err = ErrHelp
return
case f.ParseErrorsWhitelist.UnknownFlags:
+ fallthrough
+ case f.ParseErrorsAllowlist.UnknownFlags:
// '-f=arg arg ...'
// we do not want to lose arg in this case
if len(shorthands) > 2 && shorthands[1] == '=' {
@@ -1039,7 +1067,11 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse
outArgs = stripUnknownFlagValue(outArgs)
return
default:
- err = f.failf("unknown shorthand flag: %q in -%s", c, shorthands)
+ err = f.fail(&NotExistError{
+ name: string(c),
+ specifiedShorthands: shorthands,
+ messageType: flagUnknownShorthandFlagMessage,
+ })
return
}
}
@@ -1062,7 +1094,11 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse
outArgs = args[1:]
} else {
// '-f' (arg was required)
- err = f.failf("flag needs an argument: %q in -%s", c, shorthands)
+ err = f.fail(&ValueRequiredError{
+ flag: flag,
+ specifiedName: string(c),
+ specifiedShorthands: shorthands,
+ })
return
}
@@ -1072,7 +1108,7 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse
err = fn(flag, value)
if err != nil {
- f.failf(err.Error())
+ f.fail(err)
}
return
}
@@ -1135,12 +1171,12 @@ func (f *FlagSet) Parse(arguments []string) error {
}
f.parsed = true
- if len(arguments) < 0 {
+ f.args = make([]string, 0, len(arguments))
+
+ if len(arguments) == 0 {
return nil
}
- f.args = make([]string, 0, len(arguments))
-
set := func(flag *Flag, value string) error {
return f.Set(flag.Name, value)
}
@@ -1151,7 +1187,10 @@ func (f *FlagSet) Parse(arguments []string) error {
case ContinueOnError:
return err
case ExitOnError:
- fmt.Println(err)
+ if err == ErrHelp {
+ os.Exit(0)
+ }
+ fmt.Fprintln(f.Output(), err)
os.Exit(2)
case PanicOnError:
panic(err)
@@ -1177,6 +1216,10 @@ func (f *FlagSet) ParseAll(arguments []string, fn func(flag *Flag, value string)
case ContinueOnError:
return err
case ExitOnError:
+ if err == ErrHelp {
+ os.Exit(0)
+ }
+ fmt.Fprintln(f.Output(), err)
os.Exit(2)
case PanicOnError:
panic(err)
diff --git a/vendor/github.com/spf13/pflag/func.go b/vendor/github.com/spf13/pflag/func.go
new file mode 100644
index 000000000..9f4d88f27
--- /dev/null
+++ b/vendor/github.com/spf13/pflag/func.go
@@ -0,0 +1,37 @@
+package pflag
+
+// -- func Value
+type funcValue func(string) error
+
+func (f funcValue) Set(s string) error { return f(s) }
+
+func (f funcValue) Type() string { return "func" }
+
+func (f funcValue) String() string { return "" } // same behavior as stdlib 'flag' package
+
+// Func defines a func flag with specified name, callback function and usage string.
+//
+// The callback function will be called every time "--{name}={value}" (or equivalent) is
+// parsed on the command line, with "{value}" as an argument.
+func (f *FlagSet) Func(name string, usage string, fn func(string) error) {
+ f.FuncP(name, "", usage, fn)
+}
+
+// FuncP is like Func, but accepts a shorthand letter that can be used after a single dash.
+func (f *FlagSet) FuncP(name string, shorthand string, usage string, fn func(string) error) {
+ var val Value = funcValue(fn)
+ f.VarP(val, name, shorthand, usage)
+}
+
+// Func defines a func flag with specified name, callback function and usage string.
+//
+// The callback function will be called every time "--{name}={value}" (or equivalent) is
+// parsed on the command line, with "{value}" as an argument.
+func Func(name string, usage string, fn func(string) error) {
+ CommandLine.FuncP(name, "", usage, fn)
+}
+
+// FuncP is like Func, but accepts a shorthand letter that can be used after a single dash.
+func FuncP(name, shorthand string, usage string, fn func(string) error) {
+ CommandLine.FuncP(name, shorthand, usage, fn)
+}
diff --git a/vendor/github.com/spf13/pflag/golangflag.go b/vendor/github.com/spf13/pflag/golangflag.go
index d3dd72b7f..e62eab538 100644
--- a/vendor/github.com/spf13/pflag/golangflag.go
+++ b/vendor/github.com/spf13/pflag/golangflag.go
@@ -8,8 +8,18 @@ import (
goflag "flag"
"reflect"
"strings"
+ "time"
)
+// go test flags prefixes
+func isGotestFlag(flag string) bool {
+ return strings.HasPrefix(flag, "-test.")
+}
+
+func isGotestShorthandFlag(flag string) bool {
+ return strings.HasPrefix(flag, "test.")
+}
+
// flagValueWrapper implements pflag.Value around a flag.Value. The main
// difference here is the addition of the Type method that returns a string
// name of the type. As this is generally unknown, we approximate that with
@@ -103,3 +113,49 @@ func (f *FlagSet) AddGoFlagSet(newSet *goflag.FlagSet) {
}
f.addedGoFlagSets = append(f.addedGoFlagSets, newSet)
}
+
+// CopyToGoFlagSet will add all current flags to the given Go flag set.
+// Deprecation remarks get copied into the usage description.
+// Whenever possible, a flag gets added for which Go flags shows
+// a proper type in the help message.
+func (f *FlagSet) CopyToGoFlagSet(newSet *goflag.FlagSet) {
+ f.VisitAll(func(flag *Flag) {
+ usage := flag.Usage
+ if flag.Deprecated != "" {
+ usage += " (DEPRECATED: " + flag.Deprecated + ")"
+ }
+
+ switch value := flag.Value.(type) {
+ case *stringValue:
+ newSet.StringVar((*string)(value), flag.Name, flag.DefValue, usage)
+ case *intValue:
+ newSet.IntVar((*int)(value), flag.Name, *(*int)(value), usage)
+ case *int64Value:
+ newSet.Int64Var((*int64)(value), flag.Name, *(*int64)(value), usage)
+ case *uintValue:
+ newSet.UintVar((*uint)(value), flag.Name, *(*uint)(value), usage)
+ case *uint64Value:
+ newSet.Uint64Var((*uint64)(value), flag.Name, *(*uint64)(value), usage)
+ case *durationValue:
+ newSet.DurationVar((*time.Duration)(value), flag.Name, *(*time.Duration)(value), usage)
+ case *float64Value:
+ newSet.Float64Var((*float64)(value), flag.Name, *(*float64)(value), usage)
+ default:
+ newSet.Var(flag.Value, flag.Name, usage)
+ }
+ })
+}
+
+// ParseSkippedFlags explicitly Parses go test flags (i.e. the one starting with '-test.') with goflag.Parse(),
+// since by default those are skipped by pflag.Parse().
+// Typical usage example: `ParseGoTestFlags(os.Args[1:], goflag.CommandLine)`
+func ParseSkippedFlags(osArgs []string, goFlagSet *goflag.FlagSet) error {
+ var skippedFlags []string
+ for _, f := range osArgs {
+ if isGotestFlag(f) {
+ skippedFlags = append(skippedFlags, f)
+ }
+ }
+ return goFlagSet.Parse(skippedFlags)
+}
+
diff --git a/vendor/github.com/spf13/pflag/ipnet_slice.go b/vendor/github.com/spf13/pflag/ipnet_slice.go
index 6b541aa87..c6e89da18 100644
--- a/vendor/github.com/spf13/pflag/ipnet_slice.go
+++ b/vendor/github.com/spf13/pflag/ipnet_slice.go
@@ -73,7 +73,7 @@ func (s *ipNetSliceValue) String() string {
func ipNetSliceConv(val string) (interface{}, error) {
val = strings.Trim(val, "[]")
- // Emtpy string would cause a slice with one (empty) entry
+ // Empty string would cause a slice with one (empty) entry
if len(val) == 0 {
return []net.IPNet{}, nil
}
diff --git a/vendor/github.com/spf13/pflag/string_to_string.go b/vendor/github.com/spf13/pflag/string_to_string.go
index 890a01afc..1d1e3bf91 100644
--- a/vendor/github.com/spf13/pflag/string_to_string.go
+++ b/vendor/github.com/spf13/pflag/string_to_string.go
@@ -4,6 +4,7 @@ import (
"bytes"
"encoding/csv"
"fmt"
+ "sort"
"strings"
)
@@ -62,8 +63,15 @@ func (s *stringToStringValue) Type() string {
}
func (s *stringToStringValue) String() string {
+ keys := make([]string, 0, len(*s.value))
+ for k := range *s.value {
+ keys = append(keys, k)
+ }
+ sort.Strings(keys)
+
records := make([]string, 0, len(*s.value)>>1)
- for k, v := range *s.value {
+ for _, k := range keys {
+ v := (*s.value)[k]
records = append(records, k+"="+v)
}
diff --git a/vendor/github.com/spf13/pflag/text.go b/vendor/github.com/spf13/pflag/text.go
new file mode 100644
index 000000000..886d5a3d8
--- /dev/null
+++ b/vendor/github.com/spf13/pflag/text.go
@@ -0,0 +1,81 @@
+package pflag
+
+import (
+ "encoding"
+ "fmt"
+ "reflect"
+)
+
+// following is copied from go 1.23.4 flag.go
+type textValue struct{ p encoding.TextUnmarshaler }
+
+func newTextValue(val encoding.TextMarshaler, p encoding.TextUnmarshaler) textValue {
+ ptrVal := reflect.ValueOf(p)
+ if ptrVal.Kind() != reflect.Ptr {
+ panic("variable value type must be a pointer")
+ }
+ defVal := reflect.ValueOf(val)
+ if defVal.Kind() == reflect.Ptr {
+ defVal = defVal.Elem()
+ }
+ if defVal.Type() != ptrVal.Type().Elem() {
+ panic(fmt.Sprintf("default type does not match variable type: %v != %v", defVal.Type(), ptrVal.Type().Elem()))
+ }
+ ptrVal.Elem().Set(defVal)
+ return textValue{p}
+}
+
+func (v textValue) Set(s string) error {
+ return v.p.UnmarshalText([]byte(s))
+}
+
+func (v textValue) Get() interface{} {
+ return v.p
+}
+
+func (v textValue) String() string {
+ if m, ok := v.p.(encoding.TextMarshaler); ok {
+ if b, err := m.MarshalText(); err == nil {
+ return string(b)
+ }
+ }
+ return ""
+}
+
+//end of copy
+
+func (v textValue) Type() string {
+ return reflect.ValueOf(v.p).Type().Name()
+}
+
+// GetText set out, which implements encoding.UnmarshalText, to the value of a flag with given name
+func (f *FlagSet) GetText(name string, out encoding.TextUnmarshaler) error {
+ flag := f.Lookup(name)
+ if flag == nil {
+ return fmt.Errorf("flag accessed but not defined: %s", name)
+ }
+ if flag.Value.Type() != reflect.TypeOf(out).Name() {
+ return fmt.Errorf("trying to get %s value of flag of type %s", reflect.TypeOf(out).Name(), flag.Value.Type())
+ }
+ return out.UnmarshalText([]byte(flag.Value.String()))
+}
+
+// TextVar defines a flag with a specified name, default value, and usage string. The argument p must be a pointer to a variable that will hold the value of the flag, and p must implement encoding.TextUnmarshaler. If the flag is used, the flag value will be passed to p's UnmarshalText method. The type of the default value must be the same as the type of p.
+func (f *FlagSet) TextVar(p encoding.TextUnmarshaler, name string, value encoding.TextMarshaler, usage string) {
+ f.VarP(newTextValue(value, p), name, "", usage)
+}
+
+// TextVarP is like TextVar, but accepts a shorthand letter that can be used after a single dash.
+func (f *FlagSet) TextVarP(p encoding.TextUnmarshaler, name, shorthand string, value encoding.TextMarshaler, usage string) {
+ f.VarP(newTextValue(value, p), name, shorthand, usage)
+}
+
+// TextVar defines a flag with a specified name, default value, and usage string. The argument p must be a pointer to a variable that will hold the value of the flag, and p must implement encoding.TextUnmarshaler. If the flag is used, the flag value will be passed to p's UnmarshalText method. The type of the default value must be the same as the type of p.
+func TextVar(p encoding.TextUnmarshaler, name string, value encoding.TextMarshaler, usage string) {
+ CommandLine.VarP(newTextValue(value, p), name, "", usage)
+}
+
+// TextVarP is like TextVar, but accepts a shorthand letter that can be used after a single dash.
+func TextVarP(p encoding.TextUnmarshaler, name, shorthand string, value encoding.TextMarshaler, usage string) {
+ CommandLine.VarP(newTextValue(value, p), name, shorthand, usage)
+}
diff --git a/vendor/github.com/spf13/pflag/time.go b/vendor/github.com/spf13/pflag/time.go
new file mode 100644
index 000000000..3dee42479
--- /dev/null
+++ b/vendor/github.com/spf13/pflag/time.go
@@ -0,0 +1,124 @@
+package pflag
+
+import (
+ "fmt"
+ "strings"
+ "time"
+)
+
+// TimeValue adapts time.Time for use as a flag.
+type timeValue struct {
+ *time.Time
+ formats []string
+}
+
+func newTimeValue(val time.Time, p *time.Time, formats []string) *timeValue {
+ *p = val
+ return &timeValue{
+ Time: p,
+ formats: formats,
+ }
+}
+
+// Set time.Time value from string based on accepted formats.
+func (d *timeValue) Set(s string) error {
+ s = strings.TrimSpace(s)
+ for _, f := range d.formats {
+ v, err := time.Parse(f, s)
+ if err != nil {
+ continue
+ }
+ *d.Time = v
+ return nil
+ }
+
+ formatsString := ""
+ for i, f := range d.formats {
+ if i > 0 {
+ formatsString += ", "
+ }
+ formatsString += fmt.Sprintf("`%s`", f)
+ }
+
+ return fmt.Errorf("invalid time format `%s` must be one of: %s", s, formatsString)
+}
+
+// Type name for time.Time flags.
+func (d *timeValue) Type() string {
+ return "time"
+}
+
+func (d *timeValue) String() string {
+ if d.Time.IsZero() {
+ return ""
+ } else {
+ return d.Time.Format(time.RFC3339Nano)
+ }
+}
+
+// GetTime return the time value of a flag with the given name
+func (f *FlagSet) GetTime(name string) (time.Time, error) {
+ flag := f.Lookup(name)
+ if flag == nil {
+ err := fmt.Errorf("flag accessed but not defined: %s", name)
+ return time.Time{}, err
+ }
+
+ if flag.Value.Type() != "time" {
+ err := fmt.Errorf("trying to get %s value of flag of type %s", "time", flag.Value.Type())
+ return time.Time{}, err
+ }
+
+ val, ok := flag.Value.(*timeValue)
+ if !ok {
+ return time.Time{}, fmt.Errorf("value %s is not a time", flag.Value)
+ }
+
+ return *val.Time, nil
+}
+
+// TimeVar defines a time.Time flag with specified name, default value, and usage string.
+// The argument p points to a time.Time variable in which to store the value of the flag.
+func (f *FlagSet) TimeVar(p *time.Time, name string, value time.Time, formats []string, usage string) {
+ f.TimeVarP(p, name, "", value, formats, usage)
+}
+
+// TimeVarP is like TimeVar, but accepts a shorthand letter that can be used after a single dash.
+func (f *FlagSet) TimeVarP(p *time.Time, name, shorthand string, value time.Time, formats []string, usage string) {
+ f.VarP(newTimeValue(value, p, formats), name, shorthand, usage)
+}
+
+// TimeVar defines a time.Time flag with specified name, default value, and usage string.
+// The argument p points to a time.Time variable in which to store the value of the flag.
+func TimeVar(p *time.Time, name string, value time.Time, formats []string, usage string) {
+ CommandLine.TimeVarP(p, name, "", value, formats, usage)
+}
+
+// TimeVarP is like TimeVar, but accepts a shorthand letter that can be used after a single dash.
+func TimeVarP(p *time.Time, name, shorthand string, value time.Time, formats []string, usage string) {
+ CommandLine.VarP(newTimeValue(value, p, formats), name, shorthand, usage)
+}
+
+// Time defines a time.Time flag with specified name, default value, and usage string.
+// The return value is the address of a time.Time variable that stores the value of the flag.
+func (f *FlagSet) Time(name string, value time.Time, formats []string, usage string) *time.Time {
+ return f.TimeP(name, "", value, formats, usage)
+}
+
+// TimeP is like Time, but accepts a shorthand letter that can be used after a single dash.
+func (f *FlagSet) TimeP(name, shorthand string, value time.Time, formats []string, usage string) *time.Time {
+ p := new(time.Time)
+ f.TimeVarP(p, name, shorthand, value, formats, usage)
+ return p
+}
+
+// Time defines a time.Time flag with specified name, default value, and usage string.
+// The return value is the address of a time.Time variable that stores the value of the flag.
+func Time(name string, value time.Time, formats []string, usage string) *time.Time {
+ return CommandLine.TimeP(name, "", value, formats, usage)
+}
+
+// TimeP is like Time, but accepts a shorthand letter that can be used after a single dash.
+func TimeP(name, shorthand string, value time.Time, formats []string, usage string) *time.Time {
+ return CommandLine.TimeP(name, shorthand, value, formats, usage)
+}
diff --git a/vendor/github.com/spf13/viper/.editorconfig b/vendor/github.com/spf13/viper/.editorconfig
index 1f664d13a..faef0c91e 100644
--- a/vendor/github.com/spf13/viper/.editorconfig
+++ b/vendor/github.com/spf13/viper/.editorconfig
@@ -16,3 +16,6 @@ indent_style = tab
[*.nix]
indent_size = 2
+
+[.golangci.yaml]
+indent_size = 2
diff --git a/vendor/github.com/spf13/viper/.golangci.yaml b/vendor/github.com/spf13/viper/.golangci.yaml
index 474f41633..bed0b83ec 100644
--- a/vendor/github.com/spf13/viper/.golangci.yaml
+++ b/vendor/github.com/spf13/viper/.golangci.yaml
@@ -1,105 +1,118 @@
-run:
- timeout: 5m
+version: "2"
-linters-settings:
- gci:
- sections:
- - standard
- - default
- - prefix(github.com/spf13/viper)
- gocritic:
- # Enable multiple checks by tags. See "Tags" section in https://github.com/go-critic/go-critic#usage.
- enabled-tags:
- - diagnostic
- - experimental
- - opinionated
- - style
- disabled-checks:
- - importShadow
- - unnamedResult
- goimports:
- local-prefixes: github.com/spf13/viper
+run:
+ timeout: 5m
linters:
- disable-all: true
- enable:
- - bodyclose
- - dogsled
- - dupl
- - durationcheck
- - exhaustive
- - gci
- - gocritic
- - godot
- - gofmt
- - gofumpt
- - goimports
- - gomoddirectives
- - goprintffuncname
- - govet
- - importas
- - ineffassign
- - makezero
- - misspell
- - nakedret
- - nilerr
- - noctx
- - nolintlint
- - prealloc
- - predeclared
- - revive
- - rowserrcheck
- - sqlclosecheck
- - staticcheck
- - stylecheck
- - tparallel
- - typecheck
- - unconvert
- - unparam
- - unused
- - wastedassign
- - whitespace
+ enable:
+ - bodyclose
+ - dogsled
+ - dupl
+ - durationcheck
+ - exhaustive
+ - gocritic
+ - godot
+ - gomoddirectives
+ - goprintffuncname
+ - govet
+ - importas
+ - ineffassign
+ - makezero
+ - misspell
+ - nakedret
+ - nilerr
+ - noctx
+ - nolintlint
+ - prealloc
+ - predeclared
+ - revive
+ - rowserrcheck
+ - sqlclosecheck
+ - staticcheck
+ - tparallel
+ - unconvert
+ - unparam
+ - unused
+ - wastedassign
+ - whitespace
- # fixme
- # - cyclop
- # - errcheck
- # - errorlint
- # - exhaustivestruct
- # - forbidigo
- # - forcetypeassert
- # - gochecknoglobals
- # - gochecknoinits
- # - gocognit
- # - goconst
- # - gocyclo
- # - gosec
- # - gosimple
- # - ifshort
- # - lll
- # - nlreturn
- # - paralleltest
- # - scopelint
- # - thelper
- # - wrapcheck
+ # fixme
+ # - cyclop
+ # - errcheck
+ # - errorlint
+ # - exhaustivestruct
+ # - forbidigo
+ # - forcetypeassert
+ # - gochecknoglobals
+ # - gochecknoinits
+ # - gocognit
+ # - goconst
+ # - gocyclo
+ # - gosec
+ # - gosimple
+ # - ifshort
+ # - lll
+ # - nlreturn
+ # - paralleltest
+ # - scopelint
+ # - thelper
+ # - wrapcheck
- # unused
- # - depguard
- # - goheader
- # - gomodguard
+ # unused
+ # - depguard
+ # - goheader
+ # - gomodguard
- # deprecated
- # - deadcode
- # - structcheck
- # - varcheck
+ # don't enable:
+ # - asciicheck
+ # - funlen
+ # - godox
+ # - goerr113
+ # - gomnd
+ # - interfacer
+ # - maligned
+ # - nestif
+ # - testpackage
+ # - wsl
- # don't enable:
- # - asciicheck
- # - funlen
- # - godox
- # - goerr113
- # - gomnd
- # - interfacer
- # - maligned
- # - nestif
- # - testpackage
- # - wsl
+ exclusions:
+ rules:
+ - linters:
+ - errcheck
+ - noctx
+ path: _test.go
+ presets:
+ - comments
+ - std-error-handling
+
+ settings:
+ misspell:
+ locale: US
+ nolintlint:
+ allow-unused: false # report any unused nolint directives
+ require-specific: false # don't require nolint directives to be specific about which linter is being skipped
+ gocritic:
+ # Enable multiple checks by tags. See "Tags" section in https://github.com/go-critic/go-critic#usage.
+ enabled-tags:
+ - diagnostic
+ - experimental
+ - opinionated
+ - style
+ disabled-checks:
+ - importShadow
+ - unnamedResult
+
+formatters:
+ enable:
+ - gci
+ - gofmt
+ - gofumpt
+ - goimports
+ # - golines
+
+ settings:
+ gci:
+ sections:
+ - standard
+ - default
+ - localmodule
diff --git a/vendor/github.com/spf13/viper/README.md b/vendor/github.com/spf13/viper/README.md
index 769a5d900..7a4c0fc30 100644
--- a/vendor/github.com/spf13/viper/README.md
+++ b/vendor/github.com/spf13/viper/README.md
@@ -12,7 +12,7 @@
[](https://github.com/spf13/viper/actions?query=workflow%3ACI)
[](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[](https://goreportcard.com/report/github.com/spf13/viper)
-
+
[](https://pkg.go.dev/mod/github.com/spf13/viper)
**Go configuration with fangs!**
@@ -821,7 +821,7 @@ You can use your favorite format's marshaller with the config returned by `AllSe
```go
import (
- yaml "gopkg.in/yaml.v2"
+ yaml "go.yaml.in/yaml/v3"
// ...
)
diff --git a/vendor/github.com/spf13/viper/UPDATES.md b/vendor/github.com/spf13/viper/UPGRADE.md
similarity index 79%
rename from vendor/github.com/spf13/viper/UPDATES.md
rename to vendor/github.com/spf13/viper/UPGRADE.md
index ccf413ed7..a33c965a4 100644
--- a/vendor/github.com/spf13/viper/UPDATES.md
+++ b/vendor/github.com/spf13/viper/UPGRADE.md
@@ -83,6 +83,27 @@ v := viper.NewWithOptions(
)
```
+### BREAKING: "github.com/mitchellh/mapstructure" depedency replaced
+
+The original [mapstructure](https://github.com/mitchellh/mapstructure) has been [archived](https://github.com/mitchellh/mapstructure/issues/349) and was replaced with a [fork](https://github.com/go-viper/mapstructure) maintained by Viper ([#1723](https://github.com/spf13/viper/pull/1723)).
+
+As a result, the package import path needs to be changed in cases where `mapstructure` is directly referenced in your code.
+
+For example, when providing a custom decoder config:
+
+```go
+err := viper.Unmarshal(&appConfig, func(config *mapstructure.DecoderConfig) {
+ config.TagName = "yaml"
+})
+```
+
+The change is fairly straightforward, just replace all occurrences of the import path `github.com/mitchellh/mapstructure` with `github.com/go-viper/mapstructure/v2`:
+
+```diff
+- import "github.com/mitchellh/mapstructure"
++ import "github.com/go-viper/mapstructure/v2"
+```
+
### BREAKING: HCL, Java properties, INI removed from core
In order to reduce third-party dependencies, Viper dropped support for the following formats from the core:
diff --git a/vendor/github.com/spf13/viper/flake.lock b/vendor/github.com/spf13/viper/flake.lock
index d76dfbddd..0b8cfb5a8 100644
--- a/vendor/github.com/spf13/viper/flake.lock
+++ b/vendor/github.com/spf13/viper/flake.lock
@@ -2,30 +2,32 @@
"nodes": {
"cachix": {
"inputs": {
- "devenv": "devenv_2",
+ "devenv": [
+ "devenv"
+ ],
"flake-compat": [
+ "devenv"
+ ],
+ "git-hooks": [
"devenv",
- "flake-compat"
+ "git-hooks"
],
"nixpkgs": [
"devenv",
"nixpkgs"
- ],
- "pre-commit-hooks": [
- "devenv",
- "pre-commit-hooks"
]
},
"locked": {
- "lastModified": 1712055811,
- "narHash": "sha256-7FcfMm5A/f02yyzuavJe06zLa9hcMHsagE28ADcmQvk=",
+ "lastModified": 1748883665,
+ "narHash": "sha256-R0W7uAg+BLoHjMRMQ8+oiSbTq8nkGz5RDpQ+ZfxxP3A=",
"owner": "cachix",
"repo": "cachix",
- "rev": "02e38da89851ec7fec3356a5c04bc8349cae0e30",
+ "rev": "f707778d902af4d62d8dd92c269f8e70de09acbe",
"type": "github"
},
"original": {
"owner": "cachix",
+ "ref": "latest",
"repo": "cachix",
"type": "github"
}
@@ -33,52 +35,21 @@
"devenv": {
"inputs": {
"cachix": "cachix",
- "flake-compat": "flake-compat_2",
- "nix": "nix_2",
- "nixpkgs": "nixpkgs_2",
- "pre-commit-hooks": "pre-commit-hooks"
- },
- "locked": {
- "lastModified": 1724763216,
- "narHash": "sha256-oW2bwCrJpIzibCNK6zfIDaIQw765yMAuMSG2gyZfGv0=",
- "owner": "cachix",
- "repo": "devenv",
- "rev": "1e4ef61205b9aa20fe04bf1c468b6a316281c4f1",
- "type": "github"
- },
- "original": {
- "owner": "cachix",
- "repo": "devenv",
- "type": "github"
- }
- },
- "devenv_2": {
- "inputs": {
- "flake-compat": [
- "devenv",
- "cachix",
- "flake-compat"
- ],
+ "flake-compat": "flake-compat",
+ "git-hooks": "git-hooks",
"nix": "nix",
- "nixpkgs": "nixpkgs",
- "poetry2nix": "poetry2nix",
- "pre-commit-hooks": [
- "devenv",
- "cachix",
- "pre-commit-hooks"
- ]
+ "nixpkgs": "nixpkgs"
},
"locked": {
- "lastModified": 1708704632,
- "narHash": "sha256-w+dOIW60FKMaHI1q5714CSibk99JfYxm0CzTinYWr+Q=",
+ "lastModified": 1755257397,
+ "narHash": "sha256-VU+OHexL2y6y7yrpEc6bZvYYwoQg6aZK1b4YxT0yZCk=",
"owner": "cachix",
"repo": "devenv",
- "rev": "2ee4450b0f4b95a1b90f2eb5ffea98b90e48c196",
+ "rev": "6f9c3d4722aa253631644329f7bda60b1d3d1b97",
"type": "github"
},
"original": {
"owner": "cachix",
- "ref": "python-rewrite",
"repo": "devenv",
"type": "github"
}
@@ -86,27 +57,11 @@
"flake-compat": {
"flake": false,
"locked": {
- "lastModified": 1673956053,
- "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
+ "lastModified": 1747046372,
+ "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
"owner": "edolstra",
"repo": "flake-compat",
- "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
- "type": "github"
- },
- "original": {
- "owner": "edolstra",
- "repo": "flake-compat",
- "type": "github"
- }
- },
- "flake-compat_2": {
- "flake": false,
- "locked": {
- "lastModified": 1696426674,
- "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
- "owner": "edolstra",
- "repo": "flake-compat",
- "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
+ "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
"type": "github"
},
"original": {
@@ -117,14 +72,18 @@
},
"flake-parts": {
"inputs": {
- "nixpkgs-lib": "nixpkgs-lib"
+ "nixpkgs-lib": [
+ "devenv",
+ "nix",
+ "nixpkgs"
+ ]
},
"locked": {
- "lastModified": 1722555600,
- "narHash": "sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC+x4=",
+ "lastModified": 1733312601,
+ "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=",
"owner": "hercules-ci",
"repo": "flake-parts",
- "rev": "8471fe90ad337a8074e957b69ca4d0089218391d",
+ "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9",
"type": "github"
},
"original": {
@@ -133,39 +92,47 @@
"type": "github"
}
},
- "flake-utils": {
+ "flake-parts_2": {
"inputs": {
- "systems": "systems"
+ "nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
- "lastModified": 1689068808,
- "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
- "owner": "numtide",
- "repo": "flake-utils",
- "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
+ "lastModified": 1754487366,
+ "narHash": "sha256-pHYj8gUBapuUzKV/kN/tR3Zvqc7o6gdFB9XKXIp1SQ8=",
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "rev": "af66ad14b28a127c5c0f3bbb298218fc63528a18",
"type": "github"
},
"original": {
- "owner": "numtide",
- "repo": "flake-utils",
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
"type": "github"
}
},
- "flake-utils_2": {
+ "git-hooks": {
"inputs": {
- "systems": "systems_2"
+ "flake-compat": [
+ "devenv",
+ "flake-compat"
+ ],
+ "gitignore": "gitignore",
+ "nixpkgs": [
+ "devenv",
+ "nixpkgs"
+ ]
},
"locked": {
- "lastModified": 1710146030,
- "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
- "owner": "numtide",
- "repo": "flake-utils",
- "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
+ "lastModified": 1750779888,
+ "narHash": "sha256-wibppH3g/E2lxU43ZQHC5yA/7kIKLGxVEnsnVK1BtRg=",
+ "owner": "cachix",
+ "repo": "git-hooks.nix",
+ "rev": "16ec914f6fb6f599ce988427d9d94efddf25fe6d",
"type": "github"
},
"original": {
- "owner": "numtide",
- "repo": "flake-utils",
+ "owner": "cachix",
+ "repo": "git-hooks.nix",
"type": "github"
}
},
@@ -173,7 +140,7 @@
"inputs": {
"nixpkgs": [
"devenv",
- "pre-commit-hooks",
+ "git-hooks",
"nixpkgs"
]
},
@@ -192,165 +159,49 @@
}
},
"nix": {
- "inputs": {
- "flake-compat": "flake-compat",
- "nixpkgs": [
- "devenv",
- "cachix",
- "devenv",
- "nixpkgs"
- ],
- "nixpkgs-regression": "nixpkgs-regression"
- },
- "locked": {
- "lastModified": 1712911606,
- "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=",
- "owner": "domenkozar",
- "repo": "nix",
- "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12",
- "type": "github"
- },
- "original": {
- "owner": "domenkozar",
- "ref": "devenv-2.21",
- "repo": "nix",
- "type": "github"
- }
- },
- "nix-github-actions": {
- "inputs": {
- "nixpkgs": [
- "devenv",
- "cachix",
- "devenv",
- "poetry2nix",
- "nixpkgs"
- ]
- },
- "locked": {
- "lastModified": 1688870561,
- "narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=",
- "owner": "nix-community",
- "repo": "nix-github-actions",
- "rev": "165b1650b753316aa7f1787f3005a8d2da0f5301",
- "type": "github"
- },
- "original": {
- "owner": "nix-community",
- "repo": "nix-github-actions",
- "type": "github"
- }
- },
- "nix_2": {
"inputs": {
"flake-compat": [
"devenv",
"flake-compat"
],
+ "flake-parts": "flake-parts",
+ "git-hooks-nix": [
+ "devenv",
+ "git-hooks"
+ ],
"nixpkgs": [
"devenv",
"nixpkgs"
],
- "nixpkgs-regression": "nixpkgs-regression_2"
+ "nixpkgs-23-11": [
+ "devenv"
+ ],
+ "nixpkgs-regression": [
+ "devenv"
+ ]
},
"locked": {
- "lastModified": 1712911606,
- "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=",
- "owner": "domenkozar",
+ "lastModified": 1755029779,
+ "narHash": "sha256-3+GHIYGg4U9XKUN4rg473frIVNn8YD06bjwxKS1IPrU=",
+ "owner": "cachix",
"repo": "nix",
- "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12",
+ "rev": "b0972b0eee6726081d10b1199f54de6d2917f861",
"type": "github"
},
"original": {
- "owner": "domenkozar",
- "ref": "devenv-2.21",
+ "owner": "cachix",
+ "ref": "devenv-2.30",
"repo": "nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
- "lastModified": 1692808169,
- "narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=",
- "owner": "NixOS",
- "repo": "nixpkgs",
- "rev": "9201b5ff357e781bf014d0330d18555695df7ba8",
- "type": "github"
- },
- "original": {
- "owner": "NixOS",
- "ref": "nixpkgs-unstable",
- "repo": "nixpkgs",
- "type": "github"
- }
- },
- "nixpkgs-lib": {
- "locked": {
- "lastModified": 1722555339,
- "narHash": "sha256-uFf2QeW7eAHlYXuDktm9c25OxOyCoUOQmh5SZ9amE5Q=",
- "type": "tarball",
- "url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz"
- },
- "original": {
- "type": "tarball",
- "url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz"
- }
- },
- "nixpkgs-regression": {
- "locked": {
- "lastModified": 1643052045,
- "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
- "owner": "NixOS",
- "repo": "nixpkgs",
- "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
- "type": "github"
- },
- "original": {
- "owner": "NixOS",
- "repo": "nixpkgs",
- "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
- "type": "github"
- }
- },
- "nixpkgs-regression_2": {
- "locked": {
- "lastModified": 1643052045,
- "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
- "owner": "NixOS",
- "repo": "nixpkgs",
- "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
- "type": "github"
- },
- "original": {
- "owner": "NixOS",
- "repo": "nixpkgs",
- "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
- "type": "github"
- }
- },
- "nixpkgs-stable": {
- "locked": {
- "lastModified": 1710695816,
- "narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=",
- "owner": "NixOS",
- "repo": "nixpkgs",
- "rev": "614b4613980a522ba49f0d194531beddbb7220d3",
- "type": "github"
- },
- "original": {
- "owner": "NixOS",
- "ref": "nixos-23.11",
- "repo": "nixpkgs",
- "type": "github"
- }
- },
- "nixpkgs_2": {
- "locked": {
- "lastModified": 1713361204,
- "narHash": "sha256-TA6EDunWTkc5FvDCqU3W2T3SFn0gRZqh6D/hJnM02MM=",
+ "lastModified": 1750441195,
+ "narHash": "sha256-yke+pm+MdgRb6c0dPt8MgDhv7fcBbdjmv1ZceNTyzKg=",
"owner": "cachix",
"repo": "devenv-nixpkgs",
- "rev": "285676e87ad9f0ca23d8714a6ab61e7e027020c6",
+ "rev": "0ceffe312871b443929ff3006960d29b120dc627",
"type": "github"
},
"original": {
@@ -360,110 +211,42 @@
"type": "github"
}
},
- "nixpkgs_3": {
- "locked": {
- "lastModified": 1724748588,
- "narHash": "sha256-NlpGA4+AIf1dKNq76ps90rxowlFXUsV9x7vK/mN37JM=",
- "owner": "NixOS",
- "repo": "nixpkgs",
- "rev": "a6292e34000dc93d43bccf78338770c1c5ec8a99",
- "type": "github"
- },
- "original": {
- "owner": "NixOS",
- "ref": "nixpkgs-unstable",
- "repo": "nixpkgs",
- "type": "github"
- }
- },
- "poetry2nix": {
- "inputs": {
- "flake-utils": "flake-utils",
- "nix-github-actions": "nix-github-actions",
- "nixpkgs": [
- "devenv",
- "cachix",
- "devenv",
- "nixpkgs"
- ]
- },
+ "nixpkgs-lib": {
"locked": {
- "lastModified": 1692876271,
- "narHash": "sha256-IXfZEkI0Mal5y1jr6IRWMqK8GW2/f28xJenZIPQqkY0=",
+ "lastModified": 1753579242,
+ "narHash": "sha256-zvaMGVn14/Zz8hnp4VWT9xVnhc8vuL3TStRqwk22biA=",
"owner": "nix-community",
- "repo": "poetry2nix",
- "rev": "d5006be9c2c2417dafb2e2e5034d83fabd207ee3",
+ "repo": "nixpkgs.lib",
+ "rev": "0f36c44e01a6129be94e3ade315a5883f0228a6e",
"type": "github"
},
"original": {
"owner": "nix-community",
- "repo": "poetry2nix",
+ "repo": "nixpkgs.lib",
"type": "github"
}
},
- "pre-commit-hooks": {
- "inputs": {
- "flake-compat": [
- "devenv",
- "flake-compat"
- ],
- "flake-utils": "flake-utils_2",
- "gitignore": "gitignore",
- "nixpkgs": [
- "devenv",
- "nixpkgs"
- ],
- "nixpkgs-stable": "nixpkgs-stable"
- },
+ "nixpkgs_2": {
"locked": {
- "lastModified": 1713775815,
- "narHash": "sha256-Wu9cdYTnGQQwtT20QQMg7jzkANKQjwBD9iccfGKkfls=",
- "owner": "cachix",
- "repo": "pre-commit-hooks.nix",
- "rev": "2ac4dcbf55ed43f3be0bae15e181f08a57af24a4",
+ "lastModified": 1755268003,
+ "narHash": "sha256-nNaeJjo861wFR0tjHDyCnHs1rbRtrMgxAKMoig9Sj/w=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "32f313e49e42f715491e1ea7b306a87c16fe0388",
"type": "github"
},
"original": {
- "owner": "cachix",
- "repo": "pre-commit-hooks.nix",
+ "owner": "NixOS",
+ "ref": "nixpkgs-unstable",
+ "repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"devenv": "devenv",
- "flake-parts": "flake-parts",
- "nixpkgs": "nixpkgs_3"
- }
- },
- "systems": {
- "locked": {
- "lastModified": 1681028828,
- "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
- "owner": "nix-systems",
- "repo": "default",
- "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
- "type": "github"
- },
- "original": {
- "owner": "nix-systems",
- "repo": "default",
- "type": "github"
- }
- },
- "systems_2": {
- "locked": {
- "lastModified": 1681028828,
- "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
- "owner": "nix-systems",
- "repo": "default",
- "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
- "type": "github"
- },
- "original": {
- "owner": "nix-systems",
- "repo": "default",
- "type": "github"
+ "flake-parts": "flake-parts_2",
+ "nixpkgs": "nixpkgs_2"
}
}
},
diff --git a/vendor/github.com/spf13/viper/flake.nix b/vendor/github.com/spf13/viper/flake.nix
index 52ad7d581..a16b2e3a7 100644
--- a/vendor/github.com/spf13/viper/flake.nix
+++ b/vendor/github.com/spf13/viper/flake.nix
@@ -7,51 +7,55 @@
devenv.url = "github:cachix/devenv";
};
- outputs = inputs@{ flake-parts, ... }:
+ outputs =
+ inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
imports = [
inputs.devenv.flakeModule
];
- systems = [ "x86_64-linux" "x86_64-darwin" "aarch64-darwin" ];
-
- perSystem = { config, self', inputs', pkgs, system, ... }: rec {
- devenv.shells = {
- default = {
- languages = {
- go.enable = true;
- go.package = pkgs.go_1_23;
- };
-
- pre-commit.hooks = {
- nixpkgs-fmt.enable = true;
- yamllint.enable = true;
- };
-
- packages = with pkgs; [
- gnumake
-
- golangci-lint
- yamllint
- ];
+ systems = [
+ "x86_64-linux"
+ "x86_64-darwin"
+ "aarch64-darwin"
+ ];
- scripts = {
- versions.exec = ''
- go version
- golangci-lint version
+ perSystem =
+ { pkgs, ... }:
+ {
+ devenv.shells = {
+ default = {
+ languages = {
+ go.enable = true;
+ };
+
+ git-hooks.hooks = {
+ nixpkgs-fmt.enable = true;
+ yamllint.enable = true;
+ };
+
+ packages = with pkgs; [
+ gnumake
+
+ golangci-lint
+ yamllint
+ ];
+
+ scripts = {
+ versions.exec = ''
+ go version
+ golangci-lint version
+ '';
+ };
+
+ enterShell = ''
+ versions
'';
- };
-
- enterShell = ''
- versions
- '';
- # https://github.com/cachix/devenv/issues/528#issuecomment-1556108767
- containers = pkgs.lib.mkForce { };
+ # https://github.com/cachix/devenv/issues/528#issuecomment-1556108767
+ containers = pkgs.lib.mkForce { };
+ };
};
-
- ci = devenv.shells.default;
};
- };
};
}
diff --git a/vendor/github.com/spf13/viper/internal/encoding/yaml/codec.go b/vendor/github.com/spf13/viper/internal/encoding/yaml/codec.go
index 036879249..a7a839fd9 100644
--- a/vendor/github.com/spf13/viper/internal/encoding/yaml/codec.go
+++ b/vendor/github.com/spf13/viper/internal/encoding/yaml/codec.go
@@ -1,6 +1,6 @@
package yaml
-import "gopkg.in/yaml.v3"
+import "go.yaml.in/yaml/v3"
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for YAML encoding.
type Codec struct{}
diff --git a/vendor/github.com/spf13/viper/remote.go b/vendor/github.com/spf13/viper/remote.go
index bdde7de26..46f26721d 100644
--- a/vendor/github.com/spf13/viper/remote.go
+++ b/vendor/github.com/spf13/viper/remote.go
@@ -219,7 +219,10 @@ func (v *Viper) watchKeyValueConfigOnChannel() error {
for {
b := <-rc
reader := bytes.NewReader(b.Value)
- v.unmarshalReader(reader, v.kvstore)
+ err := v.unmarshalReader(reader, v.kvstore)
+ if err != nil {
+ v.logger.Error(fmt.Errorf("failed to unmarshal remote config: %w", err).Error())
+ }
}
}(respc)
return nil
diff --git a/vendor/github.com/spf13/viper/util.go b/vendor/github.com/spf13/viper/util.go
index 2a08074bc..d08ed4621 100644
--- a/vendor/github.com/spf13/viper/util.go
+++ b/vendor/github.com/spf13/viper/util.go
@@ -174,10 +174,7 @@ func parseSizeInBytes(sizeStr string) uint {
}
}
- size := cast.ToInt(sizeStr)
- if size < 0 {
- size = 0
- }
+ size := max(cast.ToInt(sizeStr), 0)
return safeMul(uint(size), multiplier)
}
diff --git a/vendor/github.com/spf13/viper/viper.go b/vendor/github.com/spf13/viper/viper.go
index a58d757bd..34a94798b 100644
--- a/vendor/github.com/spf13/viper/viper.go
+++ b/vendor/github.com/spf13/viper/viper.go
@@ -376,7 +376,12 @@ func (v *Viper) WatchConfig() {
}
}
}()
- watcher.Add(configDir)
+ err = watcher.Add(configDir)
+ if err != nil {
+ v.logger.Error(fmt.Sprintf("failed to add watcher: %s", err))
+ initWG.Done()
+ return
+ }
initWG.Done() // done initializing the watch in this go routine, so the parent routine can move on...
eventsWG.Wait() // now, wait for event loop to end in this go-routine...
}()
@@ -1181,11 +1186,26 @@ func (v *Viper) find(lcaseKey string, flagDefault bool) any {
s = strings.TrimSuffix(s, "]")
res, _ := readAsCSV(s)
return res
+ case "boolSlice":
+ s := strings.TrimPrefix(flag.ValueString(), "[")
+ s = strings.TrimSuffix(s, "]")
+ res, _ := readAsCSV(s)
+ return cast.ToBoolSlice(res)
case "intSlice":
s := strings.TrimPrefix(flag.ValueString(), "[")
s = strings.TrimSuffix(s, "]")
res, _ := readAsCSV(s)
return cast.ToIntSlice(res)
+ case "uintSlice":
+ s := strings.TrimPrefix(flag.ValueString(), "[")
+ s = strings.TrimSuffix(s, "]")
+ res, _ := readAsCSV(s)
+ return cast.ToUintSlice(res)
+ case "float64Slice":
+ s := strings.TrimPrefix(flag.ValueString(), "[")
+ s = strings.TrimSuffix(s, "]")
+ res, _ := readAsCSV(s)
+ return cast.ToFloat64Slice(res)
case "durationSlice":
s := strings.TrimPrefix(flag.ValueString(), "[")
s = strings.TrimSuffix(s, "]")
@@ -1268,11 +1288,26 @@ func (v *Viper) find(lcaseKey string, flagDefault bool) any {
s = strings.TrimSuffix(s, "]")
res, _ := readAsCSV(s)
return res
+ case "boolSlice":
+ s := strings.TrimPrefix(flag.ValueString(), "[")
+ s = strings.TrimSuffix(s, "]")
+ res, _ := readAsCSV(s)
+ return cast.ToBoolSlice(res)
case "intSlice":
s := strings.TrimPrefix(flag.ValueString(), "[")
s = strings.TrimSuffix(s, "]")
res, _ := readAsCSV(s)
return cast.ToIntSlice(res)
+ case "uintSlice":
+ s := strings.TrimPrefix(flag.ValueString(), "[")
+ s = strings.TrimSuffix(s, "]")
+ res, _ := readAsCSV(s)
+ return cast.ToUintSlice(res)
+ case "float64Slice":
+ s := strings.TrimPrefix(flag.ValueString(), "[")
+ s = strings.TrimSuffix(s, "]")
+ res, _ := readAsCSV(s)
+ return cast.ToFloat64Slice(res)
case "stringToString":
return stringToStringConv(flag.ValueString())
case "stringToInt":
@@ -1670,7 +1705,10 @@ func (v *Viper) unmarshalReader(in io.Reader, c map[string]any) error {
}
buf := new(bytes.Buffer)
- buf.ReadFrom(in)
+ _, err := buf.ReadFrom(in)
+ if err != nil {
+ return fmt.Errorf("failed to read configuration from input: %w", err)
+ }
// TODO: remove this once SupportedExts is deprecated/removed
if !slices.Contains(SupportedExts, format) {
diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare.go b/vendor/github.com/stretchr/testify/assert/assertion_compare.go
index 7e19eba09..ffb24e8e3 100644
--- a/vendor/github.com/stretchr/testify/assert/assertion_compare.go
+++ b/vendor/github.com/stretchr/testify/assert/assertion_compare.go
@@ -390,7 +390,8 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface
if h, ok := t.(tHelper); ok {
h.Helper()
}
- return compareTwoValues(t, e1, e2, []compareResult{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...)
+ failMessage := fmt.Sprintf("\"%v\" is not greater than \"%v\"", e1, e2)
+ return compareTwoValues(t, e1, e2, []compareResult{compareGreater}, failMessage, msgAndArgs...)
}
// GreaterOrEqual asserts that the first element is greater than or equal to the second
@@ -403,7 +404,8 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in
if h, ok := t.(tHelper); ok {
h.Helper()
}
- return compareTwoValues(t, e1, e2, []compareResult{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...)
+ failMessage := fmt.Sprintf("\"%v\" is not greater than or equal to \"%v\"", e1, e2)
+ return compareTwoValues(t, e1, e2, []compareResult{compareGreater, compareEqual}, failMessage, msgAndArgs...)
}
// Less asserts that the first element is less than the second
@@ -415,7 +417,8 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{})
if h, ok := t.(tHelper); ok {
h.Helper()
}
- return compareTwoValues(t, e1, e2, []compareResult{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...)
+ failMessage := fmt.Sprintf("\"%v\" is not less than \"%v\"", e1, e2)
+ return compareTwoValues(t, e1, e2, []compareResult{compareLess}, failMessage, msgAndArgs...)
}
// LessOrEqual asserts that the first element is less than or equal to the second
@@ -428,7 +431,8 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter
if h, ok := t.(tHelper); ok {
h.Helper()
}
- return compareTwoValues(t, e1, e2, []compareResult{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...)
+ failMessage := fmt.Sprintf("\"%v\" is not less than or equal to \"%v\"", e1, e2)
+ return compareTwoValues(t, e1, e2, []compareResult{compareLess, compareEqual}, failMessage, msgAndArgs...)
}
// Positive asserts that the specified element is positive
@@ -440,7 +444,8 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
h.Helper()
}
zero := reflect.Zero(reflect.TypeOf(e))
- return compareTwoValues(t, e, zero.Interface(), []compareResult{compareGreater}, "\"%v\" is not positive", msgAndArgs...)
+ failMessage := fmt.Sprintf("\"%v\" is not positive", e)
+ return compareTwoValues(t, e, zero.Interface(), []compareResult{compareGreater}, failMessage, msgAndArgs...)
}
// Negative asserts that the specified element is negative
@@ -452,7 +457,8 @@ func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool {
h.Helper()
}
zero := reflect.Zero(reflect.TypeOf(e))
- return compareTwoValues(t, e, zero.Interface(), []compareResult{compareLess}, "\"%v\" is not negative", msgAndArgs...)
+ failMessage := fmt.Sprintf("\"%v\" is not negative", e)
+ return compareTwoValues(t, e, zero.Interface(), []compareResult{compareLess}, failMessage, msgAndArgs...)
}
func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...interface{}) bool {
@@ -468,11 +474,11 @@ func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedCompare
compareResult, isComparable := compare(e1, e2, e1Kind)
if !isComparable {
- return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
+ return Fail(t, fmt.Sprintf(`Can not compare type "%T"`, e1), msgAndArgs...)
}
if !containsValue(allowedComparesResults, compareResult) {
- return Fail(t, fmt.Sprintf(failMessage, e1, e2), msgAndArgs...)
+ return Fail(t, failMessage, msgAndArgs...)
}
return true
diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go
index 190634165..c592f6ad5 100644
--- a/vendor/github.com/stretchr/testify/assert/assertion_format.go
+++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go
@@ -50,10 +50,19 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string
return ElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...)
}
-// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either
-// a slice or a channel with len == 0.
+// Emptyf asserts that the given value is "empty".
+//
+// [Zero values] are "empty".
+//
+// Arrays are "empty" if every element is the zero value of the type (stricter than "empty").
+//
+// Slices, maps and channels with zero length are "empty".
+//
+// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty".
//
// assert.Emptyf(t, obj, "error message %s", "formatted")
+//
+// [Zero values]: https://go.dev/ref/spec#The_zero_value
func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
@@ -117,10 +126,8 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri
// Errorf asserts that a function returned an error (i.e. not `nil`).
//
-// actualObj, err := SomeFunction()
-// if assert.Errorf(t, err, "error message %s", "formatted") {
-// assert.Equal(t, expectedErrorf, err)
-// }
+// actualObj, err := SomeFunction()
+// assert.Errorf(t, err, "error message %s", "formatted")
func Errorf(t TestingT, err error, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
@@ -438,7 +445,19 @@ func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interf
return IsNonIncreasing(t, object, append([]interface{}{msg}, args...)...)
}
+// IsNotTypef asserts that the specified objects are not of the same type.
+//
+// assert.IsNotTypef(t, &NotMyStruct{}, &MyStruct{}, "error message %s", "formatted")
+func IsNotTypef(t TestingT, theType interface{}, object interface{}, msg string, args ...interface{}) bool {
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return IsNotType(t, theType, object, append([]interface{}{msg}, args...)...)
+}
+
// IsTypef asserts that the specified objects are of the same type.
+//
+// assert.IsTypef(t, &MyStruct{}, &MyStruct{}, "error message %s", "formatted")
func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
@@ -585,8 +604,7 @@ func NotElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg str
return NotElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...)
}
-// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
-// a slice or a channel with len == 0.
+// NotEmptyf asserts that the specified object is NOT [Empty].
//
// if assert.NotEmptyf(t, obj, "error message %s", "formatted") {
// assert.Equal(t, "two", obj[1])
@@ -693,12 +711,15 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string,
return NotSame(t, expected, actual, append([]interface{}{msg}, args...)...)
}
-// NotSubsetf asserts that the specified list(array, slice...) or map does NOT
-// contain all elements given in the specified subset list(array, slice...) or
-// map.
+// NotSubsetf asserts that the list (array, slice, or map) does NOT contain all
+// elements given in the subset (array, slice, or map).
+// Map elements are key-value pairs unless compared with an array or slice where
+// only the map key is evaluated.
//
// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted")
// assert.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted")
+// assert.NotSubsetf(t, [1, 3, 4], {1: "one", 2: "two"}, "error message %s", "formatted")
+// assert.NotSubsetf(t, {"x": 1, "y": 2}, ["z"], "error message %s", "formatted")
func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
@@ -782,11 +803,15 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg
return Same(t, expected, actual, append([]interface{}{msg}, args...)...)
}
-// Subsetf asserts that the specified list(array, slice...) or map contains all
-// elements given in the specified subset list(array, slice...) or map.
+// Subsetf asserts that the list (array, slice, or map) contains all elements
+// given in the subset (array, slice, or map).
+// Map elements are key-value pairs unless compared with an array or slice where
+// only the map key is evaluated.
//
// assert.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted")
// assert.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted")
+// assert.Subsetf(t, [1, 2, 3], {1: "one", 2: "two"}, "error message %s", "formatted")
+// assert.Subsetf(t, {"x": 1, "y": 2}, ["x"], "error message %s", "formatted")
func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go
index 21629087b..58db92845 100644
--- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go
+++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go
@@ -92,10 +92,19 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st
return ElementsMatchf(a.t, listA, listB, msg, args...)
}
-// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either
-// a slice or a channel with len == 0.
+// Empty asserts that the given value is "empty".
+//
+// [Zero values] are "empty".
+//
+// Arrays are "empty" if every element is the zero value of the type (stricter than "empty").
+//
+// Slices, maps and channels with zero length are "empty".
+//
+// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty".
//
// a.Empty(obj)
+//
+// [Zero values]: https://go.dev/ref/spec#The_zero_value
func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
@@ -103,10 +112,19 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool {
return Empty(a.t, object, msgAndArgs...)
}
-// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either
-// a slice or a channel with len == 0.
+// Emptyf asserts that the given value is "empty".
+//
+// [Zero values] are "empty".
+//
+// Arrays are "empty" if every element is the zero value of the type (stricter than "empty").
+//
+// Slices, maps and channels with zero length are "empty".
+//
+// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty".
//
// a.Emptyf(obj, "error message %s", "formatted")
+//
+// [Zero values]: https://go.dev/ref/spec#The_zero_value
func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
@@ -224,10 +242,8 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string
// Error asserts that a function returned an error (i.e. not `nil`).
//
-// actualObj, err := SomeFunction()
-// if a.Error(err) {
-// assert.Equal(t, expectedError, err)
-// }
+// actualObj, err := SomeFunction()
+// a.Error(err)
func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
@@ -297,10 +313,8 @@ func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...inter
// Errorf asserts that a function returned an error (i.e. not `nil`).
//
-// actualObj, err := SomeFunction()
-// if a.Errorf(err, "error message %s", "formatted") {
-// assert.Equal(t, expectedErrorf, err)
-// }
+// actualObj, err := SomeFunction()
+// a.Errorf(err, "error message %s", "formatted")
func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
@@ -868,7 +882,29 @@ func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...in
return IsNonIncreasingf(a.t, object, msg, args...)
}
+// IsNotType asserts that the specified objects are not of the same type.
+//
+// a.IsNotType(&NotMyStruct{}, &MyStruct{})
+func (a *Assertions) IsNotType(theType interface{}, object interface{}, msgAndArgs ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return IsNotType(a.t, theType, object, msgAndArgs...)
+}
+
+// IsNotTypef asserts that the specified objects are not of the same type.
+//
+// a.IsNotTypef(&NotMyStruct{}, &MyStruct{}, "error message %s", "formatted")
+func (a *Assertions) IsNotTypef(theType interface{}, object interface{}, msg string, args ...interface{}) bool {
+ if h, ok := a.t.(tHelper); ok {
+ h.Helper()
+ }
+ return IsNotTypef(a.t, theType, object, msg, args...)
+}
+
// IsType asserts that the specified objects are of the same type.
+//
+// a.IsType(&MyStruct{}, &MyStruct{})
func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
@@ -877,6 +913,8 @@ func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAnd
}
// IsTypef asserts that the specified objects are of the same type.
+//
+// a.IsTypef(&MyStruct{}, &MyStruct{}, "error message %s", "formatted")
func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
@@ -1162,8 +1200,7 @@ func (a *Assertions) NotElementsMatchf(listA interface{}, listB interface{}, msg
return NotElementsMatchf(a.t, listA, listB, msg, args...)
}
-// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
-// a slice or a channel with len == 0.
+// NotEmpty asserts that the specified object is NOT [Empty].
//
// if a.NotEmpty(obj) {
// assert.Equal(t, "two", obj[1])
@@ -1175,8 +1212,7 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) boo
return NotEmpty(a.t, object, msgAndArgs...)
}
-// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
-// a slice or a channel with len == 0.
+// NotEmptyf asserts that the specified object is NOT [Empty].
//
// if a.NotEmptyf(obj, "error message %s", "formatted") {
// assert.Equal(t, "two", obj[1])
@@ -1378,12 +1414,15 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri
return NotSamef(a.t, expected, actual, msg, args...)
}
-// NotSubset asserts that the specified list(array, slice...) or map does NOT
-// contain all elements given in the specified subset list(array, slice...) or
-// map.
+// NotSubset asserts that the list (array, slice, or map) does NOT contain all
+// elements given in the subset (array, slice, or map).
+// Map elements are key-value pairs unless compared with an array or slice where
+// only the map key is evaluated.
//
// a.NotSubset([1, 3, 4], [1, 2])
// a.NotSubset({"x": 1, "y": 2}, {"z": 3})
+// a.NotSubset([1, 3, 4], {1: "one", 2: "two"})
+// a.NotSubset({"x": 1, "y": 2}, ["z"])
func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
@@ -1391,12 +1430,15 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs
return NotSubset(a.t, list, subset, msgAndArgs...)
}
-// NotSubsetf asserts that the specified list(array, slice...) or map does NOT
-// contain all elements given in the specified subset list(array, slice...) or
-// map.
+// NotSubsetf asserts that the list (array, slice, or map) does NOT contain all
+// elements given in the subset (array, slice, or map).
+// Map elements are key-value pairs unless compared with an array or slice where
+// only the map key is evaluated.
//
// a.NotSubsetf([1, 3, 4], [1, 2], "error message %s", "formatted")
// a.NotSubsetf({"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted")
+// a.NotSubsetf([1, 3, 4], {1: "one", 2: "two"}, "error message %s", "formatted")
+// a.NotSubsetf({"x": 1, "y": 2}, ["z"], "error message %s", "formatted")
func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
@@ -1556,11 +1598,15 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string,
return Samef(a.t, expected, actual, msg, args...)
}
-// Subset asserts that the specified list(array, slice...) or map contains all
-// elements given in the specified subset list(array, slice...) or map.
+// Subset asserts that the list (array, slice, or map) contains all elements
+// given in the subset (array, slice, or map).
+// Map elements are key-value pairs unless compared with an array or slice where
+// only the map key is evaluated.
//
// a.Subset([1, 2, 3], [1, 2])
// a.Subset({"x": 1, "y": 2}, {"x": 1})
+// a.Subset([1, 2, 3], {1: "one", 2: "two"})
+// a.Subset({"x": 1, "y": 2}, ["x"])
func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
@@ -1568,11 +1614,15 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...
return Subset(a.t, list, subset, msgAndArgs...)
}
-// Subsetf asserts that the specified list(array, slice...) or map contains all
-// elements given in the specified subset list(array, slice...) or map.
+// Subsetf asserts that the list (array, slice, or map) contains all elements
+// given in the subset (array, slice, or map).
+// Map elements are key-value pairs unless compared with an array or slice where
+// only the map key is evaluated.
//
// a.Subsetf([1, 2, 3], [1, 2], "error message %s", "formatted")
// a.Subsetf({"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted")
+// a.Subsetf([1, 2, 3], {1: "one", 2: "two"}, "error message %s", "formatted")
+// a.Subsetf({"x": 1, "y": 2}, ["x"], "error message %s", "formatted")
func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
diff --git a/vendor/github.com/stretchr/testify/assert/assertion_order.go b/vendor/github.com/stretchr/testify/assert/assertion_order.go
index 1d2f71824..2fdf80fdd 100644
--- a/vendor/github.com/stretchr/testify/assert/assertion_order.go
+++ b/vendor/github.com/stretchr/testify/assert/assertion_order.go
@@ -33,7 +33,7 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []compareR
compareResult, isComparable := compare(prevValueInterface, valueInterface, firstValueKind)
if !isComparable {
- return Fail(t, fmt.Sprintf("Can not compare type \"%s\" and \"%s\"", reflect.TypeOf(value), reflect.TypeOf(prevValue)), msgAndArgs...)
+ return Fail(t, fmt.Sprintf(`Can not compare type "%T" and "%T"`, value, prevValue), msgAndArgs...)
}
if !containsValue(allowedComparesResults, compareResult) {
diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go
index 4e91332bb..de8de0cb6 100644
--- a/vendor/github.com/stretchr/testify/assert/assertions.go
+++ b/vendor/github.com/stretchr/testify/assert/assertions.go
@@ -210,59 +210,77 @@ the problem actually occurred in calling code.*/
// of each stack frame leading from the current test to the assert call that
// failed.
func CallerInfo() []string {
-
var pc uintptr
- var ok bool
var file string
var line int
var name string
+ const stackFrameBufferSize = 10
+ pcs := make([]uintptr, stackFrameBufferSize)
+
callers := []string{}
- for i := 0; ; i++ {
- pc, file, line, ok = runtime.Caller(i)
- if !ok {
- // The breaks below failed to terminate the loop, and we ran off the
- // end of the call stack.
- break
- }
+ offset := 1
- // This is a huge edge case, but it will panic if this is the case, see #180
- if file == "" {
- break
- }
+ for {
+ n := runtime.Callers(offset, pcs)
- f := runtime.FuncForPC(pc)
- if f == nil {
- break
- }
- name = f.Name()
-
- // testing.tRunner is the standard library function that calls
- // tests. Subtests are called directly by tRunner, without going through
- // the Test/Benchmark/Example function that contains the t.Run calls, so
- // with subtests we should break when we hit tRunner, without adding it
- // to the list of callers.
- if name == "testing.tRunner" {
+ if n == 0 {
break
}
- parts := strings.Split(file, "/")
- if len(parts) > 1 {
- filename := parts[len(parts)-1]
- dir := parts[len(parts)-2]
- if (dir != "assert" && dir != "mock" && dir != "require") || filename == "mock_test.go" {
- callers = append(callers, fmt.Sprintf("%s:%d", file, line))
+ frames := runtime.CallersFrames(pcs[:n])
+
+ for {
+ frame, more := frames.Next()
+ pc = frame.PC
+ file = frame.File
+ line = frame.Line
+
+ // This is a huge edge case, but it will panic if this is the case, see #180
+ if file == "" {
+ break
}
- }
- // Drop the package
- segments := strings.Split(name, ".")
- name = segments[len(segments)-1]
- if isTest(name, "Test") ||
- isTest(name, "Benchmark") ||
- isTest(name, "Example") {
- break
+ f := runtime.FuncForPC(pc)
+ if f == nil {
+ break
+ }
+ name = f.Name()
+
+ // testing.tRunner is the standard library function that calls
+ // tests. Subtests are called directly by tRunner, without going through
+ // the Test/Benchmark/Example function that contains the t.Run calls, so
+ // with subtests we should break when we hit tRunner, without adding it
+ // to the list of callers.
+ if name == "testing.tRunner" {
+ break
+ }
+
+ parts := strings.Split(file, "/")
+ if len(parts) > 1 {
+ filename := parts[len(parts)-1]
+ dir := parts[len(parts)-2]
+ if (dir != "assert" && dir != "mock" && dir != "require") || filename == "mock_test.go" {
+ callers = append(callers, fmt.Sprintf("%s:%d", file, line))
+ }
+ }
+
+ // Drop the package
+ dotPos := strings.LastIndexByte(name, '.')
+ name = name[dotPos+1:]
+ if isTest(name, "Test") ||
+ isTest(name, "Benchmark") ||
+ isTest(name, "Example") {
+ break
+ }
+
+ if !more {
+ break
+ }
}
+
+ // Next batch
+ offset += cap(pcs)
}
return callers
@@ -437,17 +455,34 @@ func NotImplements(t TestingT, interfaceObject interface{}, object interface{},
return true
}
+func isType(expectedType, object interface{}) bool {
+ return ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType))
+}
+
// IsType asserts that the specified objects are of the same type.
-func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool {
+//
+// assert.IsType(t, &MyStruct{}, &MyStruct{})
+func IsType(t TestingT, expectedType, object interface{}, msgAndArgs ...interface{}) bool {
+ if isType(expectedType, object) {
+ return true
+ }
if h, ok := t.(tHelper); ok {
h.Helper()
}
+ return Fail(t, fmt.Sprintf("Object expected to be of type %T, but was %T", expectedType, object), msgAndArgs...)
+}
- if !ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) {
- return Fail(t, fmt.Sprintf("Object expected to be of type %v, but was %v", reflect.TypeOf(expectedType), reflect.TypeOf(object)), msgAndArgs...)
+// IsNotType asserts that the specified objects are not of the same type.
+//
+// assert.IsNotType(t, &NotMyStruct{}, &MyStruct{})
+func IsNotType(t TestingT, theType, object interface{}, msgAndArgs ...interface{}) bool {
+ if !isType(theType, object) {
+ return true
}
-
- return true
+ if h, ok := t.(tHelper); ok {
+ h.Helper()
+ }
+ return Fail(t, fmt.Sprintf("Object type expected to be different than %T", theType), msgAndArgs...)
}
// Equal asserts that two objects are equal.
@@ -475,7 +510,6 @@ func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{})
}
return true
-
}
// validateEqualArgs checks whether provided arguments can be safely used in the
@@ -510,8 +544,9 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b
if !same {
// both are pointers but not the same type & pointing to the same address
return Fail(t, fmt.Sprintf("Not same: \n"+
- "expected: %p %#v\n"+
- "actual : %p %#v", expected, expected, actual, actual), msgAndArgs...)
+ "expected: %p %#[1]v\n"+
+ "actual : %p %#[2]v",
+ expected, actual), msgAndArgs...)
}
return true
@@ -530,14 +565,14 @@ func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}
same, ok := samePointers(expected, actual)
if !ok {
- //fails when the arguments are not pointers
+ // fails when the arguments are not pointers
return !(Fail(t, "Both arguments must be pointers", msgAndArgs...))
}
if same {
return Fail(t, fmt.Sprintf(
- "Expected and actual point to the same object: %p %#v",
- expected, expected), msgAndArgs...)
+ "Expected and actual point to the same object: %p %#[1]v",
+ expected), msgAndArgs...)
}
return true
}
@@ -549,7 +584,7 @@ func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}
func samePointers(first, second interface{}) (same bool, ok bool) {
firstPtr, secondPtr := reflect.ValueOf(first), reflect.ValueOf(second)
if firstPtr.Kind() != reflect.Ptr || secondPtr.Kind() != reflect.Ptr {
- return false, false //not both are pointers
+ return false, false // not both are pointers
}
firstType, secondType := reflect.TypeOf(first), reflect.TypeOf(second)
@@ -610,7 +645,6 @@ func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interfa
}
return true
-
}
// EqualExportedValues asserts that the types of two objects are equal and their public
@@ -665,7 +699,6 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}
}
return Equal(t, expected, actual, msgAndArgs...)
-
}
// NotNil asserts that the specified object is not nil.
@@ -715,37 +748,45 @@ func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
// isEmpty gets whether the specified object is considered empty or not.
func isEmpty(object interface{}) bool {
-
// get nil case out of the way
if object == nil {
return true
}
- objValue := reflect.ValueOf(object)
+ return isEmptyValue(reflect.ValueOf(object))
+}
+// isEmptyValue gets whether the specified reflect.Value is considered empty or not.
+func isEmptyValue(objValue reflect.Value) bool {
+ if objValue.IsZero() {
+ return true
+ }
+ // Special cases of non-zero values that we consider empty
switch objValue.Kind() {
// collection types are empty when they have no element
+ // Note: array types are empty when they match their zero-initialized state.
case reflect.Chan, reflect.Map, reflect.Slice:
return objValue.Len() == 0
- // pointers are empty if nil or if the value they point to is empty
+ // non-nil pointers are empty if the value they point to is empty
case reflect.Ptr:
- if objValue.IsNil() {
- return true
- }
- deref := objValue.Elem().Interface()
- return isEmpty(deref)
- // for all other types, compare against the zero value
- // array types are empty when they match their zero-initialized state
- default:
- zero := reflect.Zero(objValue.Type())
- return reflect.DeepEqual(object, zero.Interface())
+ return isEmptyValue(objValue.Elem())
}
+ return false
}
-// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either
-// a slice or a channel with len == 0.
+// Empty asserts that the given value is "empty".
+//
+// [Zero values] are "empty".
+//
+// Arrays are "empty" if every element is the zero value of the type (stricter than "empty").
+//
+// Slices, maps and channels with zero length are "empty".
+//
+// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty".
//
// assert.Empty(t, obj)
+//
+// [Zero values]: https://go.dev/ref/spec#The_zero_value
func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
pass := isEmpty(object)
if !pass {
@@ -756,11 +797,9 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
}
return pass
-
}
-// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
-// a slice or a channel with len == 0.
+// NotEmpty asserts that the specified object is NOT [Empty].
//
// if assert.NotEmpty(t, obj) {
// assert.Equal(t, "two", obj[1])
@@ -775,7 +814,6 @@ func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
}
return pass
-
}
// getLen tries to get the length of an object.
@@ -819,7 +857,6 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
}
return true
-
}
// False asserts that the specified value is false.
@@ -834,7 +871,6 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
}
return true
-
}
// NotEqual asserts that the specified values are NOT equal.
@@ -857,7 +893,6 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{
}
return true
-
}
// NotEqualValues asserts that two objects are not equal even when converted to the same type
@@ -880,7 +915,6 @@ func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...inte
// return (true, false) if element was not found.
// return (true, true) if element was found.
func containsElement(list interface{}, element interface{}) (ok, found bool) {
-
listValue := reflect.ValueOf(list)
listType := reflect.TypeOf(list)
if listType == nil {
@@ -915,7 +949,6 @@ func containsElement(list interface{}, element interface{}) (ok, found bool) {
}
}
return true, false
-
}
// Contains asserts that the specified string, list(array, slice...) or map contains the
@@ -938,7 +971,6 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo
}
return true
-
}
// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the
@@ -961,14 +993,17 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{})
}
return true
-
}
-// Subset asserts that the specified list(array, slice...) or map contains all
-// elements given in the specified subset list(array, slice...) or map.
+// Subset asserts that the list (array, slice, or map) contains all elements
+// given in the subset (array, slice, or map).
+// Map elements are key-value pairs unless compared with an array or slice where
+// only the map key is evaluated.
//
// assert.Subset(t, [1, 2, 3], [1, 2])
// assert.Subset(t, {"x": 1, "y": 2}, {"x": 1})
+// assert.Subset(t, [1, 2, 3], {1: "one", 2: "two"})
+// assert.Subset(t, {"x": 1, "y": 2}, ["x"])
func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
if h, ok := t.(tHelper); ok {
h.Helper()
@@ -983,7 +1018,7 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
}
subsetKind := reflect.TypeOf(subset).Kind()
- if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map {
+ if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map {
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
}
@@ -1007,6 +1042,13 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
}
subsetList := reflect.ValueOf(subset)
+ if subsetKind == reflect.Map {
+ keys := make([]interface{}, subsetList.Len())
+ for idx, key := range subsetList.MapKeys() {
+ keys[idx] = key.Interface()
+ }
+ subsetList = reflect.ValueOf(keys)
+ }
for i := 0; i < subsetList.Len(); i++ {
element := subsetList.Index(i).Interface()
ok, found := containsElement(list, element)
@@ -1021,12 +1063,15 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
return true
}
-// NotSubset asserts that the specified list(array, slice...) or map does NOT
-// contain all elements given in the specified subset list(array, slice...) or
-// map.
+// NotSubset asserts that the list (array, slice, or map) does NOT contain all
+// elements given in the subset (array, slice, or map).
+// Map elements are key-value pairs unless compared with an array or slice where
+// only the map key is evaluated.
//
// assert.NotSubset(t, [1, 3, 4], [1, 2])
// assert.NotSubset(t, {"x": 1, "y": 2}, {"z": 3})
+// assert.NotSubset(t, [1, 3, 4], {1: "one", 2: "two"})
+// assert.NotSubset(t, {"x": 1, "y": 2}, ["z"])
func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
if h, ok := t.(tHelper); ok {
h.Helper()
@@ -1041,7 +1086,7 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
}
subsetKind := reflect.TypeOf(subset).Kind()
- if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map {
+ if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map {
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
}
@@ -1065,11 +1110,18 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
}
subsetList := reflect.ValueOf(subset)
+ if subsetKind == reflect.Map {
+ keys := make([]interface{}, subsetList.Len())
+ for idx, key := range subsetList.MapKeys() {
+ keys[idx] = key.Interface()
+ }
+ subsetList = reflect.ValueOf(keys)
+ }
for i := 0; i < subsetList.Len(); i++ {
element := subsetList.Index(i).Interface()
ok, found := containsElement(list, element)
if !ok {
- return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", list), msgAndArgs...)
+ return Fail(t, fmt.Sprintf("%q could not be applied builtin len()", list), msgAndArgs...)
}
if !found {
return true
@@ -1591,10 +1643,8 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
// Error asserts that a function returned an error (i.e. not `nil`).
//
-// actualObj, err := SomeFunction()
-// if assert.Error(t, err) {
-// assert.Equal(t, expectedError, err)
-// }
+// actualObj, err := SomeFunction()
+// assert.Error(t, err)
func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
if err == nil {
if h, ok := t.(tHelper); ok {
@@ -1667,7 +1717,6 @@ func matchRegexp(rx interface{}, str interface{}) bool {
default:
return r.MatchString(fmt.Sprint(v))
}
-
}
// Regexp asserts that a specified regexp matches a string.
@@ -1703,7 +1752,6 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf
}
return !match
-
}
// Zero asserts that i is the zero value for its type.
@@ -1814,6 +1862,11 @@ func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{
return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid json.\nJSON parsing error: '%s'", expected, err.Error()), msgAndArgs...)
}
+ // Shortcut if same bytes
+ if actual == expected {
+ return true
+ }
+
if err := json.Unmarshal([]byte(actual), &actualJSONAsInterface); err != nil {
return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid json.\nJSON parsing error: '%s'", actual, err.Error()), msgAndArgs...)
}
@@ -1832,6 +1885,11 @@ func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{
return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid yaml.\nYAML parsing error: '%s'", expected, err.Error()), msgAndArgs...)
}
+ // Shortcut if same bytes
+ if actual == expected {
+ return true
+ }
+
if err := yaml.Unmarshal([]byte(actual), &actualYAMLAsInterface); err != nil {
return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid yaml.\nYAML error: '%s'", actual, err.Error()), msgAndArgs...)
}
@@ -1933,6 +1991,7 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t
}
ch := make(chan bool, 1)
+ checkCond := func() { ch <- condition() }
timer := time.NewTimer(waitFor)
defer timer.Stop()
@@ -1940,18 +1999,23 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t
ticker := time.NewTicker(tick)
defer ticker.Stop()
- for tick := ticker.C; ; {
+ var tickC <-chan time.Time
+
+ // Check the condition once first on the initial call.
+ go checkCond()
+
+ for {
select {
case <-timer.C:
return Fail(t, "Condition never satisfied", msgAndArgs...)
- case <-tick:
- tick = nil
- go func() { ch <- condition() }()
+ case <-tickC:
+ tickC = nil
+ go checkCond()
case v := <-ch:
if v {
return true
}
- tick = ticker.C
+ tickC = ticker.C
}
}
}
@@ -1964,6 +2028,9 @@ type CollectT struct {
errors []error
}
+// Helper is like [testing.T.Helper] but does nothing.
+func (CollectT) Helper() {}
+
// Errorf collects the error.
func (c *CollectT) Errorf(format string, args ...interface{}) {
c.errors = append(c.errors, fmt.Errorf(format, args...))
@@ -2021,35 +2088,42 @@ func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time
var lastFinishedTickErrs []error
ch := make(chan *CollectT, 1)
+ checkCond := func() {
+ collect := new(CollectT)
+ defer func() {
+ ch <- collect
+ }()
+ condition(collect)
+ }
+
timer := time.NewTimer(waitFor)
defer timer.Stop()
ticker := time.NewTicker(tick)
defer ticker.Stop()
- for tick := ticker.C; ; {
+ var tickC <-chan time.Time
+
+ // Check the condition once first on the initial call.
+ go checkCond()
+
+ for {
select {
case <-timer.C:
for _, err := range lastFinishedTickErrs {
t.Errorf("%v", err)
}
return Fail(t, "Condition never satisfied", msgAndArgs...)
- case <-tick:
- tick = nil
- go func() {
- collect := new(CollectT)
- defer func() {
- ch <- collect
- }()
- condition(collect)
- }()
+ case <-tickC:
+ tickC = nil
+ go checkCond()
case collect := <-ch:
if !collect.failed() {
return true
}
// Keep the errors from the last ended condition, so that they can be copied to t if timeout is reached.
lastFinishedTickErrs = collect.errors
- tick = ticker.C
+ tickC = ticker.C
}
}
}
@@ -2064,6 +2138,7 @@ func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.D
}
ch := make(chan bool, 1)
+ checkCond := func() { ch <- condition() }
timer := time.NewTimer(waitFor)
defer timer.Stop()
@@ -2071,18 +2146,23 @@ func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.D
ticker := time.NewTicker(tick)
defer ticker.Stop()
- for tick := ticker.C; ; {
+ var tickC <-chan time.Time
+
+ // Check the condition once first on the initial call.
+ go checkCond()
+
+ for {
select {
case <-timer.C:
return true
- case <-tick:
- tick = nil
- go func() { ch <- condition() }()
+ case <-tickC:
+ tickC = nil
+ go checkCond()
case v := <-ch:
if v {
return Fail(t, "Condition satisfied", msgAndArgs...)
}
- tick = ticker.C
+ tickC = ticker.C
}
}
}
@@ -2100,9 +2180,12 @@ func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {
var expectedText string
if target != nil {
expectedText = target.Error()
+ if err == nil {
+ return Fail(t, fmt.Sprintf("Expected error with %q in chain but got nil.", expectedText), msgAndArgs...)
+ }
}
- chain := buildErrorChainString(err)
+ chain := buildErrorChainString(err, false)
return Fail(t, fmt.Sprintf("Target error should be in err chain:\n"+
"expected: %q\n"+
@@ -2125,7 +2208,7 @@ func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {
expectedText = target.Error()
}
- chain := buildErrorChainString(err)
+ chain := buildErrorChainString(err, false)
return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+
"found: %q\n"+
@@ -2143,11 +2226,17 @@ func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{
return true
}
- chain := buildErrorChainString(err)
+ expectedType := reflect.TypeOf(target).Elem().String()
+ if err == nil {
+ return Fail(t, fmt.Sprintf("An error is expected but got nil.\n"+
+ "expected: %s", expectedType), msgAndArgs...)
+ }
+
+ chain := buildErrorChainString(err, true)
return Fail(t, fmt.Sprintf("Should be in error chain:\n"+
- "expected: %q\n"+
- "in chain: %s", target, chain,
+ "expected: %s\n"+
+ "in chain: %s", expectedType, chain,
), msgAndArgs...)
}
@@ -2161,24 +2250,46 @@ func NotErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interfa
return true
}
- chain := buildErrorChainString(err)
+ chain := buildErrorChainString(err, true)
return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+
- "found: %q\n"+
- "in chain: %s", target, chain,
+ "found: %s\n"+
+ "in chain: %s", reflect.TypeOf(target).Elem().String(), chain,
), msgAndArgs...)
}
-func buildErrorChainString(err error) string {
+func unwrapAll(err error) (errs []error) {
+ errs = append(errs, err)
+ switch x := err.(type) {
+ case interface{ Unwrap() error }:
+ err = x.Unwrap()
+ if err == nil {
+ return
+ }
+ errs = append(errs, unwrapAll(err)...)
+ case interface{ Unwrap() []error }:
+ for _, err := range x.Unwrap() {
+ errs = append(errs, unwrapAll(err)...)
+ }
+ }
+ return
+}
+
+func buildErrorChainString(err error, withType bool) string {
if err == nil {
return ""
}
- e := errors.Unwrap(err)
- chain := fmt.Sprintf("%q", err.Error())
- for e != nil {
- chain += fmt.Sprintf("\n\t%q", e.Error())
- e = errors.Unwrap(e)
+ var chain string
+ errs := unwrapAll(err)
+ for i := range errs {
+ if i != 0 {
+ chain += "\n\t"
+ }
+ chain += fmt.Sprintf("%q", errs[i].Error())
+ if withType {
+ chain += fmt.Sprintf(" (%T)", errs[i])
+ }
}
return chain
}
diff --git a/vendor/github.com/stretchr/testify/assert/doc.go b/vendor/github.com/stretchr/testify/assert/doc.go
index 4953981d3..a0b953aa5 100644
--- a/vendor/github.com/stretchr/testify/assert/doc.go
+++ b/vendor/github.com/stretchr/testify/assert/doc.go
@@ -1,5 +1,9 @@
// Package assert provides a set of comprehensive testing tools for use with the normal Go testing system.
//
+// # Note
+//
+// All functions in this package return a bool value indicating whether the assertion has passed.
+//
// # Example Usage
//
// The following is a complete example using assert in a standard test function:
diff --git a/vendor/github.com/stretchr/testify/assert/http_assertions.go b/vendor/github.com/stretchr/testify/assert/http_assertions.go
index 861ed4b7c..5a6bb75f2 100644
--- a/vendor/github.com/stretchr/testify/assert/http_assertions.go
+++ b/vendor/github.com/stretchr/testify/assert/http_assertions.go
@@ -138,7 +138,7 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string,
contains := strings.Contains(body, fmt.Sprint(str))
if !contains {
- Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body), msgAndArgs...)
+ Fail(t, fmt.Sprintf("Expected response body for %q to contain %q but found %q", url+"?"+values.Encode(), str, body), msgAndArgs...)
}
return contains
@@ -158,7 +158,7 @@ func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url strin
contains := strings.Contains(body, fmt.Sprint(str))
if contains {
- Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body), msgAndArgs...)
+ Fail(t, fmt.Sprintf("Expected response body for %q to NOT contain %q but found %q", url+"?"+values.Encode(), str, body), msgAndArgs...)
}
return !contains
diff --git a/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go b/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go
index baa0cc7d7..5a74c4f4d 100644
--- a/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go
+++ b/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go
@@ -1,5 +1,4 @@
//go:build testify_yaml_custom && !testify_yaml_fail && !testify_yaml_default
-// +build testify_yaml_custom,!testify_yaml_fail,!testify_yaml_default
// Package yaml is an implementation of YAML functions that calls a pluggable implementation.
//
diff --git a/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go b/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go
index b83c6cf64..0bae80e34 100644
--- a/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go
+++ b/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go
@@ -1,5 +1,4 @@
//go:build !testify_yaml_fail && !testify_yaml_custom
-// +build !testify_yaml_fail,!testify_yaml_custom
// Package yaml is just an indirection to handle YAML deserialization.
//
diff --git a/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go b/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go
index e78f7dfe6..8041803fd 100644
--- a/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go
+++ b/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go
@@ -1,5 +1,4 @@
//go:build testify_yaml_fail && !testify_yaml_custom && !testify_yaml_default
-// +build testify_yaml_fail,!testify_yaml_custom,!testify_yaml_default
// Package yaml is an implementation of YAML functions that always fail.
//
diff --git a/vendor/go.yaml.in/yaml/v3/LICENSE b/vendor/go.yaml.in/yaml/v3/LICENSE
new file mode 100644
index 000000000..2683e4bb1
--- /dev/null
+++ b/vendor/go.yaml.in/yaml/v3/LICENSE
@@ -0,0 +1,50 @@
+
+This project is covered by two different licenses: MIT and Apache.
+
+#### MIT License ####
+
+The following files were ported to Go from C files of libyaml, and thus
+are still covered by their original MIT license, with the additional
+copyright staring in 2011 when the project was ported over:
+
+ apic.go emitterc.go parserc.go readerc.go scannerc.go
+ writerc.go yamlh.go yamlprivateh.go
+
+Copyright (c) 2006-2010 Kirill Simonov
+Copyright (c) 2006-2011 Kirill Simonov
+
+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.
+
+### Apache License ###
+
+All the remaining project files are covered by the Apache license:
+
+Copyright (c) 2011-2019 Canonical Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/vendor/go.yaml.in/yaml/v3/NOTICE b/vendor/go.yaml.in/yaml/v3/NOTICE
new file mode 100644
index 000000000..866d74a7a
--- /dev/null
+++ b/vendor/go.yaml.in/yaml/v3/NOTICE
@@ -0,0 +1,13 @@
+Copyright 2011-2016 Canonical Ltd.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/vendor/go.yaml.in/yaml/v3/README.md b/vendor/go.yaml.in/yaml/v3/README.md
new file mode 100644
index 000000000..15a85a635
--- /dev/null
+++ b/vendor/go.yaml.in/yaml/v3/README.md
@@ -0,0 +1,171 @@
+go.yaml.in/yaml
+===============
+
+YAML Support for the Go Language
+
+
+## Introduction
+
+The `yaml` package enables [Go](https://go.dev/) programs to comfortably encode
+and decode [YAML](https://yaml.org/) values.
+
+It was originally developed within [Canonical](https://www.canonical.com) as
+part of the [juju](https://juju.ubuntu.com) project, and is based on a pure Go
+port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) C library to
+parse and generate YAML data quickly and reliably.
+
+
+## Project Status
+
+This project started as a fork of the extremely popular [go-yaml](
+https://github.com/go-yaml/yaml/)
+project, and is being maintained by the official [YAML organization](
+https://github.com/yaml/).
+
+The YAML team took over ongoing maintenance and development of the project after
+discussion with go-yaml's author, @niemeyer, following his decision to
+[label the project repository as "unmaintained"](
+https://github.com/go-yaml/yaml/blob/944c86a7d2/README.md) in April 2025.
+
+We have put together a team of dedicated maintainers including representatives
+of go-yaml's most important downstream projects.
+
+We will strive to earn the trust of the various go-yaml forks to switch back to
+this repository as their upstream.
+
+Please [contact us](https://cloud-native.slack.com/archives/C08PPAT8PS7) if you
+would like to contribute or be involved.
+
+
+## Compatibility
+
+The `yaml` package supports most of YAML 1.2, but preserves some behavior from
+1.1 for backwards compatibility.
+
+Specifically, v3 of the `yaml` package:
+
+* Supports YAML 1.1 bools (`yes`/`no`, `on`/`off`) as long as they are being
+ decoded into a typed bool value.
+ Otherwise they behave as a string.
+ Booleans in YAML 1.2 are `true`/`false` only.
+* Supports octals encoded and decoded as `0777` per YAML 1.1, rather than
+ `0o777` as specified in YAML 1.2, because most parsers still use the old
+ format.
+ Octals in the `0o777` format are supported though, so new files work.
+* Does not support base-60 floats.
+ These are gone from YAML 1.2, and were actually never supported by this
+ package as it's clearly a poor choice.
+
+
+## Installation and Usage
+
+The import path for the package is *go.yaml.in/yaml/v3*.
+
+To install it, run:
+
+```bash
+go get go.yaml.in/yaml/v3
+```
+
+
+## API Documentation
+
+See:
+
+
+## API Stability
+
+The package API for yaml v3 will remain stable as described in [gopkg.in](
+https://gopkg.in).
+
+
+## Example
+
+```go
+package main
+
+import (
+ "fmt"
+ "log"
+
+ "go.yaml.in/yaml/v3"
+)
+
+var data = `
+a: Easy!
+b:
+ c: 2
+ d: [3, 4]
+`
+
+// Note: struct fields must be public in order for unmarshal to
+// correctly populate the data.
+type T struct {
+ A string
+ B struct {
+ RenamedC int `yaml:"c"`
+ D []int `yaml:",flow"`
+ }
+}
+
+func main() {
+ t := T{}
+
+ err := yaml.Unmarshal([]byte(data), &t)
+ if err != nil {
+ log.Fatalf("error: %v", err)
+ }
+ fmt.Printf("--- t:\n%v\n\n", t)
+
+ d, err := yaml.Marshal(&t)
+ if err != nil {
+ log.Fatalf("error: %v", err)
+ }
+ fmt.Printf("--- t dump:\n%s\n\n", string(d))
+
+ m := make(map[interface{}]interface{})
+
+ err = yaml.Unmarshal([]byte(data), &m)
+ if err != nil {
+ log.Fatalf("error: %v", err)
+ }
+ fmt.Printf("--- m:\n%v\n\n", m)
+
+ d, err = yaml.Marshal(&m)
+ if err != nil {
+ log.Fatalf("error: %v", err)
+ }
+ fmt.Printf("--- m dump:\n%s\n\n", string(d))
+}
+```
+
+This example will generate the following output:
+
+```
+--- t:
+{Easy! {2 [3 4]}}
+
+--- t dump:
+a: Easy!
+b:
+ c: 2
+ d: [3, 4]
+
+
+--- m:
+map[a:Easy! b:map[c:2 d:[3 4]]]
+
+--- m dump:
+a: Easy!
+b:
+ c: 2
+ d:
+ - 3
+ - 4
+```
+
+
+## License
+
+The yaml package is licensed under the MIT and Apache License 2.0 licenses.
+Please see the LICENSE file for details.
diff --git a/vendor/go.yaml.in/yaml/v3/apic.go b/vendor/go.yaml.in/yaml/v3/apic.go
new file mode 100644
index 000000000..05fd305da
--- /dev/null
+++ b/vendor/go.yaml.in/yaml/v3/apic.go
@@ -0,0 +1,747 @@
+//
+// Copyright (c) 2011-2019 Canonical Ltd
+// Copyright (c) 2006-2010 Kirill Simonov
+//
+// 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.
+
+package yaml
+
+import (
+ "io"
+)
+
+func yaml_insert_token(parser *yaml_parser_t, pos int, token *yaml_token_t) {
+ //fmt.Println("yaml_insert_token", "pos:", pos, "typ:", token.typ, "head:", parser.tokens_head, "len:", len(parser.tokens))
+
+ // Check if we can move the queue at the beginning of the buffer.
+ if parser.tokens_head > 0 && len(parser.tokens) == cap(parser.tokens) {
+ if parser.tokens_head != len(parser.tokens) {
+ copy(parser.tokens, parser.tokens[parser.tokens_head:])
+ }
+ parser.tokens = parser.tokens[:len(parser.tokens)-parser.tokens_head]
+ parser.tokens_head = 0
+ }
+ parser.tokens = append(parser.tokens, *token)
+ if pos < 0 {
+ return
+ }
+ copy(parser.tokens[parser.tokens_head+pos+1:], parser.tokens[parser.tokens_head+pos:])
+ parser.tokens[parser.tokens_head+pos] = *token
+}
+
+// Create a new parser object.
+func yaml_parser_initialize(parser *yaml_parser_t) bool {
+ *parser = yaml_parser_t{
+ raw_buffer: make([]byte, 0, input_raw_buffer_size),
+ buffer: make([]byte, 0, input_buffer_size),
+ }
+ return true
+}
+
+// Destroy a parser object.
+func yaml_parser_delete(parser *yaml_parser_t) {
+ *parser = yaml_parser_t{}
+}
+
+// String read handler.
+func yaml_string_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) {
+ if parser.input_pos == len(parser.input) {
+ return 0, io.EOF
+ }
+ n = copy(buffer, parser.input[parser.input_pos:])
+ parser.input_pos += n
+ return n, nil
+}
+
+// Reader read handler.
+func yaml_reader_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) {
+ return parser.input_reader.Read(buffer)
+}
+
+// Set a string input.
+func yaml_parser_set_input_string(parser *yaml_parser_t, input []byte) {
+ if parser.read_handler != nil {
+ panic("must set the input source only once")
+ }
+ parser.read_handler = yaml_string_read_handler
+ parser.input = input
+ parser.input_pos = 0
+}
+
+// Set a file input.
+func yaml_parser_set_input_reader(parser *yaml_parser_t, r io.Reader) {
+ if parser.read_handler != nil {
+ panic("must set the input source only once")
+ }
+ parser.read_handler = yaml_reader_read_handler
+ parser.input_reader = r
+}
+
+// Set the source encoding.
+func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) {
+ if parser.encoding != yaml_ANY_ENCODING {
+ panic("must set the encoding only once")
+ }
+ parser.encoding = encoding
+}
+
+// Create a new emitter object.
+func yaml_emitter_initialize(emitter *yaml_emitter_t) {
+ *emitter = yaml_emitter_t{
+ buffer: make([]byte, output_buffer_size),
+ raw_buffer: make([]byte, 0, output_raw_buffer_size),
+ states: make([]yaml_emitter_state_t, 0, initial_stack_size),
+ events: make([]yaml_event_t, 0, initial_queue_size),
+ best_width: -1,
+ }
+}
+
+// Destroy an emitter object.
+func yaml_emitter_delete(emitter *yaml_emitter_t) {
+ *emitter = yaml_emitter_t{}
+}
+
+// String write handler.
+func yaml_string_write_handler(emitter *yaml_emitter_t, buffer []byte) error {
+ *emitter.output_buffer = append(*emitter.output_buffer, buffer...)
+ return nil
+}
+
+// yaml_writer_write_handler uses emitter.output_writer to write the
+// emitted text.
+func yaml_writer_write_handler(emitter *yaml_emitter_t, buffer []byte) error {
+ _, err := emitter.output_writer.Write(buffer)
+ return err
+}
+
+// Set a string output.
+func yaml_emitter_set_output_string(emitter *yaml_emitter_t, output_buffer *[]byte) {
+ if emitter.write_handler != nil {
+ panic("must set the output target only once")
+ }
+ emitter.write_handler = yaml_string_write_handler
+ emitter.output_buffer = output_buffer
+}
+
+// Set a file output.
+func yaml_emitter_set_output_writer(emitter *yaml_emitter_t, w io.Writer) {
+ if emitter.write_handler != nil {
+ panic("must set the output target only once")
+ }
+ emitter.write_handler = yaml_writer_write_handler
+ emitter.output_writer = w
+}
+
+// Set the output encoding.
+func yaml_emitter_set_encoding(emitter *yaml_emitter_t, encoding yaml_encoding_t) {
+ if emitter.encoding != yaml_ANY_ENCODING {
+ panic("must set the output encoding only once")
+ }
+ emitter.encoding = encoding
+}
+
+// Set the canonical output style.
+func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) {
+ emitter.canonical = canonical
+}
+
+// Set the indentation increment.
+func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) {
+ if indent < 2 || indent > 9 {
+ indent = 2
+ }
+ emitter.best_indent = indent
+}
+
+// Set the preferred line width.
+func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) {
+ if width < 0 {
+ width = -1
+ }
+ emitter.best_width = width
+}
+
+// Set if unescaped non-ASCII characters are allowed.
+func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) {
+ emitter.unicode = unicode
+}
+
+// Set the preferred line break character.
+func yaml_emitter_set_break(emitter *yaml_emitter_t, line_break yaml_break_t) {
+ emitter.line_break = line_break
+}
+
+///*
+// * Destroy a token object.
+// */
+//
+//YAML_DECLARE(void)
+//yaml_token_delete(yaml_token_t *token)
+//{
+// assert(token); // Non-NULL token object expected.
+//
+// switch (token.type)
+// {
+// case YAML_TAG_DIRECTIVE_TOKEN:
+// yaml_free(token.data.tag_directive.handle);
+// yaml_free(token.data.tag_directive.prefix);
+// break;
+//
+// case YAML_ALIAS_TOKEN:
+// yaml_free(token.data.alias.value);
+// break;
+//
+// case YAML_ANCHOR_TOKEN:
+// yaml_free(token.data.anchor.value);
+// break;
+//
+// case YAML_TAG_TOKEN:
+// yaml_free(token.data.tag.handle);
+// yaml_free(token.data.tag.suffix);
+// break;
+//
+// case YAML_SCALAR_TOKEN:
+// yaml_free(token.data.scalar.value);
+// break;
+//
+// default:
+// break;
+// }
+//
+// memset(token, 0, sizeof(yaml_token_t));
+//}
+//
+///*
+// * Check if a string is a valid UTF-8 sequence.
+// *
+// * Check 'reader.c' for more details on UTF-8 encoding.
+// */
+//
+//static int
+//yaml_check_utf8(yaml_char_t *start, size_t length)
+//{
+// yaml_char_t *end = start+length;
+// yaml_char_t *pointer = start;
+//
+// while (pointer < end) {
+// unsigned char octet;
+// unsigned int width;
+// unsigned int value;
+// size_t k;
+//
+// octet = pointer[0];
+// width = (octet & 0x80) == 0x00 ? 1 :
+// (octet & 0xE0) == 0xC0 ? 2 :
+// (octet & 0xF0) == 0xE0 ? 3 :
+// (octet & 0xF8) == 0xF0 ? 4 : 0;
+// value = (octet & 0x80) == 0x00 ? octet & 0x7F :
+// (octet & 0xE0) == 0xC0 ? octet & 0x1F :
+// (octet & 0xF0) == 0xE0 ? octet & 0x0F :
+// (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0;
+// if (!width) return 0;
+// if (pointer+width > end) return 0;
+// for (k = 1; k < width; k ++) {
+// octet = pointer[k];
+// if ((octet & 0xC0) != 0x80) return 0;
+// value = (value << 6) + (octet & 0x3F);
+// }
+// if (!((width == 1) ||
+// (width == 2 && value >= 0x80) ||
+// (width == 3 && value >= 0x800) ||
+// (width == 4 && value >= 0x10000))) return 0;
+//
+// pointer += width;
+// }
+//
+// return 1;
+//}
+//
+
+// Create STREAM-START.
+func yaml_stream_start_event_initialize(event *yaml_event_t, encoding yaml_encoding_t) {
+ *event = yaml_event_t{
+ typ: yaml_STREAM_START_EVENT,
+ encoding: encoding,
+ }
+}
+
+// Create STREAM-END.
+func yaml_stream_end_event_initialize(event *yaml_event_t) {
+ *event = yaml_event_t{
+ typ: yaml_STREAM_END_EVENT,
+ }
+}
+
+// Create DOCUMENT-START.
+func yaml_document_start_event_initialize(
+ event *yaml_event_t,
+ version_directive *yaml_version_directive_t,
+ tag_directives []yaml_tag_directive_t,
+ implicit bool,
+) {
+ *event = yaml_event_t{
+ typ: yaml_DOCUMENT_START_EVENT,
+ version_directive: version_directive,
+ tag_directives: tag_directives,
+ implicit: implicit,
+ }
+}
+
+// Create DOCUMENT-END.
+func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) {
+ *event = yaml_event_t{
+ typ: yaml_DOCUMENT_END_EVENT,
+ implicit: implicit,
+ }
+}
+
+// Create ALIAS.
+func yaml_alias_event_initialize(event *yaml_event_t, anchor []byte) bool {
+ *event = yaml_event_t{
+ typ: yaml_ALIAS_EVENT,
+ anchor: anchor,
+ }
+ return true
+}
+
+// Create SCALAR.
+func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool {
+ *event = yaml_event_t{
+ typ: yaml_SCALAR_EVENT,
+ anchor: anchor,
+ tag: tag,
+ value: value,
+ implicit: plain_implicit,
+ quoted_implicit: quoted_implicit,
+ style: yaml_style_t(style),
+ }
+ return true
+}
+
+// Create SEQUENCE-START.
+func yaml_sequence_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_sequence_style_t) bool {
+ *event = yaml_event_t{
+ typ: yaml_SEQUENCE_START_EVENT,
+ anchor: anchor,
+ tag: tag,
+ implicit: implicit,
+ style: yaml_style_t(style),
+ }
+ return true
+}
+
+// Create SEQUENCE-END.
+func yaml_sequence_end_event_initialize(event *yaml_event_t) bool {
+ *event = yaml_event_t{
+ typ: yaml_SEQUENCE_END_EVENT,
+ }
+ return true
+}
+
+// Create MAPPING-START.
+func yaml_mapping_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_mapping_style_t) {
+ *event = yaml_event_t{
+ typ: yaml_MAPPING_START_EVENT,
+ anchor: anchor,
+ tag: tag,
+ implicit: implicit,
+ style: yaml_style_t(style),
+ }
+}
+
+// Create MAPPING-END.
+func yaml_mapping_end_event_initialize(event *yaml_event_t) {
+ *event = yaml_event_t{
+ typ: yaml_MAPPING_END_EVENT,
+ }
+}
+
+// Destroy an event object.
+func yaml_event_delete(event *yaml_event_t) {
+ *event = yaml_event_t{}
+}
+
+///*
+// * Create a document object.
+// */
+//
+//YAML_DECLARE(int)
+//yaml_document_initialize(document *yaml_document_t,
+// version_directive *yaml_version_directive_t,
+// tag_directives_start *yaml_tag_directive_t,
+// tag_directives_end *yaml_tag_directive_t,
+// start_implicit int, end_implicit int)
+//{
+// struct {
+// error yaml_error_type_t
+// } context
+// struct {
+// start *yaml_node_t
+// end *yaml_node_t
+// top *yaml_node_t
+// } nodes = { NULL, NULL, NULL }
+// version_directive_copy *yaml_version_directive_t = NULL
+// struct {
+// start *yaml_tag_directive_t
+// end *yaml_tag_directive_t
+// top *yaml_tag_directive_t
+// } tag_directives_copy = { NULL, NULL, NULL }
+// value yaml_tag_directive_t = { NULL, NULL }
+// mark yaml_mark_t = { 0, 0, 0 }
+//
+// assert(document) // Non-NULL document object is expected.
+// assert((tag_directives_start && tag_directives_end) ||
+// (tag_directives_start == tag_directives_end))
+// // Valid tag directives are expected.
+//
+// if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error
+//
+// if (version_directive) {
+// version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t))
+// if (!version_directive_copy) goto error
+// version_directive_copy.major = version_directive.major
+// version_directive_copy.minor = version_directive.minor
+// }
+//
+// if (tag_directives_start != tag_directives_end) {
+// tag_directive *yaml_tag_directive_t
+// if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE))
+// goto error
+// for (tag_directive = tag_directives_start
+// tag_directive != tag_directives_end; tag_directive ++) {
+// assert(tag_directive.handle)
+// assert(tag_directive.prefix)
+// if (!yaml_check_utf8(tag_directive.handle,
+// strlen((char *)tag_directive.handle)))
+// goto error
+// if (!yaml_check_utf8(tag_directive.prefix,
+// strlen((char *)tag_directive.prefix)))
+// goto error
+// value.handle = yaml_strdup(tag_directive.handle)
+// value.prefix = yaml_strdup(tag_directive.prefix)
+// if (!value.handle || !value.prefix) goto error
+// if (!PUSH(&context, tag_directives_copy, value))
+// goto error
+// value.handle = NULL
+// value.prefix = NULL
+// }
+// }
+//
+// DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy,
+// tag_directives_copy.start, tag_directives_copy.top,
+// start_implicit, end_implicit, mark, mark)
+//
+// return 1
+//
+//error:
+// STACK_DEL(&context, nodes)
+// yaml_free(version_directive_copy)
+// while (!STACK_EMPTY(&context, tag_directives_copy)) {
+// value yaml_tag_directive_t = POP(&context, tag_directives_copy)
+// yaml_free(value.handle)
+// yaml_free(value.prefix)
+// }
+// STACK_DEL(&context, tag_directives_copy)
+// yaml_free(value.handle)
+// yaml_free(value.prefix)
+//
+// return 0
+//}
+//
+///*
+// * Destroy a document object.
+// */
+//
+//YAML_DECLARE(void)
+//yaml_document_delete(document *yaml_document_t)
+//{
+// struct {
+// error yaml_error_type_t
+// } context
+// tag_directive *yaml_tag_directive_t
+//
+// context.error = YAML_NO_ERROR // Eliminate a compiler warning.
+//
+// assert(document) // Non-NULL document object is expected.
+//
+// while (!STACK_EMPTY(&context, document.nodes)) {
+// node yaml_node_t = POP(&context, document.nodes)
+// yaml_free(node.tag)
+// switch (node.type) {
+// case YAML_SCALAR_NODE:
+// yaml_free(node.data.scalar.value)
+// break
+// case YAML_SEQUENCE_NODE:
+// STACK_DEL(&context, node.data.sequence.items)
+// break
+// case YAML_MAPPING_NODE:
+// STACK_DEL(&context, node.data.mapping.pairs)
+// break
+// default:
+// assert(0) // Should not happen.
+// }
+// }
+// STACK_DEL(&context, document.nodes)
+//
+// yaml_free(document.version_directive)
+// for (tag_directive = document.tag_directives.start
+// tag_directive != document.tag_directives.end
+// tag_directive++) {
+// yaml_free(tag_directive.handle)
+// yaml_free(tag_directive.prefix)
+// }
+// yaml_free(document.tag_directives.start)
+//
+// memset(document, 0, sizeof(yaml_document_t))
+//}
+//
+///**
+// * Get a document node.
+// */
+//
+//YAML_DECLARE(yaml_node_t *)
+//yaml_document_get_node(document *yaml_document_t, index int)
+//{
+// assert(document) // Non-NULL document object is expected.
+//
+// if (index > 0 && document.nodes.start + index <= document.nodes.top) {
+// return document.nodes.start + index - 1
+// }
+// return NULL
+//}
+//
+///**
+// * Get the root object.
+// */
+//
+//YAML_DECLARE(yaml_node_t *)
+//yaml_document_get_root_node(document *yaml_document_t)
+//{
+// assert(document) // Non-NULL document object is expected.
+//
+// if (document.nodes.top != document.nodes.start) {
+// return document.nodes.start
+// }
+// return NULL
+//}
+//
+///*
+// * Add a scalar node to a document.
+// */
+//
+//YAML_DECLARE(int)
+//yaml_document_add_scalar(document *yaml_document_t,
+// tag *yaml_char_t, value *yaml_char_t, length int,
+// style yaml_scalar_style_t)
+//{
+// struct {
+// error yaml_error_type_t
+// } context
+// mark yaml_mark_t = { 0, 0, 0 }
+// tag_copy *yaml_char_t = NULL
+// value_copy *yaml_char_t = NULL
+// node yaml_node_t
+//
+// assert(document) // Non-NULL document object is expected.
+// assert(value) // Non-NULL value is expected.
+//
+// if (!tag) {
+// tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG
+// }
+//
+// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error
+// tag_copy = yaml_strdup(tag)
+// if (!tag_copy) goto error
+//
+// if (length < 0) {
+// length = strlen((char *)value)
+// }
+//
+// if (!yaml_check_utf8(value, length)) goto error
+// value_copy = yaml_malloc(length+1)
+// if (!value_copy) goto error
+// memcpy(value_copy, value, length)
+// value_copy[length] = '\0'
+//
+// SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark)
+// if (!PUSH(&context, document.nodes, node)) goto error
+//
+// return document.nodes.top - document.nodes.start
+//
+//error:
+// yaml_free(tag_copy)
+// yaml_free(value_copy)
+//
+// return 0
+//}
+//
+///*
+// * Add a sequence node to a document.
+// */
+//
+//YAML_DECLARE(int)
+//yaml_document_add_sequence(document *yaml_document_t,
+// tag *yaml_char_t, style yaml_sequence_style_t)
+//{
+// struct {
+// error yaml_error_type_t
+// } context
+// mark yaml_mark_t = { 0, 0, 0 }
+// tag_copy *yaml_char_t = NULL
+// struct {
+// start *yaml_node_item_t
+// end *yaml_node_item_t
+// top *yaml_node_item_t
+// } items = { NULL, NULL, NULL }
+// node yaml_node_t
+//
+// assert(document) // Non-NULL document object is expected.
+//
+// if (!tag) {
+// tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG
+// }
+//
+// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error
+// tag_copy = yaml_strdup(tag)
+// if (!tag_copy) goto error
+//
+// if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error
+//
+// SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end,
+// style, mark, mark)
+// if (!PUSH(&context, document.nodes, node)) goto error
+//
+// return document.nodes.top - document.nodes.start
+//
+//error:
+// STACK_DEL(&context, items)
+// yaml_free(tag_copy)
+//
+// return 0
+//}
+//
+///*
+// * Add a mapping node to a document.
+// */
+//
+//YAML_DECLARE(int)
+//yaml_document_add_mapping(document *yaml_document_t,
+// tag *yaml_char_t, style yaml_mapping_style_t)
+//{
+// struct {
+// error yaml_error_type_t
+// } context
+// mark yaml_mark_t = { 0, 0, 0 }
+// tag_copy *yaml_char_t = NULL
+// struct {
+// start *yaml_node_pair_t
+// end *yaml_node_pair_t
+// top *yaml_node_pair_t
+// } pairs = { NULL, NULL, NULL }
+// node yaml_node_t
+//
+// assert(document) // Non-NULL document object is expected.
+//
+// if (!tag) {
+// tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG
+// }
+//
+// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error
+// tag_copy = yaml_strdup(tag)
+// if (!tag_copy) goto error
+//
+// if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error
+//
+// MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end,
+// style, mark, mark)
+// if (!PUSH(&context, document.nodes, node)) goto error
+//
+// return document.nodes.top - document.nodes.start
+//
+//error:
+// STACK_DEL(&context, pairs)
+// yaml_free(tag_copy)
+//
+// return 0
+//}
+//
+///*
+// * Append an item to a sequence node.
+// */
+//
+//YAML_DECLARE(int)
+//yaml_document_append_sequence_item(document *yaml_document_t,
+// sequence int, item int)
+//{
+// struct {
+// error yaml_error_type_t
+// } context
+//
+// assert(document) // Non-NULL document is required.
+// assert(sequence > 0
+// && document.nodes.start + sequence <= document.nodes.top)
+// // Valid sequence id is required.
+// assert(document.nodes.start[sequence-1].type == YAML_SEQUENCE_NODE)
+// // A sequence node is required.
+// assert(item > 0 && document.nodes.start + item <= document.nodes.top)
+// // Valid item id is required.
+//
+// if (!PUSH(&context,
+// document.nodes.start[sequence-1].data.sequence.items, item))
+// return 0
+//
+// return 1
+//}
+//
+///*
+// * Append a pair of a key and a value to a mapping node.
+// */
+//
+//YAML_DECLARE(int)
+//yaml_document_append_mapping_pair(document *yaml_document_t,
+// mapping int, key int, value int)
+//{
+// struct {
+// error yaml_error_type_t
+// } context
+//
+// pair yaml_node_pair_t
+//
+// assert(document) // Non-NULL document is required.
+// assert(mapping > 0
+// && document.nodes.start + mapping <= document.nodes.top)
+// // Valid mapping id is required.
+// assert(document.nodes.start[mapping-1].type == YAML_MAPPING_NODE)
+// // A mapping node is required.
+// assert(key > 0 && document.nodes.start + key <= document.nodes.top)
+// // Valid key id is required.
+// assert(value > 0 && document.nodes.start + value <= document.nodes.top)
+// // Valid value id is required.
+//
+// pair.key = key
+// pair.value = value
+//
+// if (!PUSH(&context,
+// document.nodes.start[mapping-1].data.mapping.pairs, pair))
+// return 0
+//
+// return 1
+//}
+//
+//
diff --git a/vendor/go.yaml.in/yaml/v3/decode.go b/vendor/go.yaml.in/yaml/v3/decode.go
new file mode 100644
index 000000000..02e2b17bf
--- /dev/null
+++ b/vendor/go.yaml.in/yaml/v3/decode.go
@@ -0,0 +1,1018 @@
+//
+// Copyright (c) 2011-2019 Canonical Ltd
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package yaml
+
+import (
+ "encoding"
+ "encoding/base64"
+ "fmt"
+ "io"
+ "math"
+ "reflect"
+ "strconv"
+ "time"
+)
+
+// ----------------------------------------------------------------------------
+// Parser, produces a node tree out of a libyaml event stream.
+
+type parser struct {
+ parser yaml_parser_t
+ event yaml_event_t
+ doc *Node
+ anchors map[string]*Node
+ doneInit bool
+ textless bool
+}
+
+func newParser(b []byte) *parser {
+ p := parser{}
+ if !yaml_parser_initialize(&p.parser) {
+ panic("failed to initialize YAML emitter")
+ }
+ if len(b) == 0 {
+ b = []byte{'\n'}
+ }
+ yaml_parser_set_input_string(&p.parser, b)
+ return &p
+}
+
+func newParserFromReader(r io.Reader) *parser {
+ p := parser{}
+ if !yaml_parser_initialize(&p.parser) {
+ panic("failed to initialize YAML emitter")
+ }
+ yaml_parser_set_input_reader(&p.parser, r)
+ return &p
+}
+
+func (p *parser) init() {
+ if p.doneInit {
+ return
+ }
+ p.anchors = make(map[string]*Node)
+ p.expect(yaml_STREAM_START_EVENT)
+ p.doneInit = true
+}
+
+func (p *parser) destroy() {
+ if p.event.typ != yaml_NO_EVENT {
+ yaml_event_delete(&p.event)
+ }
+ yaml_parser_delete(&p.parser)
+}
+
+// expect consumes an event from the event stream and
+// checks that it's of the expected type.
+func (p *parser) expect(e yaml_event_type_t) {
+ if p.event.typ == yaml_NO_EVENT {
+ if !yaml_parser_parse(&p.parser, &p.event) {
+ p.fail()
+ }
+ }
+ if p.event.typ == yaml_STREAM_END_EVENT {
+ failf("attempted to go past the end of stream; corrupted value?")
+ }
+ if p.event.typ != e {
+ p.parser.problem = fmt.Sprintf("expected %s event but got %s", e, p.event.typ)
+ p.fail()
+ }
+ yaml_event_delete(&p.event)
+ p.event.typ = yaml_NO_EVENT
+}
+
+// peek peeks at the next event in the event stream,
+// puts the results into p.event and returns the event type.
+func (p *parser) peek() yaml_event_type_t {
+ if p.event.typ != yaml_NO_EVENT {
+ return p.event.typ
+ }
+ // It's curious choice from the underlying API to generally return a
+ // positive result on success, but on this case return true in an error
+ // scenario. This was the source of bugs in the past (issue #666).
+ if !yaml_parser_parse(&p.parser, &p.event) || p.parser.error != yaml_NO_ERROR {
+ p.fail()
+ }
+ return p.event.typ
+}
+
+func (p *parser) fail() {
+ var where string
+ var line int
+ if p.parser.context_mark.line != 0 {
+ line = p.parser.context_mark.line
+ // Scanner errors don't iterate line before returning error
+ if p.parser.error == yaml_SCANNER_ERROR {
+ line++
+ }
+ } else if p.parser.problem_mark.line != 0 {
+ line = p.parser.problem_mark.line
+ // Scanner errors don't iterate line before returning error
+ if p.parser.error == yaml_SCANNER_ERROR {
+ line++
+ }
+ }
+ if line != 0 {
+ where = "line " + strconv.Itoa(line) + ": "
+ }
+ var msg string
+ if len(p.parser.problem) > 0 {
+ msg = p.parser.problem
+ } else {
+ msg = "unknown problem parsing YAML content"
+ }
+ failf("%s%s", where, msg)
+}
+
+func (p *parser) anchor(n *Node, anchor []byte) {
+ if anchor != nil {
+ n.Anchor = string(anchor)
+ p.anchors[n.Anchor] = n
+ }
+}
+
+func (p *parser) parse() *Node {
+ p.init()
+ switch p.peek() {
+ case yaml_SCALAR_EVENT:
+ return p.scalar()
+ case yaml_ALIAS_EVENT:
+ return p.alias()
+ case yaml_MAPPING_START_EVENT:
+ return p.mapping()
+ case yaml_SEQUENCE_START_EVENT:
+ return p.sequence()
+ case yaml_DOCUMENT_START_EVENT:
+ return p.document()
+ case yaml_STREAM_END_EVENT:
+ // Happens when attempting to decode an empty buffer.
+ return nil
+ case yaml_TAIL_COMMENT_EVENT:
+ panic("internal error: unexpected tail comment event (please report)")
+ default:
+ panic("internal error: attempted to parse unknown event (please report): " + p.event.typ.String())
+ }
+}
+
+func (p *parser) node(kind Kind, defaultTag, tag, value string) *Node {
+ var style Style
+ if tag != "" && tag != "!" {
+ tag = shortTag(tag)
+ style = TaggedStyle
+ } else if defaultTag != "" {
+ tag = defaultTag
+ } else if kind == ScalarNode {
+ tag, _ = resolve("", value)
+ }
+ n := &Node{
+ Kind: kind,
+ Tag: tag,
+ Value: value,
+ Style: style,
+ }
+ if !p.textless {
+ n.Line = p.event.start_mark.line + 1
+ n.Column = p.event.start_mark.column + 1
+ n.HeadComment = string(p.event.head_comment)
+ n.LineComment = string(p.event.line_comment)
+ n.FootComment = string(p.event.foot_comment)
+ }
+ return n
+}
+
+func (p *parser) parseChild(parent *Node) *Node {
+ child := p.parse()
+ parent.Content = append(parent.Content, child)
+ return child
+}
+
+func (p *parser) document() *Node {
+ n := p.node(DocumentNode, "", "", "")
+ p.doc = n
+ p.expect(yaml_DOCUMENT_START_EVENT)
+ p.parseChild(n)
+ if p.peek() == yaml_DOCUMENT_END_EVENT {
+ n.FootComment = string(p.event.foot_comment)
+ }
+ p.expect(yaml_DOCUMENT_END_EVENT)
+ return n
+}
+
+func (p *parser) alias() *Node {
+ n := p.node(AliasNode, "", "", string(p.event.anchor))
+ n.Alias = p.anchors[n.Value]
+ if n.Alias == nil {
+ failf("unknown anchor '%s' referenced", n.Value)
+ }
+ p.expect(yaml_ALIAS_EVENT)
+ return n
+}
+
+func (p *parser) scalar() *Node {
+ var parsedStyle = p.event.scalar_style()
+ var nodeStyle Style
+ switch {
+ case parsedStyle&yaml_DOUBLE_QUOTED_SCALAR_STYLE != 0:
+ nodeStyle = DoubleQuotedStyle
+ case parsedStyle&yaml_SINGLE_QUOTED_SCALAR_STYLE != 0:
+ nodeStyle = SingleQuotedStyle
+ case parsedStyle&yaml_LITERAL_SCALAR_STYLE != 0:
+ nodeStyle = LiteralStyle
+ case parsedStyle&yaml_FOLDED_SCALAR_STYLE != 0:
+ nodeStyle = FoldedStyle
+ }
+ var nodeValue = string(p.event.value)
+ var nodeTag = string(p.event.tag)
+ var defaultTag string
+ if nodeStyle == 0 {
+ if nodeValue == "<<" {
+ defaultTag = mergeTag
+ }
+ } else {
+ defaultTag = strTag
+ }
+ n := p.node(ScalarNode, defaultTag, nodeTag, nodeValue)
+ n.Style |= nodeStyle
+ p.anchor(n, p.event.anchor)
+ p.expect(yaml_SCALAR_EVENT)
+ return n
+}
+
+func (p *parser) sequence() *Node {
+ n := p.node(SequenceNode, seqTag, string(p.event.tag), "")
+ if p.event.sequence_style()&yaml_FLOW_SEQUENCE_STYLE != 0 {
+ n.Style |= FlowStyle
+ }
+ p.anchor(n, p.event.anchor)
+ p.expect(yaml_SEQUENCE_START_EVENT)
+ for p.peek() != yaml_SEQUENCE_END_EVENT {
+ p.parseChild(n)
+ }
+ n.LineComment = string(p.event.line_comment)
+ n.FootComment = string(p.event.foot_comment)
+ p.expect(yaml_SEQUENCE_END_EVENT)
+ return n
+}
+
+func (p *parser) mapping() *Node {
+ n := p.node(MappingNode, mapTag, string(p.event.tag), "")
+ block := true
+ if p.event.mapping_style()&yaml_FLOW_MAPPING_STYLE != 0 {
+ block = false
+ n.Style |= FlowStyle
+ }
+ p.anchor(n, p.event.anchor)
+ p.expect(yaml_MAPPING_START_EVENT)
+ for p.peek() != yaml_MAPPING_END_EVENT {
+ k := p.parseChild(n)
+ if block && k.FootComment != "" {
+ // Must be a foot comment for the prior value when being dedented.
+ if len(n.Content) > 2 {
+ n.Content[len(n.Content)-3].FootComment = k.FootComment
+ k.FootComment = ""
+ }
+ }
+ v := p.parseChild(n)
+ if k.FootComment == "" && v.FootComment != "" {
+ k.FootComment = v.FootComment
+ v.FootComment = ""
+ }
+ if p.peek() == yaml_TAIL_COMMENT_EVENT {
+ if k.FootComment == "" {
+ k.FootComment = string(p.event.foot_comment)
+ }
+ p.expect(yaml_TAIL_COMMENT_EVENT)
+ }
+ }
+ n.LineComment = string(p.event.line_comment)
+ n.FootComment = string(p.event.foot_comment)
+ if n.Style&FlowStyle == 0 && n.FootComment != "" && len(n.Content) > 1 {
+ n.Content[len(n.Content)-2].FootComment = n.FootComment
+ n.FootComment = ""
+ }
+ p.expect(yaml_MAPPING_END_EVENT)
+ return n
+}
+
+// ----------------------------------------------------------------------------
+// Decoder, unmarshals a node into a provided value.
+
+type decoder struct {
+ doc *Node
+ aliases map[*Node]bool
+ terrors []string
+
+ stringMapType reflect.Type
+ generalMapType reflect.Type
+
+ knownFields bool
+ uniqueKeys bool
+ decodeCount int
+ aliasCount int
+ aliasDepth int
+
+ mergedFields map[interface{}]bool
+}
+
+var (
+ nodeType = reflect.TypeOf(Node{})
+ durationType = reflect.TypeOf(time.Duration(0))
+ stringMapType = reflect.TypeOf(map[string]interface{}{})
+ generalMapType = reflect.TypeOf(map[interface{}]interface{}{})
+ ifaceType = generalMapType.Elem()
+ timeType = reflect.TypeOf(time.Time{})
+ ptrTimeType = reflect.TypeOf(&time.Time{})
+)
+
+func newDecoder() *decoder {
+ d := &decoder{
+ stringMapType: stringMapType,
+ generalMapType: generalMapType,
+ uniqueKeys: true,
+ }
+ d.aliases = make(map[*Node]bool)
+ return d
+}
+
+func (d *decoder) terror(n *Node, tag string, out reflect.Value) {
+ if n.Tag != "" {
+ tag = n.Tag
+ }
+ value := n.Value
+ if tag != seqTag && tag != mapTag {
+ if len(value) > 10 {
+ value = " `" + value[:7] + "...`"
+ } else {
+ value = " `" + value + "`"
+ }
+ }
+ d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.Line, shortTag(tag), value, out.Type()))
+}
+
+func (d *decoder) callUnmarshaler(n *Node, u Unmarshaler) (good bool) {
+ err := u.UnmarshalYAML(n)
+ if e, ok := err.(*TypeError); ok {
+ d.terrors = append(d.terrors, e.Errors...)
+ return false
+ }
+ if err != nil {
+ fail(err)
+ }
+ return true
+}
+
+func (d *decoder) callObsoleteUnmarshaler(n *Node, u obsoleteUnmarshaler) (good bool) {
+ terrlen := len(d.terrors)
+ err := u.UnmarshalYAML(func(v interface{}) (err error) {
+ defer handleErr(&err)
+ d.unmarshal(n, reflect.ValueOf(v))
+ if len(d.terrors) > terrlen {
+ issues := d.terrors[terrlen:]
+ d.terrors = d.terrors[:terrlen]
+ return &TypeError{issues}
+ }
+ return nil
+ })
+ if e, ok := err.(*TypeError); ok {
+ d.terrors = append(d.terrors, e.Errors...)
+ return false
+ }
+ if err != nil {
+ fail(err)
+ }
+ return true
+}
+
+// d.prepare initializes and dereferences pointers and calls UnmarshalYAML
+// if a value is found to implement it.
+// It returns the initialized and dereferenced out value, whether
+// unmarshalling was already done by UnmarshalYAML, and if so whether
+// its types unmarshalled appropriately.
+//
+// If n holds a null value, prepare returns before doing anything.
+func (d *decoder) prepare(n *Node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) {
+ if n.ShortTag() == nullTag {
+ return out, false, false
+ }
+ again := true
+ for again {
+ again = false
+ if out.Kind() == reflect.Ptr {
+ if out.IsNil() {
+ out.Set(reflect.New(out.Type().Elem()))
+ }
+ out = out.Elem()
+ again = true
+ }
+ if out.CanAddr() {
+ outi := out.Addr().Interface()
+ if u, ok := outi.(Unmarshaler); ok {
+ good = d.callUnmarshaler(n, u)
+ return out, true, good
+ }
+ if u, ok := outi.(obsoleteUnmarshaler); ok {
+ good = d.callObsoleteUnmarshaler(n, u)
+ return out, true, good
+ }
+ }
+ }
+ return out, false, false
+}
+
+func (d *decoder) fieldByIndex(n *Node, v reflect.Value, index []int) (field reflect.Value) {
+ if n.ShortTag() == nullTag {
+ return reflect.Value{}
+ }
+ for _, num := range index {
+ for {
+ if v.Kind() == reflect.Ptr {
+ if v.IsNil() {
+ v.Set(reflect.New(v.Type().Elem()))
+ }
+ v = v.Elem()
+ continue
+ }
+ break
+ }
+ v = v.Field(num)
+ }
+ return v
+}
+
+const (
+ // 400,000 decode operations is ~500kb of dense object declarations, or
+ // ~5kb of dense object declarations with 10000% alias expansion
+ alias_ratio_range_low = 400000
+
+ // 4,000,000 decode operations is ~5MB of dense object declarations, or
+ // ~4.5MB of dense object declarations with 10% alias expansion
+ alias_ratio_range_high = 4000000
+
+ // alias_ratio_range is the range over which we scale allowed alias ratios
+ alias_ratio_range = float64(alias_ratio_range_high - alias_ratio_range_low)
+)
+
+func allowedAliasRatio(decodeCount int) float64 {
+ switch {
+ case decodeCount <= alias_ratio_range_low:
+ // allow 99% to come from alias expansion for small-to-medium documents
+ return 0.99
+ case decodeCount >= alias_ratio_range_high:
+ // allow 10% to come from alias expansion for very large documents
+ return 0.10
+ default:
+ // scale smoothly from 99% down to 10% over the range.
+ // this maps to 396,000 - 400,000 allowed alias-driven decodes over the range.
+ // 400,000 decode operations is ~100MB of allocations in worst-case scenarios (single-item maps).
+ return 0.99 - 0.89*(float64(decodeCount-alias_ratio_range_low)/alias_ratio_range)
+ }
+}
+
+func (d *decoder) unmarshal(n *Node, out reflect.Value) (good bool) {
+ d.decodeCount++
+ if d.aliasDepth > 0 {
+ d.aliasCount++
+ }
+ if d.aliasCount > 100 && d.decodeCount > 1000 && float64(d.aliasCount)/float64(d.decodeCount) > allowedAliasRatio(d.decodeCount) {
+ failf("document contains excessive aliasing")
+ }
+ if out.Type() == nodeType {
+ out.Set(reflect.ValueOf(n).Elem())
+ return true
+ }
+ switch n.Kind {
+ case DocumentNode:
+ return d.document(n, out)
+ case AliasNode:
+ return d.alias(n, out)
+ }
+ out, unmarshaled, good := d.prepare(n, out)
+ if unmarshaled {
+ return good
+ }
+ switch n.Kind {
+ case ScalarNode:
+ good = d.scalar(n, out)
+ case MappingNode:
+ good = d.mapping(n, out)
+ case SequenceNode:
+ good = d.sequence(n, out)
+ case 0:
+ if n.IsZero() {
+ return d.null(out)
+ }
+ fallthrough
+ default:
+ failf("cannot decode node with unknown kind %d", n.Kind)
+ }
+ return good
+}
+
+func (d *decoder) document(n *Node, out reflect.Value) (good bool) {
+ if len(n.Content) == 1 {
+ d.doc = n
+ d.unmarshal(n.Content[0], out)
+ return true
+ }
+ return false
+}
+
+func (d *decoder) alias(n *Node, out reflect.Value) (good bool) {
+ if d.aliases[n] {
+ // TODO this could actually be allowed in some circumstances.
+ failf("anchor '%s' value contains itself", n.Value)
+ }
+ d.aliases[n] = true
+ d.aliasDepth++
+ good = d.unmarshal(n.Alias, out)
+ d.aliasDepth--
+ delete(d.aliases, n)
+ return good
+}
+
+var zeroValue reflect.Value
+
+func resetMap(out reflect.Value) {
+ for _, k := range out.MapKeys() {
+ out.SetMapIndex(k, zeroValue)
+ }
+}
+
+func (d *decoder) null(out reflect.Value) bool {
+ if out.CanAddr() {
+ switch out.Kind() {
+ case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
+ out.Set(reflect.Zero(out.Type()))
+ return true
+ }
+ }
+ return false
+}
+
+func (d *decoder) scalar(n *Node, out reflect.Value) bool {
+ var tag string
+ var resolved interface{}
+ if n.indicatedString() {
+ tag = strTag
+ resolved = n.Value
+ } else {
+ tag, resolved = resolve(n.Tag, n.Value)
+ if tag == binaryTag {
+ data, err := base64.StdEncoding.DecodeString(resolved.(string))
+ if err != nil {
+ failf("!!binary value contains invalid base64 data")
+ }
+ resolved = string(data)
+ }
+ }
+ if resolved == nil {
+ return d.null(out)
+ }
+ if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() {
+ // We've resolved to exactly the type we want, so use that.
+ out.Set(resolvedv)
+ return true
+ }
+ // Perhaps we can use the value as a TextUnmarshaler to
+ // set its value.
+ if out.CanAddr() {
+ u, ok := out.Addr().Interface().(encoding.TextUnmarshaler)
+ if ok {
+ var text []byte
+ if tag == binaryTag {
+ text = []byte(resolved.(string))
+ } else {
+ // We let any value be unmarshaled into TextUnmarshaler.
+ // That might be more lax than we'd like, but the
+ // TextUnmarshaler itself should bowl out any dubious values.
+ text = []byte(n.Value)
+ }
+ err := u.UnmarshalText(text)
+ if err != nil {
+ fail(err)
+ }
+ return true
+ }
+ }
+ switch out.Kind() {
+ case reflect.String:
+ if tag == binaryTag {
+ out.SetString(resolved.(string))
+ return true
+ }
+ out.SetString(n.Value)
+ return true
+ case reflect.Interface:
+ out.Set(reflect.ValueOf(resolved))
+ return true
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ // This used to work in v2, but it's very unfriendly.
+ isDuration := out.Type() == durationType
+
+ switch resolved := resolved.(type) {
+ case int:
+ if !isDuration && !out.OverflowInt(int64(resolved)) {
+ out.SetInt(int64(resolved))
+ return true
+ }
+ case int64:
+ if !isDuration && !out.OverflowInt(resolved) {
+ out.SetInt(resolved)
+ return true
+ }
+ case uint64:
+ if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
+ out.SetInt(int64(resolved))
+ return true
+ }
+ case float64:
+ if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
+ out.SetInt(int64(resolved))
+ return true
+ }
+ case string:
+ if out.Type() == durationType {
+ d, err := time.ParseDuration(resolved)
+ if err == nil {
+ out.SetInt(int64(d))
+ return true
+ }
+ }
+ }
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ switch resolved := resolved.(type) {
+ case int:
+ if resolved >= 0 && !out.OverflowUint(uint64(resolved)) {
+ out.SetUint(uint64(resolved))
+ return true
+ }
+ case int64:
+ if resolved >= 0 && !out.OverflowUint(uint64(resolved)) {
+ out.SetUint(uint64(resolved))
+ return true
+ }
+ case uint64:
+ if !out.OverflowUint(uint64(resolved)) {
+ out.SetUint(uint64(resolved))
+ return true
+ }
+ case float64:
+ if resolved <= math.MaxUint64 && !out.OverflowUint(uint64(resolved)) {
+ out.SetUint(uint64(resolved))
+ return true
+ }
+ }
+ case reflect.Bool:
+ switch resolved := resolved.(type) {
+ case bool:
+ out.SetBool(resolved)
+ return true
+ case string:
+ // This offers some compatibility with the 1.1 spec (https://yaml.org/type/bool.html).
+ // It only works if explicitly attempting to unmarshal into a typed bool value.
+ switch resolved {
+ case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON":
+ out.SetBool(true)
+ return true
+ case "n", "N", "no", "No", "NO", "off", "Off", "OFF":
+ out.SetBool(false)
+ return true
+ }
+ }
+ case reflect.Float32, reflect.Float64:
+ switch resolved := resolved.(type) {
+ case int:
+ out.SetFloat(float64(resolved))
+ return true
+ case int64:
+ out.SetFloat(float64(resolved))
+ return true
+ case uint64:
+ out.SetFloat(float64(resolved))
+ return true
+ case float64:
+ out.SetFloat(resolved)
+ return true
+ }
+ case reflect.Struct:
+ if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() {
+ out.Set(resolvedv)
+ return true
+ }
+ case reflect.Ptr:
+ panic("yaml internal error: please report the issue")
+ }
+ d.terror(n, tag, out)
+ return false
+}
+
+func settableValueOf(i interface{}) reflect.Value {
+ v := reflect.ValueOf(i)
+ sv := reflect.New(v.Type()).Elem()
+ sv.Set(v)
+ return sv
+}
+
+func (d *decoder) sequence(n *Node, out reflect.Value) (good bool) {
+ l := len(n.Content)
+
+ var iface reflect.Value
+ switch out.Kind() {
+ case reflect.Slice:
+ out.Set(reflect.MakeSlice(out.Type(), l, l))
+ case reflect.Array:
+ if l != out.Len() {
+ failf("invalid array: want %d elements but got %d", out.Len(), l)
+ }
+ case reflect.Interface:
+ // No type hints. Will have to use a generic sequence.
+ iface = out
+ out = settableValueOf(make([]interface{}, l))
+ default:
+ d.terror(n, seqTag, out)
+ return false
+ }
+ et := out.Type().Elem()
+
+ j := 0
+ for i := 0; i < l; i++ {
+ e := reflect.New(et).Elem()
+ if ok := d.unmarshal(n.Content[i], e); ok {
+ out.Index(j).Set(e)
+ j++
+ }
+ }
+ if out.Kind() != reflect.Array {
+ out.Set(out.Slice(0, j))
+ }
+ if iface.IsValid() {
+ iface.Set(out)
+ }
+ return true
+}
+
+func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
+ l := len(n.Content)
+ if d.uniqueKeys {
+ nerrs := len(d.terrors)
+ for i := 0; i < l; i += 2 {
+ ni := n.Content[i]
+ for j := i + 2; j < l; j += 2 {
+ nj := n.Content[j]
+ if ni.Kind == nj.Kind && ni.Value == nj.Value {
+ d.terrors = append(d.terrors, fmt.Sprintf("line %d: mapping key %#v already defined at line %d", nj.Line, nj.Value, ni.Line))
+ }
+ }
+ }
+ if len(d.terrors) > nerrs {
+ return false
+ }
+ }
+ switch out.Kind() {
+ case reflect.Struct:
+ return d.mappingStruct(n, out)
+ case reflect.Map:
+ // okay
+ case reflect.Interface:
+ iface := out
+ if isStringMap(n) {
+ out = reflect.MakeMap(d.stringMapType)
+ } else {
+ out = reflect.MakeMap(d.generalMapType)
+ }
+ iface.Set(out)
+ default:
+ d.terror(n, mapTag, out)
+ return false
+ }
+
+ outt := out.Type()
+ kt := outt.Key()
+ et := outt.Elem()
+
+ stringMapType := d.stringMapType
+ generalMapType := d.generalMapType
+ if outt.Elem() == ifaceType {
+ if outt.Key().Kind() == reflect.String {
+ d.stringMapType = outt
+ } else if outt.Key() == ifaceType {
+ d.generalMapType = outt
+ }
+ }
+
+ mergedFields := d.mergedFields
+ d.mergedFields = nil
+
+ var mergeNode *Node
+
+ mapIsNew := false
+ if out.IsNil() {
+ out.Set(reflect.MakeMap(outt))
+ mapIsNew = true
+ }
+ for i := 0; i < l; i += 2 {
+ if isMerge(n.Content[i]) {
+ mergeNode = n.Content[i+1]
+ continue
+ }
+ k := reflect.New(kt).Elem()
+ if d.unmarshal(n.Content[i], k) {
+ if mergedFields != nil {
+ ki := k.Interface()
+ if d.getPossiblyUnhashableKey(mergedFields, ki) {
+ continue
+ }
+ d.setPossiblyUnhashableKey(mergedFields, ki, true)
+ }
+ kkind := k.Kind()
+ if kkind == reflect.Interface {
+ kkind = k.Elem().Kind()
+ }
+ if kkind == reflect.Map || kkind == reflect.Slice {
+ failf("invalid map key: %#v", k.Interface())
+ }
+ e := reflect.New(et).Elem()
+ if d.unmarshal(n.Content[i+1], e) || n.Content[i+1].ShortTag() == nullTag && (mapIsNew || !out.MapIndex(k).IsValid()) {
+ out.SetMapIndex(k, e)
+ }
+ }
+ }
+
+ d.mergedFields = mergedFields
+ if mergeNode != nil {
+ d.merge(n, mergeNode, out)
+ }
+
+ d.stringMapType = stringMapType
+ d.generalMapType = generalMapType
+ return true
+}
+
+func isStringMap(n *Node) bool {
+ if n.Kind != MappingNode {
+ return false
+ }
+ l := len(n.Content)
+ for i := 0; i < l; i += 2 {
+ shortTag := n.Content[i].ShortTag()
+ if shortTag != strTag && shortTag != mergeTag {
+ return false
+ }
+ }
+ return true
+}
+
+func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
+ sinfo, err := getStructInfo(out.Type())
+ if err != nil {
+ panic(err)
+ }
+
+ var inlineMap reflect.Value
+ var elemType reflect.Type
+ if sinfo.InlineMap != -1 {
+ inlineMap = out.Field(sinfo.InlineMap)
+ elemType = inlineMap.Type().Elem()
+ }
+
+ for _, index := range sinfo.InlineUnmarshalers {
+ field := d.fieldByIndex(n, out, index)
+ d.prepare(n, field)
+ }
+
+ mergedFields := d.mergedFields
+ d.mergedFields = nil
+ var mergeNode *Node
+ var doneFields []bool
+ if d.uniqueKeys {
+ doneFields = make([]bool, len(sinfo.FieldsList))
+ }
+ name := settableValueOf("")
+ l := len(n.Content)
+ for i := 0; i < l; i += 2 {
+ ni := n.Content[i]
+ if isMerge(ni) {
+ mergeNode = n.Content[i+1]
+ continue
+ }
+ if !d.unmarshal(ni, name) {
+ continue
+ }
+ sname := name.String()
+ if mergedFields != nil {
+ if mergedFields[sname] {
+ continue
+ }
+ mergedFields[sname] = true
+ }
+ if info, ok := sinfo.FieldsMap[sname]; ok {
+ if d.uniqueKeys {
+ if doneFields[info.Id] {
+ d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type()))
+ continue
+ }
+ doneFields[info.Id] = true
+ }
+ var field reflect.Value
+ if info.Inline == nil {
+ field = out.Field(info.Num)
+ } else {
+ field = d.fieldByIndex(n, out, info.Inline)
+ }
+ d.unmarshal(n.Content[i+1], field)
+ } else if sinfo.InlineMap != -1 {
+ if inlineMap.IsNil() {
+ inlineMap.Set(reflect.MakeMap(inlineMap.Type()))
+ }
+ value := reflect.New(elemType).Elem()
+ d.unmarshal(n.Content[i+1], value)
+ inlineMap.SetMapIndex(name, value)
+ } else if d.knownFields {
+ d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type()))
+ }
+ }
+
+ d.mergedFields = mergedFields
+ if mergeNode != nil {
+ d.merge(n, mergeNode, out)
+ }
+ return true
+}
+
+func failWantMap() {
+ failf("map merge requires map or sequence of maps as the value")
+}
+
+func (d *decoder) setPossiblyUnhashableKey(m map[interface{}]bool, key interface{}, value bool) {
+ defer func() {
+ if err := recover(); err != nil {
+ failf("%v", err)
+ }
+ }()
+ m[key] = value
+}
+
+func (d *decoder) getPossiblyUnhashableKey(m map[interface{}]bool, key interface{}) bool {
+ defer func() {
+ if err := recover(); err != nil {
+ failf("%v", err)
+ }
+ }()
+ return m[key]
+}
+
+func (d *decoder) merge(parent *Node, merge *Node, out reflect.Value) {
+ mergedFields := d.mergedFields
+ if mergedFields == nil {
+ d.mergedFields = make(map[interface{}]bool)
+ for i := 0; i < len(parent.Content); i += 2 {
+ k := reflect.New(ifaceType).Elem()
+ if d.unmarshal(parent.Content[i], k) {
+ d.setPossiblyUnhashableKey(d.mergedFields, k.Interface(), true)
+ }
+ }
+ }
+
+ switch merge.Kind {
+ case MappingNode:
+ d.unmarshal(merge, out)
+ case AliasNode:
+ if merge.Alias != nil && merge.Alias.Kind != MappingNode {
+ failWantMap()
+ }
+ d.unmarshal(merge, out)
+ case SequenceNode:
+ for i := 0; i < len(merge.Content); i++ {
+ ni := merge.Content[i]
+ if ni.Kind == AliasNode {
+ if ni.Alias != nil && ni.Alias.Kind != MappingNode {
+ failWantMap()
+ }
+ } else if ni.Kind != MappingNode {
+ failWantMap()
+ }
+ d.unmarshal(ni, out)
+ }
+ default:
+ failWantMap()
+ }
+
+ d.mergedFields = mergedFields
+}
+
+func isMerge(n *Node) bool {
+ return n.Kind == ScalarNode && n.Value == "<<" && (n.Tag == "" || n.Tag == "!" || shortTag(n.Tag) == mergeTag)
+}
diff --git a/vendor/go.yaml.in/yaml/v3/emitterc.go b/vendor/go.yaml.in/yaml/v3/emitterc.go
new file mode 100644
index 000000000..ab4e03ba7
--- /dev/null
+++ b/vendor/go.yaml.in/yaml/v3/emitterc.go
@@ -0,0 +1,2054 @@
+//
+// Copyright (c) 2011-2019 Canonical Ltd
+// Copyright (c) 2006-2010 Kirill Simonov
+//
+// 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.
+
+package yaml
+
+import (
+ "bytes"
+ "fmt"
+)
+
+// Flush the buffer if needed.
+func flush(emitter *yaml_emitter_t) bool {
+ if emitter.buffer_pos+5 >= len(emitter.buffer) {
+ return yaml_emitter_flush(emitter)
+ }
+ return true
+}
+
+// Put a character to the output buffer.
+func put(emitter *yaml_emitter_t, value byte) bool {
+ if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) {
+ return false
+ }
+ emitter.buffer[emitter.buffer_pos] = value
+ emitter.buffer_pos++
+ emitter.column++
+ return true
+}
+
+// Put a line break to the output buffer.
+func put_break(emitter *yaml_emitter_t) bool {
+ if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) {
+ return false
+ }
+ switch emitter.line_break {
+ case yaml_CR_BREAK:
+ emitter.buffer[emitter.buffer_pos] = '\r'
+ emitter.buffer_pos += 1
+ case yaml_LN_BREAK:
+ emitter.buffer[emitter.buffer_pos] = '\n'
+ emitter.buffer_pos += 1
+ case yaml_CRLN_BREAK:
+ emitter.buffer[emitter.buffer_pos+0] = '\r'
+ emitter.buffer[emitter.buffer_pos+1] = '\n'
+ emitter.buffer_pos += 2
+ default:
+ panic("unknown line break setting")
+ }
+ if emitter.column == 0 {
+ emitter.space_above = true
+ }
+ emitter.column = 0
+ emitter.line++
+ // [Go] Do this here and below and drop from everywhere else (see commented lines).
+ emitter.indention = true
+ return true
+}
+
+// Copy a character from a string into buffer.
+func write(emitter *yaml_emitter_t, s []byte, i *int) bool {
+ if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) {
+ return false
+ }
+ p := emitter.buffer_pos
+ w := width(s[*i])
+ switch w {
+ case 4:
+ emitter.buffer[p+3] = s[*i+3]
+ fallthrough
+ case 3:
+ emitter.buffer[p+2] = s[*i+2]
+ fallthrough
+ case 2:
+ emitter.buffer[p+1] = s[*i+1]
+ fallthrough
+ case 1:
+ emitter.buffer[p+0] = s[*i+0]
+ default:
+ panic("unknown character width")
+ }
+ emitter.column++
+ emitter.buffer_pos += w
+ *i += w
+ return true
+}
+
+// Write a whole string into buffer.
+func write_all(emitter *yaml_emitter_t, s []byte) bool {
+ for i := 0; i < len(s); {
+ if !write(emitter, s, &i) {
+ return false
+ }
+ }
+ return true
+}
+
+// Copy a line break character from a string into buffer.
+func write_break(emitter *yaml_emitter_t, s []byte, i *int) bool {
+ if s[*i] == '\n' {
+ if !put_break(emitter) {
+ return false
+ }
+ *i++
+ } else {
+ if !write(emitter, s, i) {
+ return false
+ }
+ if emitter.column == 0 {
+ emitter.space_above = true
+ }
+ emitter.column = 0
+ emitter.line++
+ // [Go] Do this here and above and drop from everywhere else (see commented lines).
+ emitter.indention = true
+ }
+ return true
+}
+
+// Set an emitter error and return false.
+func yaml_emitter_set_emitter_error(emitter *yaml_emitter_t, problem string) bool {
+ emitter.error = yaml_EMITTER_ERROR
+ emitter.problem = problem
+ return false
+}
+
+// Emit an event.
+func yaml_emitter_emit(emitter *yaml_emitter_t, event *yaml_event_t) bool {
+ emitter.events = append(emitter.events, *event)
+ for !yaml_emitter_need_more_events(emitter) {
+ event := &emitter.events[emitter.events_head]
+ if !yaml_emitter_analyze_event(emitter, event) {
+ return false
+ }
+ if !yaml_emitter_state_machine(emitter, event) {
+ return false
+ }
+ yaml_event_delete(event)
+ emitter.events_head++
+ }
+ return true
+}
+
+// Check if we need to accumulate more events before emitting.
+//
+// We accumulate extra
+// - 1 event for DOCUMENT-START
+// - 2 events for SEQUENCE-START
+// - 3 events for MAPPING-START
+func yaml_emitter_need_more_events(emitter *yaml_emitter_t) bool {
+ if emitter.events_head == len(emitter.events) {
+ return true
+ }
+ var accumulate int
+ switch emitter.events[emitter.events_head].typ {
+ case yaml_DOCUMENT_START_EVENT:
+ accumulate = 1
+ break
+ case yaml_SEQUENCE_START_EVENT:
+ accumulate = 2
+ break
+ case yaml_MAPPING_START_EVENT:
+ accumulate = 3
+ break
+ default:
+ return false
+ }
+ if len(emitter.events)-emitter.events_head > accumulate {
+ return false
+ }
+ var level int
+ for i := emitter.events_head; i < len(emitter.events); i++ {
+ switch emitter.events[i].typ {
+ case yaml_STREAM_START_EVENT, yaml_DOCUMENT_START_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT:
+ level++
+ case yaml_STREAM_END_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_END_EVENT, yaml_MAPPING_END_EVENT:
+ level--
+ }
+ if level == 0 {
+ return false
+ }
+ }
+ return true
+}
+
+// Append a directive to the directives stack.
+func yaml_emitter_append_tag_directive(emitter *yaml_emitter_t, value *yaml_tag_directive_t, allow_duplicates bool) bool {
+ for i := 0; i < len(emitter.tag_directives); i++ {
+ if bytes.Equal(value.handle, emitter.tag_directives[i].handle) {
+ if allow_duplicates {
+ return true
+ }
+ return yaml_emitter_set_emitter_error(emitter, "duplicate %TAG directive")
+ }
+ }
+
+ // [Go] Do we actually need to copy this given garbage collection
+ // and the lack of deallocating destructors?
+ tag_copy := yaml_tag_directive_t{
+ handle: make([]byte, len(value.handle)),
+ prefix: make([]byte, len(value.prefix)),
+ }
+ copy(tag_copy.handle, value.handle)
+ copy(tag_copy.prefix, value.prefix)
+ emitter.tag_directives = append(emitter.tag_directives, tag_copy)
+ return true
+}
+
+// Increase the indentation level.
+func yaml_emitter_increase_indent_compact(emitter *yaml_emitter_t, flow, indentless bool, compact_seq bool) bool {
+ emitter.indents = append(emitter.indents, emitter.indent)
+ if emitter.indent < 0 {
+ if flow {
+ emitter.indent = emitter.best_indent
+ } else {
+ emitter.indent = 0
+ }
+ } else if !indentless {
+ // [Go] This was changed so that indentations are more regular.
+ if emitter.states[len(emitter.states)-1] == yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE {
+ // The first indent inside a sequence will just skip the "- " indicator.
+ emitter.indent += 2
+ } else {
+ // Everything else aligns to the chosen indentation.
+ emitter.indent = emitter.best_indent * ((emitter.indent + emitter.best_indent) / emitter.best_indent)
+ if compact_seq {
+ // The value compact_seq passed in is almost always set to `false` when this function is called,
+ // except when we are dealing with sequence nodes. So this gets triggered to subtract 2 only when we
+ // are increasing the indent to account for sequence nodes, which will be correct because we need to
+ // subtract 2 to account for the - at the beginning of the sequence node.
+ emitter.indent = emitter.indent - 2
+ }
+ }
+ }
+ return true
+}
+
+// State dispatcher.
+func yaml_emitter_state_machine(emitter *yaml_emitter_t, event *yaml_event_t) bool {
+ switch emitter.state {
+ default:
+ case yaml_EMIT_STREAM_START_STATE:
+ return yaml_emitter_emit_stream_start(emitter, event)
+
+ case yaml_EMIT_FIRST_DOCUMENT_START_STATE:
+ return yaml_emitter_emit_document_start(emitter, event, true)
+
+ case yaml_EMIT_DOCUMENT_START_STATE:
+ return yaml_emitter_emit_document_start(emitter, event, false)
+
+ case yaml_EMIT_DOCUMENT_CONTENT_STATE:
+ return yaml_emitter_emit_document_content(emitter, event)
+
+ case yaml_EMIT_DOCUMENT_END_STATE:
+ return yaml_emitter_emit_document_end(emitter, event)
+
+ case yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE:
+ return yaml_emitter_emit_flow_sequence_item(emitter, event, true, false)
+
+ case yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE:
+ return yaml_emitter_emit_flow_sequence_item(emitter, event, false, true)
+
+ case yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE:
+ return yaml_emitter_emit_flow_sequence_item(emitter, event, false, false)
+
+ case yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE:
+ return yaml_emitter_emit_flow_mapping_key(emitter, event, true, false)
+
+ case yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE:
+ return yaml_emitter_emit_flow_mapping_key(emitter, event, false, true)
+
+ case yaml_EMIT_FLOW_MAPPING_KEY_STATE:
+ return yaml_emitter_emit_flow_mapping_key(emitter, event, false, false)
+
+ case yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE:
+ return yaml_emitter_emit_flow_mapping_value(emitter, event, true)
+
+ case yaml_EMIT_FLOW_MAPPING_VALUE_STATE:
+ return yaml_emitter_emit_flow_mapping_value(emitter, event, false)
+
+ case yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE:
+ return yaml_emitter_emit_block_sequence_item(emitter, event, true)
+
+ case yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE:
+ return yaml_emitter_emit_block_sequence_item(emitter, event, false)
+
+ case yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE:
+ return yaml_emitter_emit_block_mapping_key(emitter, event, true)
+
+ case yaml_EMIT_BLOCK_MAPPING_KEY_STATE:
+ return yaml_emitter_emit_block_mapping_key(emitter, event, false)
+
+ case yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE:
+ return yaml_emitter_emit_block_mapping_value(emitter, event, true)
+
+ case yaml_EMIT_BLOCK_MAPPING_VALUE_STATE:
+ return yaml_emitter_emit_block_mapping_value(emitter, event, false)
+
+ case yaml_EMIT_END_STATE:
+ return yaml_emitter_set_emitter_error(emitter, "expected nothing after STREAM-END")
+ }
+ panic("invalid emitter state")
+}
+
+// Expect STREAM-START.
+func yaml_emitter_emit_stream_start(emitter *yaml_emitter_t, event *yaml_event_t) bool {
+ if event.typ != yaml_STREAM_START_EVENT {
+ return yaml_emitter_set_emitter_error(emitter, "expected STREAM-START")
+ }
+ if emitter.encoding == yaml_ANY_ENCODING {
+ emitter.encoding = event.encoding
+ if emitter.encoding == yaml_ANY_ENCODING {
+ emitter.encoding = yaml_UTF8_ENCODING
+ }
+ }
+ if emitter.best_indent < 2 || emitter.best_indent > 9 {
+ emitter.best_indent = 2
+ }
+ if emitter.best_width >= 0 && emitter.best_width <= emitter.best_indent*2 {
+ emitter.best_width = 80
+ }
+ if emitter.best_width < 0 {
+ emitter.best_width = 1<<31 - 1
+ }
+ if emitter.line_break == yaml_ANY_BREAK {
+ emitter.line_break = yaml_LN_BREAK
+ }
+
+ emitter.indent = -1
+ emitter.line = 0
+ emitter.column = 0
+ emitter.whitespace = true
+ emitter.indention = true
+ emitter.space_above = true
+ emitter.foot_indent = -1
+
+ if emitter.encoding != yaml_UTF8_ENCODING {
+ if !yaml_emitter_write_bom(emitter) {
+ return false
+ }
+ }
+ emitter.state = yaml_EMIT_FIRST_DOCUMENT_START_STATE
+ return true
+}
+
+// Expect DOCUMENT-START or STREAM-END.
+func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {
+
+ if event.typ == yaml_DOCUMENT_START_EVENT {
+
+ if event.version_directive != nil {
+ if !yaml_emitter_analyze_version_directive(emitter, event.version_directive) {
+ return false
+ }
+ }
+
+ for i := 0; i < len(event.tag_directives); i++ {
+ tag_directive := &event.tag_directives[i]
+ if !yaml_emitter_analyze_tag_directive(emitter, tag_directive) {
+ return false
+ }
+ if !yaml_emitter_append_tag_directive(emitter, tag_directive, false) {
+ return false
+ }
+ }
+
+ for i := 0; i < len(default_tag_directives); i++ {
+ tag_directive := &default_tag_directives[i]
+ if !yaml_emitter_append_tag_directive(emitter, tag_directive, true) {
+ return false
+ }
+ }
+
+ implicit := event.implicit
+ if !first || emitter.canonical {
+ implicit = false
+ }
+
+ if emitter.open_ended && (event.version_directive != nil || len(event.tag_directives) > 0) {
+ if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) {
+ return false
+ }
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ }
+
+ if event.version_directive != nil {
+ implicit = false
+ if !yaml_emitter_write_indicator(emitter, []byte("%YAML"), true, false, false) {
+ return false
+ }
+ if !yaml_emitter_write_indicator(emitter, []byte("1.1"), true, false, false) {
+ return false
+ }
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ }
+
+ if len(event.tag_directives) > 0 {
+ implicit = false
+ for i := 0; i < len(event.tag_directives); i++ {
+ tag_directive := &event.tag_directives[i]
+ if !yaml_emitter_write_indicator(emitter, []byte("%TAG"), true, false, false) {
+ return false
+ }
+ if !yaml_emitter_write_tag_handle(emitter, tag_directive.handle) {
+ return false
+ }
+ if !yaml_emitter_write_tag_content(emitter, tag_directive.prefix, true) {
+ return false
+ }
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ }
+ }
+
+ if yaml_emitter_check_empty_document(emitter) {
+ implicit = false
+ }
+ if !implicit {
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ if !yaml_emitter_write_indicator(emitter, []byte("---"), true, false, false) {
+ return false
+ }
+ if emitter.canonical || true {
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ }
+ }
+
+ if len(emitter.head_comment) > 0 {
+ if !yaml_emitter_process_head_comment(emitter) {
+ return false
+ }
+ if !put_break(emitter) {
+ return false
+ }
+ }
+
+ emitter.state = yaml_EMIT_DOCUMENT_CONTENT_STATE
+ return true
+ }
+
+ if event.typ == yaml_STREAM_END_EVENT {
+ if emitter.open_ended {
+ if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) {
+ return false
+ }
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ }
+ if !yaml_emitter_flush(emitter) {
+ return false
+ }
+ emitter.state = yaml_EMIT_END_STATE
+ return true
+ }
+
+ return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-START or STREAM-END")
+}
+
+// yaml_emitter_increase_indent preserves the original signature and delegates to
+// yaml_emitter_increase_indent_compact without compact-sequence indentation
+func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool) bool {
+ return yaml_emitter_increase_indent_compact(emitter, flow, indentless, false)
+}
+
+// yaml_emitter_process_line_comment preserves the original signature and delegates to
+// yaml_emitter_process_line_comment_linebreak passing false for linebreak
+func yaml_emitter_process_line_comment(emitter *yaml_emitter_t) bool {
+ return yaml_emitter_process_line_comment_linebreak(emitter, false)
+}
+
+// Expect the root node.
+func yaml_emitter_emit_document_content(emitter *yaml_emitter_t, event *yaml_event_t) bool {
+ emitter.states = append(emitter.states, yaml_EMIT_DOCUMENT_END_STATE)
+
+ if !yaml_emitter_process_head_comment(emitter) {
+ return false
+ }
+ if !yaml_emitter_emit_node(emitter, event, true, false, false, false) {
+ return false
+ }
+ if !yaml_emitter_process_line_comment(emitter) {
+ return false
+ }
+ if !yaml_emitter_process_foot_comment(emitter) {
+ return false
+ }
+ return true
+}
+
+// Expect DOCUMENT-END.
+func yaml_emitter_emit_document_end(emitter *yaml_emitter_t, event *yaml_event_t) bool {
+ if event.typ != yaml_DOCUMENT_END_EVENT {
+ return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-END")
+ }
+ // [Go] Force document foot separation.
+ emitter.foot_indent = 0
+ if !yaml_emitter_process_foot_comment(emitter) {
+ return false
+ }
+ emitter.foot_indent = -1
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ if !event.implicit {
+ // [Go] Allocate the slice elsewhere.
+ if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) {
+ return false
+ }
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ }
+ if !yaml_emitter_flush(emitter) {
+ return false
+ }
+ emitter.state = yaml_EMIT_DOCUMENT_START_STATE
+ emitter.tag_directives = emitter.tag_directives[:0]
+ return true
+}
+
+// Expect a flow item node.
+func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first, trail bool) bool {
+ if first {
+ if !yaml_emitter_write_indicator(emitter, []byte{'['}, true, true, false) {
+ return false
+ }
+ if !yaml_emitter_increase_indent(emitter, true, false) {
+ return false
+ }
+ emitter.flow_level++
+ }
+
+ if event.typ == yaml_SEQUENCE_END_EVENT {
+ if emitter.canonical && !first && !trail {
+ if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
+ return false
+ }
+ }
+ emitter.flow_level--
+ emitter.indent = emitter.indents[len(emitter.indents)-1]
+ emitter.indents = emitter.indents[:len(emitter.indents)-1]
+ if emitter.column == 0 || emitter.canonical && !first {
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ }
+ if !yaml_emitter_write_indicator(emitter, []byte{']'}, false, false, false) {
+ return false
+ }
+ if !yaml_emitter_process_line_comment(emitter) {
+ return false
+ }
+ if !yaml_emitter_process_foot_comment(emitter) {
+ return false
+ }
+ emitter.state = emitter.states[len(emitter.states)-1]
+ emitter.states = emitter.states[:len(emitter.states)-1]
+
+ return true
+ }
+
+ if !first && !trail {
+ if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
+ return false
+ }
+ }
+
+ if !yaml_emitter_process_head_comment(emitter) {
+ return false
+ }
+ if emitter.column == 0 {
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ }
+
+ if emitter.canonical || emitter.column > emitter.best_width {
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ }
+ if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 {
+ emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE)
+ } else {
+ emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE)
+ }
+ if !yaml_emitter_emit_node(emitter, event, false, true, false, false) {
+ return false
+ }
+ if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 {
+ if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
+ return false
+ }
+ }
+ if !yaml_emitter_process_line_comment(emitter) {
+ return false
+ }
+ if !yaml_emitter_process_foot_comment(emitter) {
+ return false
+ }
+ return true
+}
+
+// Expect a flow key node.
+func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first, trail bool) bool {
+ if first {
+ if !yaml_emitter_write_indicator(emitter, []byte{'{'}, true, true, false) {
+ return false
+ }
+ if !yaml_emitter_increase_indent(emitter, true, false) {
+ return false
+ }
+ emitter.flow_level++
+ }
+
+ if event.typ == yaml_MAPPING_END_EVENT {
+ if (emitter.canonical || len(emitter.head_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0) && !first && !trail {
+ if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
+ return false
+ }
+ }
+ if !yaml_emitter_process_head_comment(emitter) {
+ return false
+ }
+ emitter.flow_level--
+ emitter.indent = emitter.indents[len(emitter.indents)-1]
+ emitter.indents = emitter.indents[:len(emitter.indents)-1]
+ if emitter.canonical && !first {
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ }
+ if !yaml_emitter_write_indicator(emitter, []byte{'}'}, false, false, false) {
+ return false
+ }
+ if !yaml_emitter_process_line_comment(emitter) {
+ return false
+ }
+ if !yaml_emitter_process_foot_comment(emitter) {
+ return false
+ }
+ emitter.state = emitter.states[len(emitter.states)-1]
+ emitter.states = emitter.states[:len(emitter.states)-1]
+ return true
+ }
+
+ if !first && !trail {
+ if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
+ return false
+ }
+ }
+
+ if !yaml_emitter_process_head_comment(emitter) {
+ return false
+ }
+
+ if emitter.column == 0 {
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ }
+
+ if emitter.canonical || emitter.column > emitter.best_width {
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ }
+
+ if !emitter.canonical && yaml_emitter_check_simple_key(emitter) {
+ emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE)
+ return yaml_emitter_emit_node(emitter, event, false, false, true, true)
+ }
+ if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, false) {
+ return false
+ }
+ emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_VALUE_STATE)
+ return yaml_emitter_emit_node(emitter, event, false, false, true, false)
+}
+
+// Expect a flow value node.
+func yaml_emitter_emit_flow_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool {
+ if simple {
+ if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) {
+ return false
+ }
+ } else {
+ if emitter.canonical || emitter.column > emitter.best_width {
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ }
+ if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, false) {
+ return false
+ }
+ }
+ if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 {
+ emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE)
+ } else {
+ emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE)
+ }
+ if !yaml_emitter_emit_node(emitter, event, false, false, true, false) {
+ return false
+ }
+ if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 {
+ if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
+ return false
+ }
+ }
+ if !yaml_emitter_process_line_comment(emitter) {
+ return false
+ }
+ if !yaml_emitter_process_foot_comment(emitter) {
+ return false
+ }
+ return true
+}
+
+// Expect a block item node.
+func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {
+ if first {
+ // emitter.mapping context tells us if we are currently in a mapping context.
+ // emiiter.column tells us which column we are in in the yaml output. 0 is the first char of the column.
+ // emitter.indentation tells us if the last character was an indentation character.
+ // emitter.compact_sequence_indent tells us if '- ' is considered part of the indentation for sequence elements.
+ // So, `seq` means that we are in a mapping context, and we are either at the first char of the column or
+ // the last character was not an indentation character, and we consider '- ' part of the indentation
+ // for sequence elements.
+ seq := emitter.mapping_context && (emitter.column == 0 || !emitter.indention) &&
+ emitter.compact_sequence_indent
+ if !yaml_emitter_increase_indent_compact(emitter, false, false, seq) {
+ return false
+ }
+ }
+ if event.typ == yaml_SEQUENCE_END_EVENT {
+ emitter.indent = emitter.indents[len(emitter.indents)-1]
+ emitter.indents = emitter.indents[:len(emitter.indents)-1]
+ emitter.state = emitter.states[len(emitter.states)-1]
+ emitter.states = emitter.states[:len(emitter.states)-1]
+ return true
+ }
+ if !yaml_emitter_process_head_comment(emitter) {
+ return false
+ }
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ if !yaml_emitter_write_indicator(emitter, []byte{'-'}, true, false, true) {
+ return false
+ }
+ emitter.states = append(emitter.states, yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE)
+ if !yaml_emitter_emit_node(emitter, event, false, true, false, false) {
+ return false
+ }
+ if !yaml_emitter_process_line_comment(emitter) {
+ return false
+ }
+ if !yaml_emitter_process_foot_comment(emitter) {
+ return false
+ }
+ return true
+}
+
+// Expect a block key node.
+func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {
+ if first {
+ if !yaml_emitter_increase_indent(emitter, false, false) {
+ return false
+ }
+ }
+ if !yaml_emitter_process_head_comment(emitter) {
+ return false
+ }
+ if event.typ == yaml_MAPPING_END_EVENT {
+ emitter.indent = emitter.indents[len(emitter.indents)-1]
+ emitter.indents = emitter.indents[:len(emitter.indents)-1]
+ emitter.state = emitter.states[len(emitter.states)-1]
+ emitter.states = emitter.states[:len(emitter.states)-1]
+ return true
+ }
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ if len(emitter.line_comment) > 0 {
+ // [Go] A line comment was provided for the key. That's unusual as the
+ // scanner associates line comments with the value. Either way,
+ // save the line comment and render it appropriately later.
+ emitter.key_line_comment = emitter.line_comment
+ emitter.line_comment = nil
+ }
+ if yaml_emitter_check_simple_key(emitter) {
+ emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE)
+ return yaml_emitter_emit_node(emitter, event, false, false, true, true)
+ }
+ if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, true) {
+ return false
+ }
+ emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_VALUE_STATE)
+ return yaml_emitter_emit_node(emitter, event, false, false, true, false)
+}
+
+// Expect a block value node.
+func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool {
+ if simple {
+ if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) {
+ return false
+ }
+ } else {
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, true) {
+ return false
+ }
+ }
+ if len(emitter.key_line_comment) > 0 {
+ // [Go] Line comments are generally associated with the value, but when there's
+ // no value on the same line as a mapping key they end up attached to the
+ // key itself.
+ if event.typ == yaml_SCALAR_EVENT {
+ if len(emitter.line_comment) == 0 {
+ // A scalar is coming and it has no line comments by itself yet,
+ // so just let it handle the line comment as usual. If it has a
+ // line comment, we can't have both so the one from the key is lost.
+ emitter.line_comment = emitter.key_line_comment
+ emitter.key_line_comment = nil
+ }
+ } else if event.sequence_style() != yaml_FLOW_SEQUENCE_STYLE && (event.typ == yaml_MAPPING_START_EVENT || event.typ == yaml_SEQUENCE_START_EVENT) {
+ // An indented block follows, so write the comment right now.
+ emitter.line_comment, emitter.key_line_comment = emitter.key_line_comment, emitter.line_comment
+ if !yaml_emitter_process_line_comment(emitter) {
+ return false
+ }
+ emitter.line_comment, emitter.key_line_comment = emitter.key_line_comment, emitter.line_comment
+ }
+ }
+ emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_KEY_STATE)
+ if !yaml_emitter_emit_node(emitter, event, false, false, true, false) {
+ return false
+ }
+ if !yaml_emitter_process_line_comment(emitter) {
+ return false
+ }
+ if !yaml_emitter_process_foot_comment(emitter) {
+ return false
+ }
+ return true
+}
+
+func yaml_emitter_silent_nil_event(emitter *yaml_emitter_t, event *yaml_event_t) bool {
+ return event.typ == yaml_SCALAR_EVENT && event.implicit && !emitter.canonical && len(emitter.scalar_data.value) == 0
+}
+
+// Expect a node.
+func yaml_emitter_emit_node(emitter *yaml_emitter_t, event *yaml_event_t,
+ root bool, sequence bool, mapping bool, simple_key bool) bool {
+
+ emitter.root_context = root
+ emitter.sequence_context = sequence
+ emitter.mapping_context = mapping
+ emitter.simple_key_context = simple_key
+
+ switch event.typ {
+ case yaml_ALIAS_EVENT:
+ return yaml_emitter_emit_alias(emitter, event)
+ case yaml_SCALAR_EVENT:
+ return yaml_emitter_emit_scalar(emitter, event)
+ case yaml_SEQUENCE_START_EVENT:
+ return yaml_emitter_emit_sequence_start(emitter, event)
+ case yaml_MAPPING_START_EVENT:
+ return yaml_emitter_emit_mapping_start(emitter, event)
+ default:
+ return yaml_emitter_set_emitter_error(emitter,
+ fmt.Sprintf("expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS, but got %v", event.typ))
+ }
+}
+
+// Expect ALIAS.
+func yaml_emitter_emit_alias(emitter *yaml_emitter_t, event *yaml_event_t) bool {
+ if !yaml_emitter_process_anchor(emitter) {
+ return false
+ }
+ emitter.state = emitter.states[len(emitter.states)-1]
+ emitter.states = emitter.states[:len(emitter.states)-1]
+ return true
+}
+
+// Expect SCALAR.
+func yaml_emitter_emit_scalar(emitter *yaml_emitter_t, event *yaml_event_t) bool {
+ if !yaml_emitter_select_scalar_style(emitter, event) {
+ return false
+ }
+ if !yaml_emitter_process_anchor(emitter) {
+ return false
+ }
+ if !yaml_emitter_process_tag(emitter) {
+ return false
+ }
+ if !yaml_emitter_increase_indent(emitter, true, false) {
+ return false
+ }
+ if !yaml_emitter_process_scalar(emitter) {
+ return false
+ }
+ emitter.indent = emitter.indents[len(emitter.indents)-1]
+ emitter.indents = emitter.indents[:len(emitter.indents)-1]
+ emitter.state = emitter.states[len(emitter.states)-1]
+ emitter.states = emitter.states[:len(emitter.states)-1]
+ return true
+}
+
+// Expect SEQUENCE-START.
+func yaml_emitter_emit_sequence_start(emitter *yaml_emitter_t, event *yaml_event_t) bool {
+ if !yaml_emitter_process_anchor(emitter) {
+ return false
+ }
+ if !yaml_emitter_process_tag(emitter) {
+ return false
+ }
+ if emitter.flow_level > 0 || emitter.canonical || event.sequence_style() == yaml_FLOW_SEQUENCE_STYLE ||
+ yaml_emitter_check_empty_sequence(emitter) {
+ emitter.state = yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE
+ } else {
+ emitter.state = yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE
+ }
+ return true
+}
+
+// Expect MAPPING-START.
+func yaml_emitter_emit_mapping_start(emitter *yaml_emitter_t, event *yaml_event_t) bool {
+ if !yaml_emitter_process_anchor(emitter) {
+ return false
+ }
+ if !yaml_emitter_process_tag(emitter) {
+ return false
+ }
+ if emitter.flow_level > 0 || emitter.canonical || event.mapping_style() == yaml_FLOW_MAPPING_STYLE ||
+ yaml_emitter_check_empty_mapping(emitter) {
+ emitter.state = yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE
+ } else {
+ emitter.state = yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE
+ }
+ return true
+}
+
+// Check if the document content is an empty scalar.
+func yaml_emitter_check_empty_document(emitter *yaml_emitter_t) bool {
+ return false // [Go] Huh?
+}
+
+// Check if the next events represent an empty sequence.
+func yaml_emitter_check_empty_sequence(emitter *yaml_emitter_t) bool {
+ if len(emitter.events)-emitter.events_head < 2 {
+ return false
+ }
+ return emitter.events[emitter.events_head].typ == yaml_SEQUENCE_START_EVENT &&
+ emitter.events[emitter.events_head+1].typ == yaml_SEQUENCE_END_EVENT
+}
+
+// Check if the next events represent an empty mapping.
+func yaml_emitter_check_empty_mapping(emitter *yaml_emitter_t) bool {
+ if len(emitter.events)-emitter.events_head < 2 {
+ return false
+ }
+ return emitter.events[emitter.events_head].typ == yaml_MAPPING_START_EVENT &&
+ emitter.events[emitter.events_head+1].typ == yaml_MAPPING_END_EVENT
+}
+
+// Check if the next node can be expressed as a simple key.
+func yaml_emitter_check_simple_key(emitter *yaml_emitter_t) bool {
+ length := 0
+ switch emitter.events[emitter.events_head].typ {
+ case yaml_ALIAS_EVENT:
+ length += len(emitter.anchor_data.anchor)
+ case yaml_SCALAR_EVENT:
+ if emitter.scalar_data.multiline {
+ return false
+ }
+ length += len(emitter.anchor_data.anchor) +
+ len(emitter.tag_data.handle) +
+ len(emitter.tag_data.suffix) +
+ len(emitter.scalar_data.value)
+ case yaml_SEQUENCE_START_EVENT:
+ if !yaml_emitter_check_empty_sequence(emitter) {
+ return false
+ }
+ length += len(emitter.anchor_data.anchor) +
+ len(emitter.tag_data.handle) +
+ len(emitter.tag_data.suffix)
+ case yaml_MAPPING_START_EVENT:
+ if !yaml_emitter_check_empty_mapping(emitter) {
+ return false
+ }
+ length += len(emitter.anchor_data.anchor) +
+ len(emitter.tag_data.handle) +
+ len(emitter.tag_data.suffix)
+ default:
+ return false
+ }
+ return length <= 128
+}
+
+// Determine an acceptable scalar style.
+func yaml_emitter_select_scalar_style(emitter *yaml_emitter_t, event *yaml_event_t) bool {
+
+ no_tag := len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0
+ if no_tag && !event.implicit && !event.quoted_implicit {
+ return yaml_emitter_set_emitter_error(emitter, "neither tag nor implicit flags are specified")
+ }
+
+ style := event.scalar_style()
+ if style == yaml_ANY_SCALAR_STYLE {
+ style = yaml_PLAIN_SCALAR_STYLE
+ }
+ if emitter.canonical {
+ style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
+ }
+ if emitter.simple_key_context && emitter.scalar_data.multiline {
+ style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
+ }
+
+ if style == yaml_PLAIN_SCALAR_STYLE {
+ if emitter.flow_level > 0 && !emitter.scalar_data.flow_plain_allowed ||
+ emitter.flow_level == 0 && !emitter.scalar_data.block_plain_allowed {
+ style = yaml_SINGLE_QUOTED_SCALAR_STYLE
+ }
+ if len(emitter.scalar_data.value) == 0 && (emitter.flow_level > 0 || emitter.simple_key_context) {
+ style = yaml_SINGLE_QUOTED_SCALAR_STYLE
+ }
+ if no_tag && !event.implicit {
+ style = yaml_SINGLE_QUOTED_SCALAR_STYLE
+ }
+ }
+ if style == yaml_SINGLE_QUOTED_SCALAR_STYLE {
+ if !emitter.scalar_data.single_quoted_allowed {
+ style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
+ }
+ }
+ if style == yaml_LITERAL_SCALAR_STYLE || style == yaml_FOLDED_SCALAR_STYLE {
+ if !emitter.scalar_data.block_allowed || emitter.flow_level > 0 || emitter.simple_key_context {
+ style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
+ }
+ }
+
+ if no_tag && !event.quoted_implicit && style != yaml_PLAIN_SCALAR_STYLE {
+ emitter.tag_data.handle = []byte{'!'}
+ }
+ emitter.scalar_data.style = style
+ return true
+}
+
+// Write an anchor.
+func yaml_emitter_process_anchor(emitter *yaml_emitter_t) bool {
+ if emitter.anchor_data.anchor == nil {
+ return true
+ }
+ c := []byte{'&'}
+ if emitter.anchor_data.alias {
+ c[0] = '*'
+ }
+ if !yaml_emitter_write_indicator(emitter, c, true, false, false) {
+ return false
+ }
+ return yaml_emitter_write_anchor(emitter, emitter.anchor_data.anchor)
+}
+
+// Write a tag.
+func yaml_emitter_process_tag(emitter *yaml_emitter_t) bool {
+ if len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0 {
+ return true
+ }
+ if len(emitter.tag_data.handle) > 0 {
+ if !yaml_emitter_write_tag_handle(emitter, emitter.tag_data.handle) {
+ return false
+ }
+ if len(emitter.tag_data.suffix) > 0 {
+ if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) {
+ return false
+ }
+ }
+ } else {
+ // [Go] Allocate these slices elsewhere.
+ if !yaml_emitter_write_indicator(emitter, []byte("!<"), true, false, false) {
+ return false
+ }
+ if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) {
+ return false
+ }
+ if !yaml_emitter_write_indicator(emitter, []byte{'>'}, false, false, false) {
+ return false
+ }
+ }
+ return true
+}
+
+// Write a scalar.
+func yaml_emitter_process_scalar(emitter *yaml_emitter_t) bool {
+ switch emitter.scalar_data.style {
+ case yaml_PLAIN_SCALAR_STYLE:
+ return yaml_emitter_write_plain_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context)
+
+ case yaml_SINGLE_QUOTED_SCALAR_STYLE:
+ return yaml_emitter_write_single_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context)
+
+ case yaml_DOUBLE_QUOTED_SCALAR_STYLE:
+ return yaml_emitter_write_double_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context)
+
+ case yaml_LITERAL_SCALAR_STYLE:
+ return yaml_emitter_write_literal_scalar(emitter, emitter.scalar_data.value)
+
+ case yaml_FOLDED_SCALAR_STYLE:
+ return yaml_emitter_write_folded_scalar(emitter, emitter.scalar_data.value)
+ }
+ panic("unknown scalar style")
+}
+
+// Write a head comment.
+func yaml_emitter_process_head_comment(emitter *yaml_emitter_t) bool {
+ if len(emitter.tail_comment) > 0 {
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ if !yaml_emitter_write_comment(emitter, emitter.tail_comment) {
+ return false
+ }
+ emitter.tail_comment = emitter.tail_comment[:0]
+ emitter.foot_indent = emitter.indent
+ if emitter.foot_indent < 0 {
+ emitter.foot_indent = 0
+ }
+ }
+
+ if len(emitter.head_comment) == 0 {
+ return true
+ }
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ if !yaml_emitter_write_comment(emitter, emitter.head_comment) {
+ return false
+ }
+ emitter.head_comment = emitter.head_comment[:0]
+ return true
+}
+
+// Write an line comment.
+func yaml_emitter_process_line_comment_linebreak(emitter *yaml_emitter_t, linebreak bool) bool {
+ if len(emitter.line_comment) == 0 {
+ // The next 3 lines are needed to resolve an issue with leading newlines
+ // See https://github.com/go-yaml/yaml/issues/755
+ // When linebreak is set to true, put_break will be called and will add
+ // the needed newline.
+ if linebreak && !put_break(emitter) {
+ return false
+ }
+ return true
+ }
+ if !emitter.whitespace {
+ if !put(emitter, ' ') {
+ return false
+ }
+ }
+ if !yaml_emitter_write_comment(emitter, emitter.line_comment) {
+ return false
+ }
+ emitter.line_comment = emitter.line_comment[:0]
+ return true
+}
+
+// Write a foot comment.
+func yaml_emitter_process_foot_comment(emitter *yaml_emitter_t) bool {
+ if len(emitter.foot_comment) == 0 {
+ return true
+ }
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ if !yaml_emitter_write_comment(emitter, emitter.foot_comment) {
+ return false
+ }
+ emitter.foot_comment = emitter.foot_comment[:0]
+ emitter.foot_indent = emitter.indent
+ if emitter.foot_indent < 0 {
+ emitter.foot_indent = 0
+ }
+ return true
+}
+
+// Check if a %YAML directive is valid.
+func yaml_emitter_analyze_version_directive(emitter *yaml_emitter_t, version_directive *yaml_version_directive_t) bool {
+ if version_directive.major != 1 || version_directive.minor != 1 {
+ return yaml_emitter_set_emitter_error(emitter, "incompatible %YAML directive")
+ }
+ return true
+}
+
+// Check if a %TAG directive is valid.
+func yaml_emitter_analyze_tag_directive(emitter *yaml_emitter_t, tag_directive *yaml_tag_directive_t) bool {
+ handle := tag_directive.handle
+ prefix := tag_directive.prefix
+ if len(handle) == 0 {
+ return yaml_emitter_set_emitter_error(emitter, "tag handle must not be empty")
+ }
+ if handle[0] != '!' {
+ return yaml_emitter_set_emitter_error(emitter, "tag handle must start with '!'")
+ }
+ if handle[len(handle)-1] != '!' {
+ return yaml_emitter_set_emitter_error(emitter, "tag handle must end with '!'")
+ }
+ for i := 1; i < len(handle)-1; i += width(handle[i]) {
+ if !is_alpha(handle, i) {
+ return yaml_emitter_set_emitter_error(emitter, "tag handle must contain alphanumerical characters only")
+ }
+ }
+ if len(prefix) == 0 {
+ return yaml_emitter_set_emitter_error(emitter, "tag prefix must not be empty")
+ }
+ return true
+}
+
+// Check if an anchor is valid.
+func yaml_emitter_analyze_anchor(emitter *yaml_emitter_t, anchor []byte, alias bool) bool {
+ if len(anchor) == 0 {
+ problem := "anchor value must not be empty"
+ if alias {
+ problem = "alias value must not be empty"
+ }
+ return yaml_emitter_set_emitter_error(emitter, problem)
+ }
+ for i := 0; i < len(anchor); i += width(anchor[i]) {
+ if !is_alpha(anchor, i) {
+ problem := "anchor value must contain alphanumerical characters only"
+ if alias {
+ problem = "alias value must contain alphanumerical characters only"
+ }
+ return yaml_emitter_set_emitter_error(emitter, problem)
+ }
+ }
+ emitter.anchor_data.anchor = anchor
+ emitter.anchor_data.alias = alias
+ return true
+}
+
+// Check if a tag is valid.
+func yaml_emitter_analyze_tag(emitter *yaml_emitter_t, tag []byte) bool {
+ if len(tag) == 0 {
+ return yaml_emitter_set_emitter_error(emitter, "tag value must not be empty")
+ }
+ for i := 0; i < len(emitter.tag_directives); i++ {
+ tag_directive := &emitter.tag_directives[i]
+ if bytes.HasPrefix(tag, tag_directive.prefix) {
+ emitter.tag_data.handle = tag_directive.handle
+ emitter.tag_data.suffix = tag[len(tag_directive.prefix):]
+ return true
+ }
+ }
+ emitter.tag_data.suffix = tag
+ return true
+}
+
+// Check if a scalar is valid.
+func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool {
+ var (
+ block_indicators = false
+ flow_indicators = false
+ line_breaks = false
+ special_characters = false
+ tab_characters = false
+
+ leading_space = false
+ leading_break = false
+ trailing_space = false
+ trailing_break = false
+ break_space = false
+ space_break = false
+
+ preceded_by_whitespace = false
+ followed_by_whitespace = false
+ previous_space = false
+ previous_break = false
+ )
+
+ emitter.scalar_data.value = value
+
+ if len(value) == 0 {
+ emitter.scalar_data.multiline = false
+ emitter.scalar_data.flow_plain_allowed = false
+ emitter.scalar_data.block_plain_allowed = true
+ emitter.scalar_data.single_quoted_allowed = true
+ emitter.scalar_data.block_allowed = false
+ return true
+ }
+
+ if len(value) >= 3 && ((value[0] == '-' && value[1] == '-' && value[2] == '-') || (value[0] == '.' && value[1] == '.' && value[2] == '.')) {
+ block_indicators = true
+ flow_indicators = true
+ }
+
+ preceded_by_whitespace = true
+ for i, w := 0, 0; i < len(value); i += w {
+ w = width(value[i])
+ followed_by_whitespace = i+w >= len(value) || is_blank(value, i+w)
+
+ if i == 0 {
+ switch value[i] {
+ case '#', ',', '[', ']', '{', '}', '&', '*', '!', '|', '>', '\'', '"', '%', '@', '`':
+ flow_indicators = true
+ block_indicators = true
+ case '?', ':':
+ flow_indicators = true
+ if followed_by_whitespace {
+ block_indicators = true
+ }
+ case '-':
+ if followed_by_whitespace {
+ flow_indicators = true
+ block_indicators = true
+ }
+ }
+ } else {
+ switch value[i] {
+ case ',', '?', '[', ']', '{', '}':
+ flow_indicators = true
+ case ':':
+ flow_indicators = true
+ if followed_by_whitespace {
+ block_indicators = true
+ }
+ case '#':
+ if preceded_by_whitespace {
+ flow_indicators = true
+ block_indicators = true
+ }
+ }
+ }
+
+ if value[i] == '\t' {
+ tab_characters = true
+ } else if !is_printable(value, i) || !is_ascii(value, i) && !emitter.unicode {
+ special_characters = true
+ }
+ if is_space(value, i) {
+ if i == 0 {
+ leading_space = true
+ }
+ if i+width(value[i]) == len(value) {
+ trailing_space = true
+ }
+ if previous_break {
+ break_space = true
+ }
+ previous_space = true
+ previous_break = false
+ } else if is_break(value, i) {
+ line_breaks = true
+ if i == 0 {
+ leading_break = true
+ }
+ if i+width(value[i]) == len(value) {
+ trailing_break = true
+ }
+ if previous_space {
+ space_break = true
+ }
+ previous_space = false
+ previous_break = true
+ } else {
+ previous_space = false
+ previous_break = false
+ }
+
+ // [Go]: Why 'z'? Couldn't be the end of the string as that's the loop condition.
+ preceded_by_whitespace = is_blankz(value, i)
+ }
+
+ emitter.scalar_data.multiline = line_breaks
+ emitter.scalar_data.flow_plain_allowed = true
+ emitter.scalar_data.block_plain_allowed = true
+ emitter.scalar_data.single_quoted_allowed = true
+ emitter.scalar_data.block_allowed = true
+
+ if leading_space || leading_break || trailing_space || trailing_break {
+ emitter.scalar_data.flow_plain_allowed = false
+ emitter.scalar_data.block_plain_allowed = false
+ }
+ if trailing_space {
+ emitter.scalar_data.block_allowed = false
+ }
+ if break_space {
+ emitter.scalar_data.flow_plain_allowed = false
+ emitter.scalar_data.block_plain_allowed = false
+ emitter.scalar_data.single_quoted_allowed = false
+ }
+ if space_break || tab_characters || special_characters {
+ emitter.scalar_data.flow_plain_allowed = false
+ emitter.scalar_data.block_plain_allowed = false
+ emitter.scalar_data.single_quoted_allowed = false
+ }
+ if space_break || special_characters {
+ emitter.scalar_data.block_allowed = false
+ }
+ if line_breaks {
+ emitter.scalar_data.flow_plain_allowed = false
+ emitter.scalar_data.block_plain_allowed = false
+ }
+ if flow_indicators {
+ emitter.scalar_data.flow_plain_allowed = false
+ }
+ if block_indicators {
+ emitter.scalar_data.block_plain_allowed = false
+ }
+ return true
+}
+
+// Check if the event data is valid.
+func yaml_emitter_analyze_event(emitter *yaml_emitter_t, event *yaml_event_t) bool {
+
+ emitter.anchor_data.anchor = nil
+ emitter.tag_data.handle = nil
+ emitter.tag_data.suffix = nil
+ emitter.scalar_data.value = nil
+
+ if len(event.head_comment) > 0 {
+ emitter.head_comment = event.head_comment
+ }
+ if len(event.line_comment) > 0 {
+ emitter.line_comment = event.line_comment
+ }
+ if len(event.foot_comment) > 0 {
+ emitter.foot_comment = event.foot_comment
+ }
+ if len(event.tail_comment) > 0 {
+ emitter.tail_comment = event.tail_comment
+ }
+
+ switch event.typ {
+ case yaml_ALIAS_EVENT:
+ if !yaml_emitter_analyze_anchor(emitter, event.anchor, true) {
+ return false
+ }
+
+ case yaml_SCALAR_EVENT:
+ if len(event.anchor) > 0 {
+ if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) {
+ return false
+ }
+ }
+ if len(event.tag) > 0 && (emitter.canonical || (!event.implicit && !event.quoted_implicit)) {
+ if !yaml_emitter_analyze_tag(emitter, event.tag) {
+ return false
+ }
+ }
+ if !yaml_emitter_analyze_scalar(emitter, event.value) {
+ return false
+ }
+
+ case yaml_SEQUENCE_START_EVENT:
+ if len(event.anchor) > 0 {
+ if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) {
+ return false
+ }
+ }
+ if len(event.tag) > 0 && (emitter.canonical || !event.implicit) {
+ if !yaml_emitter_analyze_tag(emitter, event.tag) {
+ return false
+ }
+ }
+
+ case yaml_MAPPING_START_EVENT:
+ if len(event.anchor) > 0 {
+ if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) {
+ return false
+ }
+ }
+ if len(event.tag) > 0 && (emitter.canonical || !event.implicit) {
+ if !yaml_emitter_analyze_tag(emitter, event.tag) {
+ return false
+ }
+ }
+ }
+ return true
+}
+
+// Write the BOM character.
+func yaml_emitter_write_bom(emitter *yaml_emitter_t) bool {
+ if !flush(emitter) {
+ return false
+ }
+ pos := emitter.buffer_pos
+ emitter.buffer[pos+0] = '\xEF'
+ emitter.buffer[pos+1] = '\xBB'
+ emitter.buffer[pos+2] = '\xBF'
+ emitter.buffer_pos += 3
+ return true
+}
+
+func yaml_emitter_write_indent(emitter *yaml_emitter_t) bool {
+ indent := emitter.indent
+ if indent < 0 {
+ indent = 0
+ }
+ if !emitter.indention || emitter.column > indent || (emitter.column == indent && !emitter.whitespace) {
+ if !put_break(emitter) {
+ return false
+ }
+ }
+ if emitter.foot_indent == indent {
+ if !put_break(emitter) {
+ return false
+ }
+ }
+ for emitter.column < indent {
+ if !put(emitter, ' ') {
+ return false
+ }
+ }
+ emitter.whitespace = true
+ //emitter.indention = true
+ emitter.space_above = false
+ emitter.foot_indent = -1
+ return true
+}
+
+func yaml_emitter_write_indicator(emitter *yaml_emitter_t, indicator []byte, need_whitespace, is_whitespace, is_indention bool) bool {
+ if need_whitespace && !emitter.whitespace {
+ if !put(emitter, ' ') {
+ return false
+ }
+ }
+ if !write_all(emitter, indicator) {
+ return false
+ }
+ emitter.whitespace = is_whitespace
+ emitter.indention = (emitter.indention && is_indention)
+ emitter.open_ended = false
+ return true
+}
+
+func yaml_emitter_write_anchor(emitter *yaml_emitter_t, value []byte) bool {
+ if !write_all(emitter, value) {
+ return false
+ }
+ emitter.whitespace = false
+ emitter.indention = false
+ return true
+}
+
+func yaml_emitter_write_tag_handle(emitter *yaml_emitter_t, value []byte) bool {
+ if !emitter.whitespace {
+ if !put(emitter, ' ') {
+ return false
+ }
+ }
+ if !write_all(emitter, value) {
+ return false
+ }
+ emitter.whitespace = false
+ emitter.indention = false
+ return true
+}
+
+func yaml_emitter_write_tag_content(emitter *yaml_emitter_t, value []byte, need_whitespace bool) bool {
+ if need_whitespace && !emitter.whitespace {
+ if !put(emitter, ' ') {
+ return false
+ }
+ }
+ for i := 0; i < len(value); {
+ var must_write bool
+ switch value[i] {
+ case ';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '_', '.', '~', '*', '\'', '(', ')', '[', ']':
+ must_write = true
+ default:
+ must_write = is_alpha(value, i)
+ }
+ if must_write {
+ if !write(emitter, value, &i) {
+ return false
+ }
+ } else {
+ w := width(value[i])
+ for k := 0; k < w; k++ {
+ octet := value[i]
+ i++
+ if !put(emitter, '%') {
+ return false
+ }
+
+ c := octet >> 4
+ if c < 10 {
+ c += '0'
+ } else {
+ c += 'A' - 10
+ }
+ if !put(emitter, c) {
+ return false
+ }
+
+ c = octet & 0x0f
+ if c < 10 {
+ c += '0'
+ } else {
+ c += 'A' - 10
+ }
+ if !put(emitter, c) {
+ return false
+ }
+ }
+ }
+ }
+ emitter.whitespace = false
+ emitter.indention = false
+ return true
+}
+
+func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool {
+ if len(value) > 0 && !emitter.whitespace {
+ if !put(emitter, ' ') {
+ return false
+ }
+ }
+
+ spaces := false
+ breaks := false
+ for i := 0; i < len(value); {
+ if is_space(value, i) {
+ if allow_breaks && !spaces && emitter.column > emitter.best_width && !is_space(value, i+1) {
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ i += width(value[i])
+ } else {
+ if !write(emitter, value, &i) {
+ return false
+ }
+ }
+ spaces = true
+ } else if is_break(value, i) {
+ if !breaks && value[i] == '\n' {
+ if !put_break(emitter) {
+ return false
+ }
+ }
+ if !write_break(emitter, value, &i) {
+ return false
+ }
+ //emitter.indention = true
+ breaks = true
+ } else {
+ if breaks {
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ }
+ if !write(emitter, value, &i) {
+ return false
+ }
+ emitter.indention = false
+ spaces = false
+ breaks = false
+ }
+ }
+
+ if len(value) > 0 {
+ emitter.whitespace = false
+ }
+ emitter.indention = false
+ if emitter.root_context {
+ emitter.open_ended = true
+ }
+
+ return true
+}
+
+func yaml_emitter_write_single_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool {
+
+ if !yaml_emitter_write_indicator(emitter, []byte{'\''}, true, false, false) {
+ return false
+ }
+
+ spaces := false
+ breaks := false
+ for i := 0; i < len(value); {
+ if is_space(value, i) {
+ if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 && !is_space(value, i+1) {
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ i += width(value[i])
+ } else {
+ if !write(emitter, value, &i) {
+ return false
+ }
+ }
+ spaces = true
+ } else if is_break(value, i) {
+ if !breaks && value[i] == '\n' {
+ if !put_break(emitter) {
+ return false
+ }
+ }
+ if !write_break(emitter, value, &i) {
+ return false
+ }
+ //emitter.indention = true
+ breaks = true
+ } else {
+ if breaks {
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ }
+ if value[i] == '\'' {
+ if !put(emitter, '\'') {
+ return false
+ }
+ }
+ if !write(emitter, value, &i) {
+ return false
+ }
+ emitter.indention = false
+ spaces = false
+ breaks = false
+ }
+ }
+ if !yaml_emitter_write_indicator(emitter, []byte{'\''}, false, false, false) {
+ return false
+ }
+ emitter.whitespace = false
+ emitter.indention = false
+ return true
+}
+
+func yaml_emitter_write_double_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool {
+ spaces := false
+ if !yaml_emitter_write_indicator(emitter, []byte{'"'}, true, false, false) {
+ return false
+ }
+
+ for i := 0; i < len(value); {
+ if !is_printable(value, i) || (!emitter.unicode && !is_ascii(value, i)) ||
+ is_bom(value, i) || is_break(value, i) ||
+ value[i] == '"' || value[i] == '\\' {
+
+ octet := value[i]
+
+ var w int
+ var v rune
+ switch {
+ case octet&0x80 == 0x00:
+ w, v = 1, rune(octet&0x7F)
+ case octet&0xE0 == 0xC0:
+ w, v = 2, rune(octet&0x1F)
+ case octet&0xF0 == 0xE0:
+ w, v = 3, rune(octet&0x0F)
+ case octet&0xF8 == 0xF0:
+ w, v = 4, rune(octet&0x07)
+ }
+ for k := 1; k < w; k++ {
+ octet = value[i+k]
+ v = (v << 6) + (rune(octet) & 0x3F)
+ }
+ i += w
+
+ if !put(emitter, '\\') {
+ return false
+ }
+
+ var ok bool
+ switch v {
+ case 0x00:
+ ok = put(emitter, '0')
+ case 0x07:
+ ok = put(emitter, 'a')
+ case 0x08:
+ ok = put(emitter, 'b')
+ case 0x09:
+ ok = put(emitter, 't')
+ case 0x0A:
+ ok = put(emitter, 'n')
+ case 0x0b:
+ ok = put(emitter, 'v')
+ case 0x0c:
+ ok = put(emitter, 'f')
+ case 0x0d:
+ ok = put(emitter, 'r')
+ case 0x1b:
+ ok = put(emitter, 'e')
+ case 0x22:
+ ok = put(emitter, '"')
+ case 0x5c:
+ ok = put(emitter, '\\')
+ case 0x85:
+ ok = put(emitter, 'N')
+ case 0xA0:
+ ok = put(emitter, '_')
+ case 0x2028:
+ ok = put(emitter, 'L')
+ case 0x2029:
+ ok = put(emitter, 'P')
+ default:
+ if v <= 0xFF {
+ ok = put(emitter, 'x')
+ w = 2
+ } else if v <= 0xFFFF {
+ ok = put(emitter, 'u')
+ w = 4
+ } else {
+ ok = put(emitter, 'U')
+ w = 8
+ }
+ for k := (w - 1) * 4; ok && k >= 0; k -= 4 {
+ digit := byte((v >> uint(k)) & 0x0F)
+ if digit < 10 {
+ ok = put(emitter, digit+'0')
+ } else {
+ ok = put(emitter, digit+'A'-10)
+ }
+ }
+ }
+ if !ok {
+ return false
+ }
+ spaces = false
+ } else if is_space(value, i) {
+ if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 {
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ if is_space(value, i+1) {
+ if !put(emitter, '\\') {
+ return false
+ }
+ }
+ i += width(value[i])
+ } else if !write(emitter, value, &i) {
+ return false
+ }
+ spaces = true
+ } else {
+ if !write(emitter, value, &i) {
+ return false
+ }
+ spaces = false
+ }
+ }
+ if !yaml_emitter_write_indicator(emitter, []byte{'"'}, false, false, false) {
+ return false
+ }
+ emitter.whitespace = false
+ emitter.indention = false
+ return true
+}
+
+func yaml_emitter_write_block_scalar_hints(emitter *yaml_emitter_t, value []byte) bool {
+ if is_space(value, 0) || is_break(value, 0) {
+ indent_hint := []byte{'0' + byte(emitter.best_indent)}
+ if !yaml_emitter_write_indicator(emitter, indent_hint, false, false, false) {
+ return false
+ }
+ }
+
+ emitter.open_ended = false
+
+ var chomp_hint [1]byte
+ if len(value) == 0 {
+ chomp_hint[0] = '-'
+ } else {
+ i := len(value) - 1
+ for value[i]&0xC0 == 0x80 {
+ i--
+ }
+ if !is_break(value, i) {
+ chomp_hint[0] = '-'
+ } else if i == 0 {
+ chomp_hint[0] = '+'
+ emitter.open_ended = true
+ } else {
+ i--
+ for value[i]&0xC0 == 0x80 {
+ i--
+ }
+ if is_break(value, i) {
+ chomp_hint[0] = '+'
+ emitter.open_ended = true
+ }
+ }
+ }
+ if chomp_hint[0] != 0 {
+ if !yaml_emitter_write_indicator(emitter, chomp_hint[:], false, false, false) {
+ return false
+ }
+ }
+ return true
+}
+
+func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bool {
+ if !yaml_emitter_write_indicator(emitter, []byte{'|'}, true, false, false) {
+ return false
+ }
+ if !yaml_emitter_write_block_scalar_hints(emitter, value) {
+ return false
+ }
+ if !yaml_emitter_process_line_comment_linebreak(emitter, true) {
+ return false
+ }
+ //emitter.indention = true
+ emitter.whitespace = true
+ breaks := true
+ for i := 0; i < len(value); {
+ if is_break(value, i) {
+ if !write_break(emitter, value, &i) {
+ return false
+ }
+ //emitter.indention = true
+ breaks = true
+ } else {
+ if breaks {
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ }
+ if !write(emitter, value, &i) {
+ return false
+ }
+ emitter.indention = false
+ breaks = false
+ }
+ }
+
+ return true
+}
+
+func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) bool {
+ if !yaml_emitter_write_indicator(emitter, []byte{'>'}, true, false, false) {
+ return false
+ }
+ if !yaml_emitter_write_block_scalar_hints(emitter, value) {
+ return false
+ }
+ if !yaml_emitter_process_line_comment_linebreak(emitter, true) {
+ return false
+ }
+
+ //emitter.indention = true
+ emitter.whitespace = true
+
+ breaks := true
+ leading_spaces := true
+ for i := 0; i < len(value); {
+ if is_break(value, i) {
+ if !breaks && !leading_spaces && value[i] == '\n' {
+ k := 0
+ for is_break(value, k) {
+ k += width(value[k])
+ }
+ if !is_blankz(value, k) {
+ if !put_break(emitter) {
+ return false
+ }
+ }
+ }
+ if !write_break(emitter, value, &i) {
+ return false
+ }
+ //emitter.indention = true
+ breaks = true
+ } else {
+ if breaks {
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ leading_spaces = is_blank(value, i)
+ }
+ if !breaks && is_space(value, i) && !is_space(value, i+1) && emitter.column > emitter.best_width {
+ if !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ i += width(value[i])
+ } else {
+ if !write(emitter, value, &i) {
+ return false
+ }
+ }
+ emitter.indention = false
+ breaks = false
+ }
+ }
+ return true
+}
+
+func yaml_emitter_write_comment(emitter *yaml_emitter_t, comment []byte) bool {
+ breaks := false
+ pound := false
+ for i := 0; i < len(comment); {
+ if is_break(comment, i) {
+ if !write_break(emitter, comment, &i) {
+ return false
+ }
+ //emitter.indention = true
+ breaks = true
+ pound = false
+ } else {
+ if breaks && !yaml_emitter_write_indent(emitter) {
+ return false
+ }
+ if !pound {
+ if comment[i] != '#' && (!put(emitter, '#') || !put(emitter, ' ')) {
+ return false
+ }
+ pound = true
+ }
+ if !write(emitter, comment, &i) {
+ return false
+ }
+ emitter.indention = false
+ breaks = false
+ }
+ }
+ if !breaks && !put_break(emitter) {
+ return false
+ }
+
+ emitter.whitespace = true
+ //emitter.indention = true
+ return true
+}
diff --git a/vendor/go.yaml.in/yaml/v3/encode.go b/vendor/go.yaml.in/yaml/v3/encode.go
new file mode 100644
index 000000000..de9e72a3e
--- /dev/null
+++ b/vendor/go.yaml.in/yaml/v3/encode.go
@@ -0,0 +1,577 @@
+//
+// Copyright (c) 2011-2019 Canonical Ltd
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package yaml
+
+import (
+ "encoding"
+ "fmt"
+ "io"
+ "reflect"
+ "regexp"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+ "unicode/utf8"
+)
+
+type encoder struct {
+ emitter yaml_emitter_t
+ event yaml_event_t
+ out []byte
+ flow bool
+ indent int
+ doneInit bool
+}
+
+func newEncoder() *encoder {
+ e := &encoder{}
+ yaml_emitter_initialize(&e.emitter)
+ yaml_emitter_set_output_string(&e.emitter, &e.out)
+ yaml_emitter_set_unicode(&e.emitter, true)
+ return e
+}
+
+func newEncoderWithWriter(w io.Writer) *encoder {
+ e := &encoder{}
+ yaml_emitter_initialize(&e.emitter)
+ yaml_emitter_set_output_writer(&e.emitter, w)
+ yaml_emitter_set_unicode(&e.emitter, true)
+ return e
+}
+
+func (e *encoder) init() {
+ if e.doneInit {
+ return
+ }
+ if e.indent == 0 {
+ e.indent = 4
+ }
+ e.emitter.best_indent = e.indent
+ yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING)
+ e.emit()
+ e.doneInit = true
+}
+
+func (e *encoder) finish() {
+ e.emitter.open_ended = false
+ yaml_stream_end_event_initialize(&e.event)
+ e.emit()
+}
+
+func (e *encoder) destroy() {
+ yaml_emitter_delete(&e.emitter)
+}
+
+func (e *encoder) emit() {
+ // This will internally delete the e.event value.
+ e.must(yaml_emitter_emit(&e.emitter, &e.event))
+}
+
+func (e *encoder) must(ok bool) {
+ if !ok {
+ msg := e.emitter.problem
+ if msg == "" {
+ msg = "unknown problem generating YAML content"
+ }
+ failf("%s", msg)
+ }
+}
+
+func (e *encoder) marshalDoc(tag string, in reflect.Value) {
+ e.init()
+ var node *Node
+ if in.IsValid() {
+ node, _ = in.Interface().(*Node)
+ }
+ if node != nil && node.Kind == DocumentNode {
+ e.nodev(in)
+ } else {
+ yaml_document_start_event_initialize(&e.event, nil, nil, true)
+ e.emit()
+ e.marshal(tag, in)
+ yaml_document_end_event_initialize(&e.event, true)
+ e.emit()
+ }
+}
+
+func (e *encoder) marshal(tag string, in reflect.Value) {
+ tag = shortTag(tag)
+ if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() {
+ e.nilv()
+ return
+ }
+ iface := in.Interface()
+ switch value := iface.(type) {
+ case *Node:
+ e.nodev(in)
+ return
+ case Node:
+ if !in.CanAddr() {
+ var n = reflect.New(in.Type()).Elem()
+ n.Set(in)
+ in = n
+ }
+ e.nodev(in.Addr())
+ return
+ case time.Time:
+ e.timev(tag, in)
+ return
+ case *time.Time:
+ e.timev(tag, in.Elem())
+ return
+ case time.Duration:
+ e.stringv(tag, reflect.ValueOf(value.String()))
+ return
+ case Marshaler:
+ v, err := value.MarshalYAML()
+ if err != nil {
+ fail(err)
+ }
+ if v == nil {
+ e.nilv()
+ return
+ }
+ e.marshal(tag, reflect.ValueOf(v))
+ return
+ case encoding.TextMarshaler:
+ text, err := value.MarshalText()
+ if err != nil {
+ fail(err)
+ }
+ in = reflect.ValueOf(string(text))
+ case nil:
+ e.nilv()
+ return
+ }
+ switch in.Kind() {
+ case reflect.Interface:
+ e.marshal(tag, in.Elem())
+ case reflect.Map:
+ e.mapv(tag, in)
+ case reflect.Ptr:
+ e.marshal(tag, in.Elem())
+ case reflect.Struct:
+ e.structv(tag, in)
+ case reflect.Slice, reflect.Array:
+ e.slicev(tag, in)
+ case reflect.String:
+ e.stringv(tag, in)
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ e.intv(tag, in)
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ e.uintv(tag, in)
+ case reflect.Float32, reflect.Float64:
+ e.floatv(tag, in)
+ case reflect.Bool:
+ e.boolv(tag, in)
+ default:
+ panic("cannot marshal type: " + in.Type().String())
+ }
+}
+
+func (e *encoder) mapv(tag string, in reflect.Value) {
+ e.mappingv(tag, func() {
+ keys := keyList(in.MapKeys())
+ sort.Sort(keys)
+ for _, k := range keys {
+ e.marshal("", k)
+ e.marshal("", in.MapIndex(k))
+ }
+ })
+}
+
+func (e *encoder) fieldByIndex(v reflect.Value, index []int) (field reflect.Value) {
+ for _, num := range index {
+ for {
+ if v.Kind() == reflect.Ptr {
+ if v.IsNil() {
+ return reflect.Value{}
+ }
+ v = v.Elem()
+ continue
+ }
+ break
+ }
+ v = v.Field(num)
+ }
+ return v
+}
+
+func (e *encoder) structv(tag string, in reflect.Value) {
+ sinfo, err := getStructInfo(in.Type())
+ if err != nil {
+ panic(err)
+ }
+ e.mappingv(tag, func() {
+ for _, info := range sinfo.FieldsList {
+ var value reflect.Value
+ if info.Inline == nil {
+ value = in.Field(info.Num)
+ } else {
+ value = e.fieldByIndex(in, info.Inline)
+ if !value.IsValid() {
+ continue
+ }
+ }
+ if info.OmitEmpty && isZero(value) {
+ continue
+ }
+ e.marshal("", reflect.ValueOf(info.Key))
+ e.flow = info.Flow
+ e.marshal("", value)
+ }
+ if sinfo.InlineMap >= 0 {
+ m := in.Field(sinfo.InlineMap)
+ if m.Len() > 0 {
+ e.flow = false
+ keys := keyList(m.MapKeys())
+ sort.Sort(keys)
+ for _, k := range keys {
+ if _, found := sinfo.FieldsMap[k.String()]; found {
+ panic(fmt.Sprintf("cannot have key %q in inlined map: conflicts with struct field", k.String()))
+ }
+ e.marshal("", k)
+ e.flow = false
+ e.marshal("", m.MapIndex(k))
+ }
+ }
+ }
+ })
+}
+
+func (e *encoder) mappingv(tag string, f func()) {
+ implicit := tag == ""
+ style := yaml_BLOCK_MAPPING_STYLE
+ if e.flow {
+ e.flow = false
+ style = yaml_FLOW_MAPPING_STYLE
+ }
+ yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)
+ e.emit()
+ f()
+ yaml_mapping_end_event_initialize(&e.event)
+ e.emit()
+}
+
+func (e *encoder) slicev(tag string, in reflect.Value) {
+ implicit := tag == ""
+ style := yaml_BLOCK_SEQUENCE_STYLE
+ if e.flow {
+ e.flow = false
+ style = yaml_FLOW_SEQUENCE_STYLE
+ }
+ e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style))
+ e.emit()
+ n := in.Len()
+ for i := 0; i < n; i++ {
+ e.marshal("", in.Index(i))
+ }
+ e.must(yaml_sequence_end_event_initialize(&e.event))
+ e.emit()
+}
+
+// isBase60 returns whether s is in base 60 notation as defined in YAML 1.1.
+//
+// The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported
+// in YAML 1.2 and by this package, but these should be marshalled quoted for
+// the time being for compatibility with other parsers.
+func isBase60Float(s string) (result bool) {
+ // Fast path.
+ if s == "" {
+ return false
+ }
+ c := s[0]
+ if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 {
+ return false
+ }
+ // Do the full match.
+ return base60float.MatchString(s)
+}
+
+// From http://yaml.org/type/float.html, except the regular expression there
+// is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix.
+var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`)
+
+// isOldBool returns whether s is bool notation as defined in YAML 1.1.
+//
+// We continue to force strings that YAML 1.1 would interpret as booleans to be
+// rendered as quotes strings so that the marshalled output valid for YAML 1.1
+// parsing.
+func isOldBool(s string) (result bool) {
+ switch s {
+ case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON",
+ "n", "N", "no", "No", "NO", "off", "Off", "OFF":
+ return true
+ default:
+ return false
+ }
+}
+
+func (e *encoder) stringv(tag string, in reflect.Value) {
+ var style yaml_scalar_style_t
+ s := in.String()
+ canUsePlain := true
+ switch {
+ case !utf8.ValidString(s):
+ if tag == binaryTag {
+ failf("explicitly tagged !!binary data must be base64-encoded")
+ }
+ if tag != "" {
+ failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag))
+ }
+ // It can't be encoded directly as YAML so use a binary tag
+ // and encode it as base64.
+ tag = binaryTag
+ s = encodeBase64(s)
+ case tag == "":
+ // Check to see if it would resolve to a specific
+ // tag when encoded unquoted. If it doesn't,
+ // there's no need to quote it.
+ rtag, _ := resolve("", s)
+ canUsePlain = rtag == strTag && !(isBase60Float(s) || isOldBool(s))
+ }
+ // Note: it's possible for user code to emit invalid YAML
+ // if they explicitly specify a tag and a string containing
+ // text that's incompatible with that tag.
+ switch {
+ case strings.Contains(s, "\n"):
+ if e.flow {
+ style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
+ } else {
+ style = yaml_LITERAL_SCALAR_STYLE
+ }
+ case canUsePlain:
+ style = yaml_PLAIN_SCALAR_STYLE
+ default:
+ style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
+ }
+ e.emitScalar(s, "", tag, style, nil, nil, nil, nil)
+}
+
+func (e *encoder) boolv(tag string, in reflect.Value) {
+ var s string
+ if in.Bool() {
+ s = "true"
+ } else {
+ s = "false"
+ }
+ e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
+}
+
+func (e *encoder) intv(tag string, in reflect.Value) {
+ s := strconv.FormatInt(in.Int(), 10)
+ e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
+}
+
+func (e *encoder) uintv(tag string, in reflect.Value) {
+ s := strconv.FormatUint(in.Uint(), 10)
+ e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
+}
+
+func (e *encoder) timev(tag string, in reflect.Value) {
+ t := in.Interface().(time.Time)
+ s := t.Format(time.RFC3339Nano)
+ e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
+}
+
+func (e *encoder) floatv(tag string, in reflect.Value) {
+ // Issue #352: When formatting, use the precision of the underlying value
+ precision := 64
+ if in.Kind() == reflect.Float32 {
+ precision = 32
+ }
+
+ s := strconv.FormatFloat(in.Float(), 'g', -1, precision)
+ switch s {
+ case "+Inf":
+ s = ".inf"
+ case "-Inf":
+ s = "-.inf"
+ case "NaN":
+ s = ".nan"
+ }
+ e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
+}
+
+func (e *encoder) nilv() {
+ e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
+}
+
+func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t, head, line, foot, tail []byte) {
+ // TODO Kill this function. Replace all initialize calls by their underlining Go literals.
+ implicit := tag == ""
+ if !implicit {
+ tag = longTag(tag)
+ }
+ e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style))
+ e.event.head_comment = head
+ e.event.line_comment = line
+ e.event.foot_comment = foot
+ e.event.tail_comment = tail
+ e.emit()
+}
+
+func (e *encoder) nodev(in reflect.Value) {
+ e.node(in.Interface().(*Node), "")
+}
+
+func (e *encoder) node(node *Node, tail string) {
+ // Zero nodes behave as nil.
+ if node.Kind == 0 && node.IsZero() {
+ e.nilv()
+ return
+ }
+
+ // If the tag was not explicitly requested, and dropping it won't change the
+ // implicit tag of the value, don't include it in the presentation.
+ var tag = node.Tag
+ var stag = shortTag(tag)
+ var forceQuoting bool
+ if tag != "" && node.Style&TaggedStyle == 0 {
+ if node.Kind == ScalarNode {
+ if stag == strTag && node.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0 {
+ tag = ""
+ } else {
+ rtag, _ := resolve("", node.Value)
+ if rtag == stag {
+ tag = ""
+ } else if stag == strTag {
+ tag = ""
+ forceQuoting = true
+ }
+ }
+ } else {
+ var rtag string
+ switch node.Kind {
+ case MappingNode:
+ rtag = mapTag
+ case SequenceNode:
+ rtag = seqTag
+ }
+ if rtag == stag {
+ tag = ""
+ }
+ }
+ }
+
+ switch node.Kind {
+ case DocumentNode:
+ yaml_document_start_event_initialize(&e.event, nil, nil, true)
+ e.event.head_comment = []byte(node.HeadComment)
+ e.emit()
+ for _, node := range node.Content {
+ e.node(node, "")
+ }
+ yaml_document_end_event_initialize(&e.event, true)
+ e.event.foot_comment = []byte(node.FootComment)
+ e.emit()
+
+ case SequenceNode:
+ style := yaml_BLOCK_SEQUENCE_STYLE
+ if node.Style&FlowStyle != 0 {
+ style = yaml_FLOW_SEQUENCE_STYLE
+ }
+ e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style))
+ e.event.head_comment = []byte(node.HeadComment)
+ e.emit()
+ for _, node := range node.Content {
+ e.node(node, "")
+ }
+ e.must(yaml_sequence_end_event_initialize(&e.event))
+ e.event.line_comment = []byte(node.LineComment)
+ e.event.foot_comment = []byte(node.FootComment)
+ e.emit()
+
+ case MappingNode:
+ style := yaml_BLOCK_MAPPING_STYLE
+ if node.Style&FlowStyle != 0 {
+ style = yaml_FLOW_MAPPING_STYLE
+ }
+ yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style)
+ e.event.tail_comment = []byte(tail)
+ e.event.head_comment = []byte(node.HeadComment)
+ e.emit()
+
+ // The tail logic below moves the foot comment of prior keys to the following key,
+ // since the value for each key may be a nested structure and the foot needs to be
+ // processed only the entirety of the value is streamed. The last tail is processed
+ // with the mapping end event.
+ var tail string
+ for i := 0; i+1 < len(node.Content); i += 2 {
+ k := node.Content[i]
+ foot := k.FootComment
+ if foot != "" {
+ kopy := *k
+ kopy.FootComment = ""
+ k = &kopy
+ }
+ e.node(k, tail)
+ tail = foot
+
+ v := node.Content[i+1]
+ e.node(v, "")
+ }
+
+ yaml_mapping_end_event_initialize(&e.event)
+ e.event.tail_comment = []byte(tail)
+ e.event.line_comment = []byte(node.LineComment)
+ e.event.foot_comment = []byte(node.FootComment)
+ e.emit()
+
+ case AliasNode:
+ yaml_alias_event_initialize(&e.event, []byte(node.Value))
+ e.event.head_comment = []byte(node.HeadComment)
+ e.event.line_comment = []byte(node.LineComment)
+ e.event.foot_comment = []byte(node.FootComment)
+ e.emit()
+
+ case ScalarNode:
+ value := node.Value
+ if !utf8.ValidString(value) {
+ if stag == binaryTag {
+ failf("explicitly tagged !!binary data must be base64-encoded")
+ }
+ if stag != "" {
+ failf("cannot marshal invalid UTF-8 data as %s", stag)
+ }
+ // It can't be encoded directly as YAML so use a binary tag
+ // and encode it as base64.
+ tag = binaryTag
+ value = encodeBase64(value)
+ }
+
+ style := yaml_PLAIN_SCALAR_STYLE
+ switch {
+ case node.Style&DoubleQuotedStyle != 0:
+ style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
+ case node.Style&SingleQuotedStyle != 0:
+ style = yaml_SINGLE_QUOTED_SCALAR_STYLE
+ case node.Style&LiteralStyle != 0:
+ style = yaml_LITERAL_SCALAR_STYLE
+ case node.Style&FoldedStyle != 0:
+ style = yaml_FOLDED_SCALAR_STYLE
+ case strings.Contains(value, "\n"):
+ style = yaml_LITERAL_SCALAR_STYLE
+ case forceQuoting:
+ style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
+ }
+
+ e.emitScalar(value, node.Anchor, tag, style, []byte(node.HeadComment), []byte(node.LineComment), []byte(node.FootComment), []byte(tail))
+ default:
+ failf("cannot encode node with unknown kind %d", node.Kind)
+ }
+}
diff --git a/vendor/go.yaml.in/yaml/v3/parserc.go b/vendor/go.yaml.in/yaml/v3/parserc.go
new file mode 100644
index 000000000..25fe82363
--- /dev/null
+++ b/vendor/go.yaml.in/yaml/v3/parserc.go
@@ -0,0 +1,1274 @@
+//
+// Copyright (c) 2011-2019 Canonical Ltd
+// Copyright (c) 2006-2010 Kirill Simonov
+//
+// 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.
+
+package yaml
+
+import (
+ "bytes"
+)
+
+// The parser implements the following grammar:
+//
+// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END
+// implicit_document ::= block_node DOCUMENT-END*
+// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
+// block_node_or_indentless_sequence ::=
+// ALIAS
+// | properties (block_content | indentless_block_sequence)?
+// | block_content
+// | indentless_block_sequence
+// block_node ::= ALIAS
+// | properties block_content?
+// | block_content
+// flow_node ::= ALIAS
+// | properties flow_content?
+// | flow_content
+// properties ::= TAG ANCHOR? | ANCHOR TAG?
+// block_content ::= block_collection | flow_collection | SCALAR
+// flow_content ::= flow_collection | SCALAR
+// block_collection ::= block_sequence | block_mapping
+// flow_collection ::= flow_sequence | flow_mapping
+// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
+// indentless_sequence ::= (BLOCK-ENTRY block_node?)+
+// block_mapping ::= BLOCK-MAPPING_START
+// ((KEY block_node_or_indentless_sequence?)?
+// (VALUE block_node_or_indentless_sequence?)?)*
+// BLOCK-END
+// flow_sequence ::= FLOW-SEQUENCE-START
+// (flow_sequence_entry FLOW-ENTRY)*
+// flow_sequence_entry?
+// FLOW-SEQUENCE-END
+// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+// flow_mapping ::= FLOW-MAPPING-START
+// (flow_mapping_entry FLOW-ENTRY)*
+// flow_mapping_entry?
+// FLOW-MAPPING-END
+// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+
+// Peek the next token in the token queue.
+func peek_token(parser *yaml_parser_t) *yaml_token_t {
+ if parser.token_available || yaml_parser_fetch_more_tokens(parser) {
+ token := &parser.tokens[parser.tokens_head]
+ yaml_parser_unfold_comments(parser, token)
+ return token
+ }
+ return nil
+}
+
+// yaml_parser_unfold_comments walks through the comments queue and joins all
+// comments behind the position of the provided token into the respective
+// top-level comment slices in the parser.
+func yaml_parser_unfold_comments(parser *yaml_parser_t, token *yaml_token_t) {
+ for parser.comments_head < len(parser.comments) && token.start_mark.index >= parser.comments[parser.comments_head].token_mark.index {
+ comment := &parser.comments[parser.comments_head]
+ if len(comment.head) > 0 {
+ if token.typ == yaml_BLOCK_END_TOKEN {
+ // No heads on ends, so keep comment.head for a follow up token.
+ break
+ }
+ if len(parser.head_comment) > 0 {
+ parser.head_comment = append(parser.head_comment, '\n')
+ }
+ parser.head_comment = append(parser.head_comment, comment.head...)
+ }
+ if len(comment.foot) > 0 {
+ if len(parser.foot_comment) > 0 {
+ parser.foot_comment = append(parser.foot_comment, '\n')
+ }
+ parser.foot_comment = append(parser.foot_comment, comment.foot...)
+ }
+ if len(comment.line) > 0 {
+ if len(parser.line_comment) > 0 {
+ parser.line_comment = append(parser.line_comment, '\n')
+ }
+ parser.line_comment = append(parser.line_comment, comment.line...)
+ }
+ *comment = yaml_comment_t{}
+ parser.comments_head++
+ }
+}
+
+// Remove the next token from the queue (must be called after peek_token).
+func skip_token(parser *yaml_parser_t) {
+ parser.token_available = false
+ parser.tokens_parsed++
+ parser.stream_end_produced = parser.tokens[parser.tokens_head].typ == yaml_STREAM_END_TOKEN
+ parser.tokens_head++
+}
+
+// Get the next event.
+func yaml_parser_parse(parser *yaml_parser_t, event *yaml_event_t) bool {
+ // Erase the event object.
+ *event = yaml_event_t{}
+
+ // No events after the end of the stream or error.
+ if parser.stream_end_produced || parser.error != yaml_NO_ERROR || parser.state == yaml_PARSE_END_STATE {
+ return true
+ }
+
+ // Generate the next event.
+ return yaml_parser_state_machine(parser, event)
+}
+
+// Set parser error.
+func yaml_parser_set_parser_error(parser *yaml_parser_t, problem string, problem_mark yaml_mark_t) bool {
+ parser.error = yaml_PARSER_ERROR
+ parser.problem = problem
+ parser.problem_mark = problem_mark
+ return false
+}
+
+func yaml_parser_set_parser_error_context(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string, problem_mark yaml_mark_t) bool {
+ parser.error = yaml_PARSER_ERROR
+ parser.context = context
+ parser.context_mark = context_mark
+ parser.problem = problem
+ parser.problem_mark = problem_mark
+ return false
+}
+
+// State dispatcher.
+func yaml_parser_state_machine(parser *yaml_parser_t, event *yaml_event_t) bool {
+ //trace("yaml_parser_state_machine", "state:", parser.state.String())
+
+ switch parser.state {
+ case yaml_PARSE_STREAM_START_STATE:
+ return yaml_parser_parse_stream_start(parser, event)
+
+ case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE:
+ return yaml_parser_parse_document_start(parser, event, true)
+
+ case yaml_PARSE_DOCUMENT_START_STATE:
+ return yaml_parser_parse_document_start(parser, event, false)
+
+ case yaml_PARSE_DOCUMENT_CONTENT_STATE:
+ return yaml_parser_parse_document_content(parser, event)
+
+ case yaml_PARSE_DOCUMENT_END_STATE:
+ return yaml_parser_parse_document_end(parser, event)
+
+ case yaml_PARSE_BLOCK_NODE_STATE:
+ return yaml_parser_parse_node(parser, event, true, false)
+
+ case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE:
+ return yaml_parser_parse_node(parser, event, true, true)
+
+ case yaml_PARSE_FLOW_NODE_STATE:
+ return yaml_parser_parse_node(parser, event, false, false)
+
+ case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE:
+ return yaml_parser_parse_block_sequence_entry(parser, event, true)
+
+ case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE:
+ return yaml_parser_parse_block_sequence_entry(parser, event, false)
+
+ case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE:
+ return yaml_parser_parse_indentless_sequence_entry(parser, event)
+
+ case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE:
+ return yaml_parser_parse_block_mapping_key(parser, event, true)
+
+ case yaml_PARSE_BLOCK_MAPPING_KEY_STATE:
+ return yaml_parser_parse_block_mapping_key(parser, event, false)
+
+ case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE:
+ return yaml_parser_parse_block_mapping_value(parser, event)
+
+ case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE:
+ return yaml_parser_parse_flow_sequence_entry(parser, event, true)
+
+ case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE:
+ return yaml_parser_parse_flow_sequence_entry(parser, event, false)
+
+ case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE:
+ return yaml_parser_parse_flow_sequence_entry_mapping_key(parser, event)
+
+ case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE:
+ return yaml_parser_parse_flow_sequence_entry_mapping_value(parser, event)
+
+ case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE:
+ return yaml_parser_parse_flow_sequence_entry_mapping_end(parser, event)
+
+ case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE:
+ return yaml_parser_parse_flow_mapping_key(parser, event, true)
+
+ case yaml_PARSE_FLOW_MAPPING_KEY_STATE:
+ return yaml_parser_parse_flow_mapping_key(parser, event, false)
+
+ case yaml_PARSE_FLOW_MAPPING_VALUE_STATE:
+ return yaml_parser_parse_flow_mapping_value(parser, event, false)
+
+ case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE:
+ return yaml_parser_parse_flow_mapping_value(parser, event, true)
+
+ default:
+ panic("invalid parser state")
+ }
+}
+
+// Parse the production:
+// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END
+//
+// ************
+func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) bool {
+ token := peek_token(parser)
+ if token == nil {
+ return false
+ }
+ if token.typ != yaml_STREAM_START_TOKEN {
+ return yaml_parser_set_parser_error(parser, "did not find expected ", token.start_mark)
+ }
+ parser.state = yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE
+ *event = yaml_event_t{
+ typ: yaml_STREAM_START_EVENT,
+ start_mark: token.start_mark,
+ end_mark: token.end_mark,
+ encoding: token.encoding,
+ }
+ skip_token(parser)
+ return true
+}
+
+// Parse the productions:
+// implicit_document ::= block_node DOCUMENT-END*
+//
+// *
+//
+// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
+//
+// *************************
+func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t, implicit bool) bool {
+
+ token := peek_token(parser)
+ if token == nil {
+ return false
+ }
+
+ // Parse extra document end indicators.
+ if !implicit {
+ for token.typ == yaml_DOCUMENT_END_TOKEN {
+ skip_token(parser)
+ token = peek_token(parser)
+ if token == nil {
+ return false
+ }
+ }
+ }
+
+ if implicit && token.typ != yaml_VERSION_DIRECTIVE_TOKEN &&
+ token.typ != yaml_TAG_DIRECTIVE_TOKEN &&
+ token.typ != yaml_DOCUMENT_START_TOKEN &&
+ token.typ != yaml_STREAM_END_TOKEN {
+ // Parse an implicit document.
+ if !yaml_parser_process_directives(parser, nil, nil) {
+ return false
+ }
+ parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE)
+ parser.state = yaml_PARSE_BLOCK_NODE_STATE
+
+ var head_comment []byte
+ if len(parser.head_comment) > 0 {
+ // [Go] Scan the header comment backwards, and if an empty line is found, break
+ // the header so the part before the last empty line goes into the
+ // document header, while the bottom of it goes into a follow up event.
+ for i := len(parser.head_comment) - 1; i > 0; i-- {
+ if parser.head_comment[i] == '\n' {
+ if i == len(parser.head_comment)-1 {
+ head_comment = parser.head_comment[:i]
+ parser.head_comment = parser.head_comment[i+1:]
+ break
+ } else if parser.head_comment[i-1] == '\n' {
+ head_comment = parser.head_comment[:i-1]
+ parser.head_comment = parser.head_comment[i+1:]
+ break
+ }
+ }
+ }
+ }
+
+ *event = yaml_event_t{
+ typ: yaml_DOCUMENT_START_EVENT,
+ start_mark: token.start_mark,
+ end_mark: token.end_mark,
+
+ head_comment: head_comment,
+ }
+
+ } else if token.typ != yaml_STREAM_END_TOKEN {
+ // Parse an explicit document.
+ var version_directive *yaml_version_directive_t
+ var tag_directives []yaml_tag_directive_t
+ start_mark := token.start_mark
+ if !yaml_parser_process_directives(parser, &version_directive, &tag_directives) {
+ return false
+ }
+ token = peek_token(parser)
+ if token == nil {
+ return false
+ }
+ if token.typ != yaml_DOCUMENT_START_TOKEN {
+ yaml_parser_set_parser_error(parser,
+ "did not find expected ", token.start_mark)
+ return false
+ }
+ parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE)
+ parser.state = yaml_PARSE_DOCUMENT_CONTENT_STATE
+ end_mark := token.end_mark
+
+ *event = yaml_event_t{
+ typ: yaml_DOCUMENT_START_EVENT,
+ start_mark: start_mark,
+ end_mark: end_mark,
+ version_directive: version_directive,
+ tag_directives: tag_directives,
+ implicit: false,
+ }
+ skip_token(parser)
+
+ } else {
+ // Parse the stream end.
+ parser.state = yaml_PARSE_END_STATE
+ *event = yaml_event_t{
+ typ: yaml_STREAM_END_EVENT,
+ start_mark: token.start_mark,
+ end_mark: token.end_mark,
+ }
+ skip_token(parser)
+ }
+
+ return true
+}
+
+// Parse the productions:
+// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
+//
+// ***********
+func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event_t) bool {
+ token := peek_token(parser)
+ if token == nil {
+ return false
+ }
+
+ if token.typ == yaml_VERSION_DIRECTIVE_TOKEN ||
+ token.typ == yaml_TAG_DIRECTIVE_TOKEN ||
+ token.typ == yaml_DOCUMENT_START_TOKEN ||
+ token.typ == yaml_DOCUMENT_END_TOKEN ||
+ token.typ == yaml_STREAM_END_TOKEN {
+ parser.state = parser.states[len(parser.states)-1]
+ parser.states = parser.states[:len(parser.states)-1]
+ return yaml_parser_process_empty_scalar(parser, event,
+ token.start_mark)
+ }
+ return yaml_parser_parse_node(parser, event, true, false)
+}
+
+// Parse the productions:
+// implicit_document ::= block_node DOCUMENT-END*
+//
+// *************
+//
+// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END*
+func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t) bool {
+ token := peek_token(parser)
+ if token == nil {
+ return false
+ }
+
+ start_mark := token.start_mark
+ end_mark := token.start_mark
+
+ implicit := true
+ if token.typ == yaml_DOCUMENT_END_TOKEN {
+ end_mark = token.end_mark
+ skip_token(parser)
+ implicit = false
+ }
+
+ parser.tag_directives = parser.tag_directives[:0]
+
+ parser.state = yaml_PARSE_DOCUMENT_START_STATE
+ *event = yaml_event_t{
+ typ: yaml_DOCUMENT_END_EVENT,
+ start_mark: start_mark,
+ end_mark: end_mark,
+ implicit: implicit,
+ }
+ yaml_parser_set_event_comments(parser, event)
+ if len(event.head_comment) > 0 && len(event.foot_comment) == 0 {
+ event.foot_comment = event.head_comment
+ event.head_comment = nil
+ }
+ return true
+}
+
+func yaml_parser_set_event_comments(parser *yaml_parser_t, event *yaml_event_t) {
+ event.head_comment = parser.head_comment
+ event.line_comment = parser.line_comment
+ event.foot_comment = parser.foot_comment
+ parser.head_comment = nil
+ parser.line_comment = nil
+ parser.foot_comment = nil
+ parser.tail_comment = nil
+ parser.stem_comment = nil
+}
+
+// Parse the productions:
+// block_node_or_indentless_sequence ::=
+//
+// ALIAS
+// *****
+// | properties (block_content | indentless_block_sequence)?
+// ********** *
+// | block_content | indentless_block_sequence
+// *
+//
+// block_node ::= ALIAS
+//
+// *****
+// | properties block_content?
+// ********** *
+// | block_content
+// *
+//
+// flow_node ::= ALIAS
+//
+// *****
+// | properties flow_content?
+// ********** *
+// | flow_content
+// *
+//
+// properties ::= TAG ANCHOR? | ANCHOR TAG?
+//
+// *************************
+//
+// block_content ::= block_collection | flow_collection | SCALAR
+//
+// ******
+//
+// flow_content ::= flow_collection | SCALAR
+//
+// ******
+func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, indentless_sequence bool) bool {
+ //defer trace("yaml_parser_parse_node", "block:", block, "indentless_sequence:", indentless_sequence)()
+
+ token := peek_token(parser)
+ if token == nil {
+ return false
+ }
+
+ if token.typ == yaml_ALIAS_TOKEN {
+ parser.state = parser.states[len(parser.states)-1]
+ parser.states = parser.states[:len(parser.states)-1]
+ *event = yaml_event_t{
+ typ: yaml_ALIAS_EVENT,
+ start_mark: token.start_mark,
+ end_mark: token.end_mark,
+ anchor: token.value,
+ }
+ yaml_parser_set_event_comments(parser, event)
+ skip_token(parser)
+ return true
+ }
+
+ start_mark := token.start_mark
+ end_mark := token.start_mark
+
+ var tag_token bool
+ var tag_handle, tag_suffix, anchor []byte
+ var tag_mark yaml_mark_t
+ if token.typ == yaml_ANCHOR_TOKEN {
+ anchor = token.value
+ start_mark = token.start_mark
+ end_mark = token.end_mark
+ skip_token(parser)
+ token = peek_token(parser)
+ if token == nil {
+ return false
+ }
+ if token.typ == yaml_TAG_TOKEN {
+ tag_token = true
+ tag_handle = token.value
+ tag_suffix = token.suffix
+ tag_mark = token.start_mark
+ end_mark = token.end_mark
+ skip_token(parser)
+ token = peek_token(parser)
+ if token == nil {
+ return false
+ }
+ }
+ } else if token.typ == yaml_TAG_TOKEN {
+ tag_token = true
+ tag_handle = token.value
+ tag_suffix = token.suffix
+ start_mark = token.start_mark
+ tag_mark = token.start_mark
+ end_mark = token.end_mark
+ skip_token(parser)
+ token = peek_token(parser)
+ if token == nil {
+ return false
+ }
+ if token.typ == yaml_ANCHOR_TOKEN {
+ anchor = token.value
+ end_mark = token.end_mark
+ skip_token(parser)
+ token = peek_token(parser)
+ if token == nil {
+ return false
+ }
+ }
+ }
+
+ var tag []byte
+ if tag_token {
+ if len(tag_handle) == 0 {
+ tag = tag_suffix
+ tag_suffix = nil
+ } else {
+ for i := range parser.tag_directives {
+ if bytes.Equal(parser.tag_directives[i].handle, tag_handle) {
+ tag = append([]byte(nil), parser.tag_directives[i].prefix...)
+ tag = append(tag, tag_suffix...)
+ break
+ }
+ }
+ if len(tag) == 0 {
+ yaml_parser_set_parser_error_context(parser,
+ "while parsing a node", start_mark,
+ "found undefined tag handle", tag_mark)
+ return false
+ }
+ }
+ }
+
+ implicit := len(tag) == 0
+ if indentless_sequence && token.typ == yaml_BLOCK_ENTRY_TOKEN {
+ end_mark = token.end_mark
+ parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE
+ *event = yaml_event_t{
+ typ: yaml_SEQUENCE_START_EVENT,
+ start_mark: start_mark,
+ end_mark: end_mark,
+ anchor: anchor,
+ tag: tag,
+ implicit: implicit,
+ style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE),
+ }
+ return true
+ }
+ if token.typ == yaml_SCALAR_TOKEN {
+ var plain_implicit, quoted_implicit bool
+ end_mark = token.end_mark
+ if (len(tag) == 0 && token.style == yaml_PLAIN_SCALAR_STYLE) || (len(tag) == 1 && tag[0] == '!') {
+ plain_implicit = true
+ } else if len(tag) == 0 {
+ quoted_implicit = true
+ }
+ parser.state = parser.states[len(parser.states)-1]
+ parser.states = parser.states[:len(parser.states)-1]
+
+ *event = yaml_event_t{
+ typ: yaml_SCALAR_EVENT,
+ start_mark: start_mark,
+ end_mark: end_mark,
+ anchor: anchor,
+ tag: tag,
+ value: token.value,
+ implicit: plain_implicit,
+ quoted_implicit: quoted_implicit,
+ style: yaml_style_t(token.style),
+ }
+ yaml_parser_set_event_comments(parser, event)
+ skip_token(parser)
+ return true
+ }
+ if token.typ == yaml_FLOW_SEQUENCE_START_TOKEN {
+ // [Go] Some of the events below can be merged as they differ only on style.
+ end_mark = token.end_mark
+ parser.state = yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE
+ *event = yaml_event_t{
+ typ: yaml_SEQUENCE_START_EVENT,
+ start_mark: start_mark,
+ end_mark: end_mark,
+ anchor: anchor,
+ tag: tag,
+ implicit: implicit,
+ style: yaml_style_t(yaml_FLOW_SEQUENCE_STYLE),
+ }
+ yaml_parser_set_event_comments(parser, event)
+ return true
+ }
+ if token.typ == yaml_FLOW_MAPPING_START_TOKEN {
+ end_mark = token.end_mark
+ parser.state = yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE
+ *event = yaml_event_t{
+ typ: yaml_MAPPING_START_EVENT,
+ start_mark: start_mark,
+ end_mark: end_mark,
+ anchor: anchor,
+ tag: tag,
+ implicit: implicit,
+ style: yaml_style_t(yaml_FLOW_MAPPING_STYLE),
+ }
+ yaml_parser_set_event_comments(parser, event)
+ return true
+ }
+ if block && token.typ == yaml_BLOCK_SEQUENCE_START_TOKEN {
+ end_mark = token.end_mark
+ parser.state = yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE
+ *event = yaml_event_t{
+ typ: yaml_SEQUENCE_START_EVENT,
+ start_mark: start_mark,
+ end_mark: end_mark,
+ anchor: anchor,
+ tag: tag,
+ implicit: implicit,
+ style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE),
+ }
+ if parser.stem_comment != nil {
+ event.head_comment = parser.stem_comment
+ parser.stem_comment = nil
+ }
+ return true
+ }
+ if block && token.typ == yaml_BLOCK_MAPPING_START_TOKEN {
+ end_mark = token.end_mark
+ parser.state = yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE
+ *event = yaml_event_t{
+ typ: yaml_MAPPING_START_EVENT,
+ start_mark: start_mark,
+ end_mark: end_mark,
+ anchor: anchor,
+ tag: tag,
+ implicit: implicit,
+ style: yaml_style_t(yaml_BLOCK_MAPPING_STYLE),
+ }
+ if parser.stem_comment != nil {
+ event.head_comment = parser.stem_comment
+ parser.stem_comment = nil
+ }
+ return true
+ }
+ if len(anchor) > 0 || len(tag) > 0 {
+ parser.state = parser.states[len(parser.states)-1]
+ parser.states = parser.states[:len(parser.states)-1]
+
+ *event = yaml_event_t{
+ typ: yaml_SCALAR_EVENT,
+ start_mark: start_mark,
+ end_mark: end_mark,
+ anchor: anchor,
+ tag: tag,
+ implicit: implicit,
+ quoted_implicit: false,
+ style: yaml_style_t(yaml_PLAIN_SCALAR_STYLE),
+ }
+ return true
+ }
+
+ context := "while parsing a flow node"
+ if block {
+ context = "while parsing a block node"
+ }
+ yaml_parser_set_parser_error_context(parser, context, start_mark,
+ "did not find expected node content", token.start_mark)
+ return false
+}
+
+// Parse the productions:
+// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END
+//
+// ******************** *********** * *********
+func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
+ if first {
+ token := peek_token(parser)
+ if token == nil {
+ return false
+ }
+ parser.marks = append(parser.marks, token.start_mark)
+ skip_token(parser)
+ }
+
+ token := peek_token(parser)
+ if token == nil {
+ return false
+ }
+
+ if token.typ == yaml_BLOCK_ENTRY_TOKEN {
+ mark := token.end_mark
+ prior_head_len := len(parser.head_comment)
+ skip_token(parser)
+ yaml_parser_split_stem_comment(parser, prior_head_len)
+ token = peek_token(parser)
+ if token == nil {
+ return false
+ }
+ if token.typ != yaml_BLOCK_ENTRY_TOKEN && token.typ != yaml_BLOCK_END_TOKEN {
+ parser.states = append(parser.states, yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE)
+ return yaml_parser_parse_node(parser, event, true, false)
+ } else {
+ parser.state = yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE
+ return yaml_parser_process_empty_scalar(parser, event, mark)
+ }
+ }
+ if token.typ == yaml_BLOCK_END_TOKEN {
+ parser.state = parser.states[len(parser.states)-1]
+ parser.states = parser.states[:len(parser.states)-1]
+ parser.marks = parser.marks[:len(parser.marks)-1]
+
+ *event = yaml_event_t{
+ typ: yaml_SEQUENCE_END_EVENT,
+ start_mark: token.start_mark,
+ end_mark: token.end_mark,
+ }
+
+ skip_token(parser)
+ return true
+ }
+
+ context_mark := parser.marks[len(parser.marks)-1]
+ parser.marks = parser.marks[:len(parser.marks)-1]
+ return yaml_parser_set_parser_error_context(parser,
+ "while parsing a block collection", context_mark,
+ "did not find expected '-' indicator", token.start_mark)
+}
+
+// Parse the productions:
+// indentless_sequence ::= (BLOCK-ENTRY block_node?)+
+//
+// *********** *
+func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *yaml_event_t) bool {
+ token := peek_token(parser)
+ if token == nil {
+ return false
+ }
+
+ if token.typ == yaml_BLOCK_ENTRY_TOKEN {
+ mark := token.end_mark
+ prior_head_len := len(parser.head_comment)
+ skip_token(parser)
+ yaml_parser_split_stem_comment(parser, prior_head_len)
+ token = peek_token(parser)
+ if token == nil {
+ return false
+ }
+ if token.typ != yaml_BLOCK_ENTRY_TOKEN &&
+ token.typ != yaml_KEY_TOKEN &&
+ token.typ != yaml_VALUE_TOKEN &&
+ token.typ != yaml_BLOCK_END_TOKEN {
+ parser.states = append(parser.states, yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE)
+ return yaml_parser_parse_node(parser, event, true, false)
+ }
+ parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE
+ return yaml_parser_process_empty_scalar(parser, event, mark)
+ }
+ parser.state = parser.states[len(parser.states)-1]
+ parser.states = parser.states[:len(parser.states)-1]
+
+ *event = yaml_event_t{
+ typ: yaml_SEQUENCE_END_EVENT,
+ start_mark: token.start_mark,
+ end_mark: token.start_mark, // [Go] Shouldn't this be token.end_mark?
+ }
+ return true
+}
+
+// Split stem comment from head comment.
+//
+// When a sequence or map is found under a sequence entry, the former head comment
+// is assigned to the underlying sequence or map as a whole, not the individual
+// sequence or map entry as would be expected otherwise. To handle this case the
+// previous head comment is moved aside as the stem comment.
+func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) {
+ if stem_len == 0 {
+ return
+ }
+
+ token := peek_token(parser)
+ if token == nil || token.typ != yaml_BLOCK_SEQUENCE_START_TOKEN && token.typ != yaml_BLOCK_MAPPING_START_TOKEN {
+ return
+ }
+
+ parser.stem_comment = parser.head_comment[:stem_len]
+ if len(parser.head_comment) == stem_len {
+ parser.head_comment = nil
+ } else {
+ // Copy suffix to prevent very strange bugs if someone ever appends
+ // further bytes to the prefix in the stem_comment slice above.
+ parser.head_comment = append([]byte(nil), parser.head_comment[stem_len+1:]...)
+ }
+}
+
+// Parse the productions:
+// block_mapping ::= BLOCK-MAPPING_START
+//
+// *******************
+// ((KEY block_node_or_indentless_sequence?)?
+// *** *
+// (VALUE block_node_or_indentless_sequence?)?)*
+//
+// BLOCK-END
+// *********
+func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
+ if first {
+ token := peek_token(parser)
+ if token == nil {
+ return false
+ }
+ parser.marks = append(parser.marks, token.start_mark)
+ skip_token(parser)
+ }
+
+ token := peek_token(parser)
+ if token == nil {
+ return false
+ }
+
+ // [Go] A tail comment was left from the prior mapping value processed. Emit an event
+ // as it needs to be processed with that value and not the following key.
+ if len(parser.tail_comment) > 0 {
+ *event = yaml_event_t{
+ typ: yaml_TAIL_COMMENT_EVENT,
+ start_mark: token.start_mark,
+ end_mark: token.end_mark,
+ foot_comment: parser.tail_comment,
+ }
+ parser.tail_comment = nil
+ return true
+ }
+
+ if token.typ == yaml_KEY_TOKEN {
+ mark := token.end_mark
+ skip_token(parser)
+ token = peek_token(parser)
+ if token == nil {
+ return false
+ }
+ if token.typ != yaml_KEY_TOKEN &&
+ token.typ != yaml_VALUE_TOKEN &&
+ token.typ != yaml_BLOCK_END_TOKEN {
+ parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_VALUE_STATE)
+ return yaml_parser_parse_node(parser, event, true, true)
+ } else {
+ parser.state = yaml_PARSE_BLOCK_MAPPING_VALUE_STATE
+ return yaml_parser_process_empty_scalar(parser, event, mark)
+ }
+ } else if token.typ == yaml_BLOCK_END_TOKEN {
+ parser.state = parser.states[len(parser.states)-1]
+ parser.states = parser.states[:len(parser.states)-1]
+ parser.marks = parser.marks[:len(parser.marks)-1]
+ *event = yaml_event_t{
+ typ: yaml_MAPPING_END_EVENT,
+ start_mark: token.start_mark,
+ end_mark: token.end_mark,
+ }
+ yaml_parser_set_event_comments(parser, event)
+ skip_token(parser)
+ return true
+ }
+
+ context_mark := parser.marks[len(parser.marks)-1]
+ parser.marks = parser.marks[:len(parser.marks)-1]
+ return yaml_parser_set_parser_error_context(parser,
+ "while parsing a block mapping", context_mark,
+ "did not find expected key", token.start_mark)
+}
+
+// Parse the productions:
+// block_mapping ::= BLOCK-MAPPING_START
+//
+// ((KEY block_node_or_indentless_sequence?)?
+//
+// (VALUE block_node_or_indentless_sequence?)?)*
+// ***** *
+// BLOCK-END
+func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool {
+ token := peek_token(parser)
+ if token == nil {
+ return false
+ }
+ if token.typ == yaml_VALUE_TOKEN {
+ mark := token.end_mark
+ skip_token(parser)
+ token = peek_token(parser)
+ if token == nil {
+ return false
+ }
+ if token.typ != yaml_KEY_TOKEN &&
+ token.typ != yaml_VALUE_TOKEN &&
+ token.typ != yaml_BLOCK_END_TOKEN {
+ parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_KEY_STATE)
+ return yaml_parser_parse_node(parser, event, true, true)
+ }
+ parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE
+ return yaml_parser_process_empty_scalar(parser, event, mark)
+ }
+ parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE
+ return yaml_parser_process_empty_scalar(parser, event, token.start_mark)
+}
+
+// Parse the productions:
+// flow_sequence ::= FLOW-SEQUENCE-START
+//
+// *******************
+// (flow_sequence_entry FLOW-ENTRY)*
+// * **********
+// flow_sequence_entry?
+// *
+// FLOW-SEQUENCE-END
+// *****************
+//
+// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+//
+// *
+func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
+ if first {
+ token := peek_token(parser)
+ if token == nil {
+ return false
+ }
+ parser.marks = append(parser.marks, token.start_mark)
+ skip_token(parser)
+ }
+ token := peek_token(parser)
+ if token == nil {
+ return false
+ }
+ if token.typ != yaml_FLOW_SEQUENCE_END_TOKEN {
+ if !first {
+ if token.typ == yaml_FLOW_ENTRY_TOKEN {
+ skip_token(parser)
+ token = peek_token(parser)
+ if token == nil {
+ return false
+ }
+ } else {
+ context_mark := parser.marks[len(parser.marks)-1]
+ parser.marks = parser.marks[:len(parser.marks)-1]
+ return yaml_parser_set_parser_error_context(parser,
+ "while parsing a flow sequence", context_mark,
+ "did not find expected ',' or ']'", token.start_mark)
+ }
+ }
+
+ if token.typ == yaml_KEY_TOKEN {
+ parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE
+ *event = yaml_event_t{
+ typ: yaml_MAPPING_START_EVENT,
+ start_mark: token.start_mark,
+ end_mark: token.end_mark,
+ implicit: true,
+ style: yaml_style_t(yaml_FLOW_MAPPING_STYLE),
+ }
+ skip_token(parser)
+ return true
+ } else if token.typ != yaml_FLOW_SEQUENCE_END_TOKEN {
+ parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE)
+ return yaml_parser_parse_node(parser, event, false, false)
+ }
+ }
+
+ parser.state = parser.states[len(parser.states)-1]
+ parser.states = parser.states[:len(parser.states)-1]
+ parser.marks = parser.marks[:len(parser.marks)-1]
+
+ *event = yaml_event_t{
+ typ: yaml_SEQUENCE_END_EVENT,
+ start_mark: token.start_mark,
+ end_mark: token.end_mark,
+ }
+ yaml_parser_set_event_comments(parser, event)
+
+ skip_token(parser)
+ return true
+}
+
+// Parse the productions:
+// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+//
+// *** *
+func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, event *yaml_event_t) bool {
+ token := peek_token(parser)
+ if token == nil {
+ return false
+ }
+ if token.typ != yaml_VALUE_TOKEN &&
+ token.typ != yaml_FLOW_ENTRY_TOKEN &&
+ token.typ != yaml_FLOW_SEQUENCE_END_TOKEN {
+ parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE)
+ return yaml_parser_parse_node(parser, event, false, false)
+ }
+ mark := token.end_mark
+ skip_token(parser)
+ parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE
+ return yaml_parser_process_empty_scalar(parser, event, mark)
+}
+
+// Parse the productions:
+// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+//
+// ***** *
+func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool {
+ token := peek_token(parser)
+ if token == nil {
+ return false
+ }
+ if token.typ == yaml_VALUE_TOKEN {
+ skip_token(parser)
+ token := peek_token(parser)
+ if token == nil {
+ return false
+ }
+ if token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_SEQUENCE_END_TOKEN {
+ parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE)
+ return yaml_parser_parse_node(parser, event, false, false)
+ }
+ }
+ parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE
+ return yaml_parser_process_empty_scalar(parser, event, token.start_mark)
+}
+
+// Parse the productions:
+// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+//
+// *
+func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, event *yaml_event_t) bool {
+ token := peek_token(parser)
+ if token == nil {
+ return false
+ }
+ parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE
+ *event = yaml_event_t{
+ typ: yaml_MAPPING_END_EVENT,
+ start_mark: token.start_mark,
+ end_mark: token.start_mark, // [Go] Shouldn't this be end_mark?
+ }
+ return true
+}
+
+// Parse the productions:
+// flow_mapping ::= FLOW-MAPPING-START
+//
+// ******************
+// (flow_mapping_entry FLOW-ENTRY)*
+// * **********
+// flow_mapping_entry?
+// ******************
+// FLOW-MAPPING-END
+// ****************
+//
+// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+// - *** *
+func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool {
+ if first {
+ token := peek_token(parser)
+ parser.marks = append(parser.marks, token.start_mark)
+ skip_token(parser)
+ }
+
+ token := peek_token(parser)
+ if token == nil {
+ return false
+ }
+
+ if token.typ != yaml_FLOW_MAPPING_END_TOKEN {
+ if !first {
+ if token.typ == yaml_FLOW_ENTRY_TOKEN {
+ skip_token(parser)
+ token = peek_token(parser)
+ if token == nil {
+ return false
+ }
+ } else {
+ context_mark := parser.marks[len(parser.marks)-1]
+ parser.marks = parser.marks[:len(parser.marks)-1]
+ return yaml_parser_set_parser_error_context(parser,
+ "while parsing a flow mapping", context_mark,
+ "did not find expected ',' or '}'", token.start_mark)
+ }
+ }
+
+ if token.typ == yaml_KEY_TOKEN {
+ skip_token(parser)
+ token = peek_token(parser)
+ if token == nil {
+ return false
+ }
+ if token.typ != yaml_VALUE_TOKEN &&
+ token.typ != yaml_FLOW_ENTRY_TOKEN &&
+ token.typ != yaml_FLOW_MAPPING_END_TOKEN {
+ parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_VALUE_STATE)
+ return yaml_parser_parse_node(parser, event, false, false)
+ } else {
+ parser.state = yaml_PARSE_FLOW_MAPPING_VALUE_STATE
+ return yaml_parser_process_empty_scalar(parser, event, token.start_mark)
+ }
+ } else if token.typ != yaml_FLOW_MAPPING_END_TOKEN {
+ parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE)
+ return yaml_parser_parse_node(parser, event, false, false)
+ }
+ }
+
+ parser.state = parser.states[len(parser.states)-1]
+ parser.states = parser.states[:len(parser.states)-1]
+ parser.marks = parser.marks[:len(parser.marks)-1]
+ *event = yaml_event_t{
+ typ: yaml_MAPPING_END_EVENT,
+ start_mark: token.start_mark,
+ end_mark: token.end_mark,
+ }
+ yaml_parser_set_event_comments(parser, event)
+ skip_token(parser)
+ return true
+}
+
+// Parse the productions:
+// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)?
+// - ***** *
+func yaml_parser_parse_flow_mapping_value(parser *yaml_parser_t, event *yaml_event_t, empty bool) bool {
+ token := peek_token(parser)
+ if token == nil {
+ return false
+ }
+ if empty {
+ parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE
+ return yaml_parser_process_empty_scalar(parser, event, token.start_mark)
+ }
+ if token.typ == yaml_VALUE_TOKEN {
+ skip_token(parser)
+ token = peek_token(parser)
+ if token == nil {
+ return false
+ }
+ if token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_MAPPING_END_TOKEN {
+ parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_KEY_STATE)
+ return yaml_parser_parse_node(parser, event, false, false)
+ }
+ }
+ parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE
+ return yaml_parser_process_empty_scalar(parser, event, token.start_mark)
+}
+
+// Generate an empty scalar event.
+func yaml_parser_process_empty_scalar(parser *yaml_parser_t, event *yaml_event_t, mark yaml_mark_t) bool {
+ *event = yaml_event_t{
+ typ: yaml_SCALAR_EVENT,
+ start_mark: mark,
+ end_mark: mark,
+ value: nil, // Empty
+ implicit: true,
+ style: yaml_style_t(yaml_PLAIN_SCALAR_STYLE),
+ }
+ return true
+}
+
+var default_tag_directives = []yaml_tag_directive_t{
+ {[]byte("!"), []byte("!")},
+ {[]byte("!!"), []byte("tag:yaml.org,2002:")},
+}
+
+// Parse directives.
+func yaml_parser_process_directives(parser *yaml_parser_t,
+ version_directive_ref **yaml_version_directive_t,
+ tag_directives_ref *[]yaml_tag_directive_t) bool {
+
+ var version_directive *yaml_version_directive_t
+ var tag_directives []yaml_tag_directive_t
+
+ token := peek_token(parser)
+ if token == nil {
+ return false
+ }
+
+ for token.typ == yaml_VERSION_DIRECTIVE_TOKEN || token.typ == yaml_TAG_DIRECTIVE_TOKEN {
+ if token.typ == yaml_VERSION_DIRECTIVE_TOKEN {
+ if version_directive != nil {
+ yaml_parser_set_parser_error(parser,
+ "found duplicate %YAML directive", token.start_mark)
+ return false
+ }
+ if token.major != 1 || token.minor != 1 {
+ yaml_parser_set_parser_error(parser,
+ "found incompatible YAML document", token.start_mark)
+ return false
+ }
+ version_directive = &yaml_version_directive_t{
+ major: token.major,
+ minor: token.minor,
+ }
+ } else if token.typ == yaml_TAG_DIRECTIVE_TOKEN {
+ value := yaml_tag_directive_t{
+ handle: token.value,
+ prefix: token.prefix,
+ }
+ if !yaml_parser_append_tag_directive(parser, value, false, token.start_mark) {
+ return false
+ }
+ tag_directives = append(tag_directives, value)
+ }
+
+ skip_token(parser)
+ token = peek_token(parser)
+ if token == nil {
+ return false
+ }
+ }
+
+ for i := range default_tag_directives {
+ if !yaml_parser_append_tag_directive(parser, default_tag_directives[i], true, token.start_mark) {
+ return false
+ }
+ }
+
+ if version_directive_ref != nil {
+ *version_directive_ref = version_directive
+ }
+ if tag_directives_ref != nil {
+ *tag_directives_ref = tag_directives
+ }
+ return true
+}
+
+// Append a tag directive to the directives stack.
+func yaml_parser_append_tag_directive(parser *yaml_parser_t, value yaml_tag_directive_t, allow_duplicates bool, mark yaml_mark_t) bool {
+ for i := range parser.tag_directives {
+ if bytes.Equal(value.handle, parser.tag_directives[i].handle) {
+ if allow_duplicates {
+ return true
+ }
+ return yaml_parser_set_parser_error(parser, "found duplicate %TAG directive", mark)
+ }
+ }
+
+ // [Go] I suspect the copy is unnecessary. This was likely done
+ // because there was no way to track ownership of the data.
+ value_copy := yaml_tag_directive_t{
+ handle: make([]byte, len(value.handle)),
+ prefix: make([]byte, len(value.prefix)),
+ }
+ copy(value_copy.handle, value.handle)
+ copy(value_copy.prefix, value.prefix)
+ parser.tag_directives = append(parser.tag_directives, value_copy)
+ return true
+}
diff --git a/vendor/go.yaml.in/yaml/v3/readerc.go b/vendor/go.yaml.in/yaml/v3/readerc.go
new file mode 100644
index 000000000..56af24536
--- /dev/null
+++ b/vendor/go.yaml.in/yaml/v3/readerc.go
@@ -0,0 +1,434 @@
+//
+// Copyright (c) 2011-2019 Canonical Ltd
+// Copyright (c) 2006-2010 Kirill Simonov
+//
+// 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.
+
+package yaml
+
+import (
+ "io"
+)
+
+// Set the reader error and return 0.
+func yaml_parser_set_reader_error(parser *yaml_parser_t, problem string, offset int, value int) bool {
+ parser.error = yaml_READER_ERROR
+ parser.problem = problem
+ parser.problem_offset = offset
+ parser.problem_value = value
+ return false
+}
+
+// Byte order marks.
+const (
+ bom_UTF8 = "\xef\xbb\xbf"
+ bom_UTF16LE = "\xff\xfe"
+ bom_UTF16BE = "\xfe\xff"
+)
+
+// Determine the input stream encoding by checking the BOM symbol. If no BOM is
+// found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure.
+func yaml_parser_determine_encoding(parser *yaml_parser_t) bool {
+ // Ensure that we had enough bytes in the raw buffer.
+ for !parser.eof && len(parser.raw_buffer)-parser.raw_buffer_pos < 3 {
+ if !yaml_parser_update_raw_buffer(parser) {
+ return false
+ }
+ }
+
+ // Determine the encoding.
+ buf := parser.raw_buffer
+ pos := parser.raw_buffer_pos
+ avail := len(buf) - pos
+ if avail >= 2 && buf[pos] == bom_UTF16LE[0] && buf[pos+1] == bom_UTF16LE[1] {
+ parser.encoding = yaml_UTF16LE_ENCODING
+ parser.raw_buffer_pos += 2
+ parser.offset += 2
+ } else if avail >= 2 && buf[pos] == bom_UTF16BE[0] && buf[pos+1] == bom_UTF16BE[1] {
+ parser.encoding = yaml_UTF16BE_ENCODING
+ parser.raw_buffer_pos += 2
+ parser.offset += 2
+ } else if avail >= 3 && buf[pos] == bom_UTF8[0] && buf[pos+1] == bom_UTF8[1] && buf[pos+2] == bom_UTF8[2] {
+ parser.encoding = yaml_UTF8_ENCODING
+ parser.raw_buffer_pos += 3
+ parser.offset += 3
+ } else {
+ parser.encoding = yaml_UTF8_ENCODING
+ }
+ return true
+}
+
+// Update the raw buffer.
+func yaml_parser_update_raw_buffer(parser *yaml_parser_t) bool {
+ size_read := 0
+
+ // Return if the raw buffer is full.
+ if parser.raw_buffer_pos == 0 && len(parser.raw_buffer) == cap(parser.raw_buffer) {
+ return true
+ }
+
+ // Return on EOF.
+ if parser.eof {
+ return true
+ }
+
+ // Move the remaining bytes in the raw buffer to the beginning.
+ if parser.raw_buffer_pos > 0 && parser.raw_buffer_pos < len(parser.raw_buffer) {
+ copy(parser.raw_buffer, parser.raw_buffer[parser.raw_buffer_pos:])
+ }
+ parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)-parser.raw_buffer_pos]
+ parser.raw_buffer_pos = 0
+
+ // Call the read handler to fill the buffer.
+ size_read, err := parser.read_handler(parser, parser.raw_buffer[len(parser.raw_buffer):cap(parser.raw_buffer)])
+ parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)+size_read]
+ if err == io.EOF {
+ parser.eof = true
+ } else if err != nil {
+ return yaml_parser_set_reader_error(parser, "input error: "+err.Error(), parser.offset, -1)
+ }
+ return true
+}
+
+// Ensure that the buffer contains at least `length` characters.
+// Return true on success, false on failure.
+//
+// The length is supposed to be significantly less that the buffer size.
+func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool {
+ if parser.read_handler == nil {
+ panic("read handler must be set")
+ }
+
+ // [Go] This function was changed to guarantee the requested length size at EOF.
+ // The fact we need to do this is pretty awful, but the description above implies
+ // for that to be the case, and there are tests
+
+ // If the EOF flag is set and the raw buffer is empty, do nothing.
+ if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) {
+ // [Go] ACTUALLY! Read the documentation of this function above.
+ // This is just broken. To return true, we need to have the
+ // given length in the buffer. Not doing that means every single
+ // check that calls this function to make sure the buffer has a
+ // given length is Go) panicking; or C) accessing invalid memory.
+ //return true
+ }
+
+ // Return if the buffer contains enough characters.
+ if parser.unread >= length {
+ return true
+ }
+
+ // Determine the input encoding if it is not known yet.
+ if parser.encoding == yaml_ANY_ENCODING {
+ if !yaml_parser_determine_encoding(parser) {
+ return false
+ }
+ }
+
+ // Move the unread characters to the beginning of the buffer.
+ buffer_len := len(parser.buffer)
+ if parser.buffer_pos > 0 && parser.buffer_pos < buffer_len {
+ copy(parser.buffer, parser.buffer[parser.buffer_pos:])
+ buffer_len -= parser.buffer_pos
+ parser.buffer_pos = 0
+ } else if parser.buffer_pos == buffer_len {
+ buffer_len = 0
+ parser.buffer_pos = 0
+ }
+
+ // Open the whole buffer for writing, and cut it before returning.
+ parser.buffer = parser.buffer[:cap(parser.buffer)]
+
+ // Fill the buffer until it has enough characters.
+ first := true
+ for parser.unread < length {
+
+ // Fill the raw buffer if necessary.
+ if !first || parser.raw_buffer_pos == len(parser.raw_buffer) {
+ if !yaml_parser_update_raw_buffer(parser) {
+ parser.buffer = parser.buffer[:buffer_len]
+ return false
+ }
+ }
+ first = false
+
+ // Decode the raw buffer.
+ inner:
+ for parser.raw_buffer_pos != len(parser.raw_buffer) {
+ var value rune
+ var width int
+
+ raw_unread := len(parser.raw_buffer) - parser.raw_buffer_pos
+
+ // Decode the next character.
+ switch parser.encoding {
+ case yaml_UTF8_ENCODING:
+ // Decode a UTF-8 character. Check RFC 3629
+ // (http://www.ietf.org/rfc/rfc3629.txt) for more details.
+ //
+ // The following table (taken from the RFC) is used for
+ // decoding.
+ //
+ // Char. number range | UTF-8 octet sequence
+ // (hexadecimal) | (binary)
+ // --------------------+------------------------------------
+ // 0000 0000-0000 007F | 0xxxxxxx
+ // 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
+ // 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
+ // 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ //
+ // Additionally, the characters in the range 0xD800-0xDFFF
+ // are prohibited as they are reserved for use with UTF-16
+ // surrogate pairs.
+
+ // Determine the length of the UTF-8 sequence.
+ octet := parser.raw_buffer[parser.raw_buffer_pos]
+ switch {
+ case octet&0x80 == 0x00:
+ width = 1
+ case octet&0xE0 == 0xC0:
+ width = 2
+ case octet&0xF0 == 0xE0:
+ width = 3
+ case octet&0xF8 == 0xF0:
+ width = 4
+ default:
+ // The leading octet is invalid.
+ return yaml_parser_set_reader_error(parser,
+ "invalid leading UTF-8 octet",
+ parser.offset, int(octet))
+ }
+
+ // Check if the raw buffer contains an incomplete character.
+ if width > raw_unread {
+ if parser.eof {
+ return yaml_parser_set_reader_error(parser,
+ "incomplete UTF-8 octet sequence",
+ parser.offset, -1)
+ }
+ break inner
+ }
+
+ // Decode the leading octet.
+ switch {
+ case octet&0x80 == 0x00:
+ value = rune(octet & 0x7F)
+ case octet&0xE0 == 0xC0:
+ value = rune(octet & 0x1F)
+ case octet&0xF0 == 0xE0:
+ value = rune(octet & 0x0F)
+ case octet&0xF8 == 0xF0:
+ value = rune(octet & 0x07)
+ default:
+ value = 0
+ }
+
+ // Check and decode the trailing octets.
+ for k := 1; k < width; k++ {
+ octet = parser.raw_buffer[parser.raw_buffer_pos+k]
+
+ // Check if the octet is valid.
+ if (octet & 0xC0) != 0x80 {
+ return yaml_parser_set_reader_error(parser,
+ "invalid trailing UTF-8 octet",
+ parser.offset+k, int(octet))
+ }
+
+ // Decode the octet.
+ value = (value << 6) + rune(octet&0x3F)
+ }
+
+ // Check the length of the sequence against the value.
+ switch {
+ case width == 1:
+ case width == 2 && value >= 0x80:
+ case width == 3 && value >= 0x800:
+ case width == 4 && value >= 0x10000:
+ default:
+ return yaml_parser_set_reader_error(parser,
+ "invalid length of a UTF-8 sequence",
+ parser.offset, -1)
+ }
+
+ // Check the range of the value.
+ if value >= 0xD800 && value <= 0xDFFF || value > 0x10FFFF {
+ return yaml_parser_set_reader_error(parser,
+ "invalid Unicode character",
+ parser.offset, int(value))
+ }
+
+ case yaml_UTF16LE_ENCODING, yaml_UTF16BE_ENCODING:
+ var low, high int
+ if parser.encoding == yaml_UTF16LE_ENCODING {
+ low, high = 0, 1
+ } else {
+ low, high = 1, 0
+ }
+
+ // The UTF-16 encoding is not as simple as one might
+ // naively think. Check RFC 2781
+ // (http://www.ietf.org/rfc/rfc2781.txt).
+ //
+ // Normally, two subsequent bytes describe a Unicode
+ // character. However a special technique (called a
+ // surrogate pair) is used for specifying character
+ // values larger than 0xFFFF.
+ //
+ // A surrogate pair consists of two pseudo-characters:
+ // high surrogate area (0xD800-0xDBFF)
+ // low surrogate area (0xDC00-0xDFFF)
+ //
+ // The following formulas are used for decoding
+ // and encoding characters using surrogate pairs:
+ //
+ // U = U' + 0x10000 (0x01 00 00 <= U <= 0x10 FF FF)
+ // U' = yyyyyyyyyyxxxxxxxxxx (0 <= U' <= 0x0F FF FF)
+ // W1 = 110110yyyyyyyyyy
+ // W2 = 110111xxxxxxxxxx
+ //
+ // where U is the character value, W1 is the high surrogate
+ // area, W2 is the low surrogate area.
+
+ // Check for incomplete UTF-16 character.
+ if raw_unread < 2 {
+ if parser.eof {
+ return yaml_parser_set_reader_error(parser,
+ "incomplete UTF-16 character",
+ parser.offset, -1)
+ }
+ break inner
+ }
+
+ // Get the character.
+ value = rune(parser.raw_buffer[parser.raw_buffer_pos+low]) +
+ (rune(parser.raw_buffer[parser.raw_buffer_pos+high]) << 8)
+
+ // Check for unexpected low surrogate area.
+ if value&0xFC00 == 0xDC00 {
+ return yaml_parser_set_reader_error(parser,
+ "unexpected low surrogate area",
+ parser.offset, int(value))
+ }
+
+ // Check for a high surrogate area.
+ if value&0xFC00 == 0xD800 {
+ width = 4
+
+ // Check for incomplete surrogate pair.
+ if raw_unread < 4 {
+ if parser.eof {
+ return yaml_parser_set_reader_error(parser,
+ "incomplete UTF-16 surrogate pair",
+ parser.offset, -1)
+ }
+ break inner
+ }
+
+ // Get the next character.
+ value2 := rune(parser.raw_buffer[parser.raw_buffer_pos+low+2]) +
+ (rune(parser.raw_buffer[parser.raw_buffer_pos+high+2]) << 8)
+
+ // Check for a low surrogate area.
+ if value2&0xFC00 != 0xDC00 {
+ return yaml_parser_set_reader_error(parser,
+ "expected low surrogate area",
+ parser.offset+2, int(value2))
+ }
+
+ // Generate the value of the surrogate pair.
+ value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF)
+ } else {
+ width = 2
+ }
+
+ default:
+ panic("impossible")
+ }
+
+ // Check if the character is in the allowed range:
+ // #x9 | #xA | #xD | [#x20-#x7E] (8 bit)
+ // | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] (16 bit)
+ // | [#x10000-#x10FFFF] (32 bit)
+ switch {
+ case value == 0x09:
+ case value == 0x0A:
+ case value == 0x0D:
+ case value >= 0x20 && value <= 0x7E:
+ case value == 0x85:
+ case value >= 0xA0 && value <= 0xD7FF:
+ case value >= 0xE000 && value <= 0xFFFD:
+ case value >= 0x10000 && value <= 0x10FFFF:
+ default:
+ return yaml_parser_set_reader_error(parser,
+ "control characters are not allowed",
+ parser.offset, int(value))
+ }
+
+ // Move the raw pointers.
+ parser.raw_buffer_pos += width
+ parser.offset += width
+
+ // Finally put the character into the buffer.
+ if value <= 0x7F {
+ // 0000 0000-0000 007F . 0xxxxxxx
+ parser.buffer[buffer_len+0] = byte(value)
+ buffer_len += 1
+ } else if value <= 0x7FF {
+ // 0000 0080-0000 07FF . 110xxxxx 10xxxxxx
+ parser.buffer[buffer_len+0] = byte(0xC0 + (value >> 6))
+ parser.buffer[buffer_len+1] = byte(0x80 + (value & 0x3F))
+ buffer_len += 2
+ } else if value <= 0xFFFF {
+ // 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx
+ parser.buffer[buffer_len+0] = byte(0xE0 + (value >> 12))
+ parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 6) & 0x3F))
+ parser.buffer[buffer_len+2] = byte(0x80 + (value & 0x3F))
+ buffer_len += 3
+ } else {
+ // 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ parser.buffer[buffer_len+0] = byte(0xF0 + (value >> 18))
+ parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 12) & 0x3F))
+ parser.buffer[buffer_len+2] = byte(0x80 + ((value >> 6) & 0x3F))
+ parser.buffer[buffer_len+3] = byte(0x80 + (value & 0x3F))
+ buffer_len += 4
+ }
+
+ parser.unread++
+ }
+
+ // On EOF, put NUL into the buffer and return.
+ if parser.eof {
+ parser.buffer[buffer_len] = 0
+ buffer_len++
+ parser.unread++
+ break
+ }
+ }
+ // [Go] Read the documentation of this function above. To return true,
+ // we need to have the given length in the buffer. Not doing that means
+ // every single check that calls this function to make sure the buffer
+ // has a given length is Go) panicking; or C) accessing invalid memory.
+ // This happens here due to the EOF above breaking early.
+ for buffer_len < length {
+ parser.buffer[buffer_len] = 0
+ buffer_len++
+ }
+ parser.buffer = parser.buffer[:buffer_len]
+ return true
+}
diff --git a/vendor/go.yaml.in/yaml/v3/resolve.go b/vendor/go.yaml.in/yaml/v3/resolve.go
new file mode 100644
index 000000000..64ae88805
--- /dev/null
+++ b/vendor/go.yaml.in/yaml/v3/resolve.go
@@ -0,0 +1,326 @@
+//
+// Copyright (c) 2011-2019 Canonical Ltd
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package yaml
+
+import (
+ "encoding/base64"
+ "math"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+)
+
+type resolveMapItem struct {
+ value interface{}
+ tag string
+}
+
+var resolveTable = make([]byte, 256)
+var resolveMap = make(map[string]resolveMapItem)
+
+func init() {
+ t := resolveTable
+ t[int('+')] = 'S' // Sign
+ t[int('-')] = 'S'
+ for _, c := range "0123456789" {
+ t[int(c)] = 'D' // Digit
+ }
+ for _, c := range "yYnNtTfFoO~" {
+ t[int(c)] = 'M' // In map
+ }
+ t[int('.')] = '.' // Float (potentially in map)
+
+ var resolveMapList = []struct {
+ v interface{}
+ tag string
+ l []string
+ }{
+ {true, boolTag, []string{"true", "True", "TRUE"}},
+ {false, boolTag, []string{"false", "False", "FALSE"}},
+ {nil, nullTag, []string{"", "~", "null", "Null", "NULL"}},
+ {math.NaN(), floatTag, []string{".nan", ".NaN", ".NAN"}},
+ {math.Inf(+1), floatTag, []string{".inf", ".Inf", ".INF"}},
+ {math.Inf(+1), floatTag, []string{"+.inf", "+.Inf", "+.INF"}},
+ {math.Inf(-1), floatTag, []string{"-.inf", "-.Inf", "-.INF"}},
+ {"<<", mergeTag, []string{"<<"}},
+ }
+
+ m := resolveMap
+ for _, item := range resolveMapList {
+ for _, s := range item.l {
+ m[s] = resolveMapItem{item.v, item.tag}
+ }
+ }
+}
+
+const (
+ nullTag = "!!null"
+ boolTag = "!!bool"
+ strTag = "!!str"
+ intTag = "!!int"
+ floatTag = "!!float"
+ timestampTag = "!!timestamp"
+ seqTag = "!!seq"
+ mapTag = "!!map"
+ binaryTag = "!!binary"
+ mergeTag = "!!merge"
+)
+
+var longTags = make(map[string]string)
+var shortTags = make(map[string]string)
+
+func init() {
+ for _, stag := range []string{nullTag, boolTag, strTag, intTag, floatTag, timestampTag, seqTag, mapTag, binaryTag, mergeTag} {
+ ltag := longTag(stag)
+ longTags[stag] = ltag
+ shortTags[ltag] = stag
+ }
+}
+
+const longTagPrefix = "tag:yaml.org,2002:"
+
+func shortTag(tag string) string {
+ if strings.HasPrefix(tag, longTagPrefix) {
+ if stag, ok := shortTags[tag]; ok {
+ return stag
+ }
+ return "!!" + tag[len(longTagPrefix):]
+ }
+ return tag
+}
+
+func longTag(tag string) string {
+ if strings.HasPrefix(tag, "!!") {
+ if ltag, ok := longTags[tag]; ok {
+ return ltag
+ }
+ return longTagPrefix + tag[2:]
+ }
+ return tag
+}
+
+func resolvableTag(tag string) bool {
+ switch tag {
+ case "", strTag, boolTag, intTag, floatTag, nullTag, timestampTag:
+ return true
+ }
+ return false
+}
+
+var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`)
+
+func resolve(tag string, in string) (rtag string, out interface{}) {
+ tag = shortTag(tag)
+ if !resolvableTag(tag) {
+ return tag, in
+ }
+
+ defer func() {
+ switch tag {
+ case "", rtag, strTag, binaryTag:
+ return
+ case floatTag:
+ if rtag == intTag {
+ switch v := out.(type) {
+ case int64:
+ rtag = floatTag
+ out = float64(v)
+ return
+ case int:
+ rtag = floatTag
+ out = float64(v)
+ return
+ }
+ }
+ }
+ failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag))
+ }()
+
+ // Any data is accepted as a !!str or !!binary.
+ // Otherwise, the prefix is enough of a hint about what it might be.
+ hint := byte('N')
+ if in != "" {
+ hint = resolveTable[in[0]]
+ }
+ if hint != 0 && tag != strTag && tag != binaryTag {
+ // Handle things we can lookup in a map.
+ if item, ok := resolveMap[in]; ok {
+ return item.tag, item.value
+ }
+
+ // Base 60 floats are a bad idea, were dropped in YAML 1.2, and
+ // are purposefully unsupported here. They're still quoted on
+ // the way out for compatibility with other parser, though.
+
+ switch hint {
+ case 'M':
+ // We've already checked the map above.
+
+ case '.':
+ // Not in the map, so maybe a normal float.
+ floatv, err := strconv.ParseFloat(in, 64)
+ if err == nil {
+ return floatTag, floatv
+ }
+
+ case 'D', 'S':
+ // Int, float, or timestamp.
+ // Only try values as a timestamp if the value is unquoted or there's an explicit
+ // !!timestamp tag.
+ if tag == "" || tag == timestampTag {
+ t, ok := parseTimestamp(in)
+ if ok {
+ return timestampTag, t
+ }
+ }
+
+ plain := strings.Replace(in, "_", "", -1)
+ intv, err := strconv.ParseInt(plain, 0, 64)
+ if err == nil {
+ if intv == int64(int(intv)) {
+ return intTag, int(intv)
+ } else {
+ return intTag, intv
+ }
+ }
+ uintv, err := strconv.ParseUint(plain, 0, 64)
+ if err == nil {
+ return intTag, uintv
+ }
+ if yamlStyleFloat.MatchString(plain) {
+ floatv, err := strconv.ParseFloat(plain, 64)
+ if err == nil {
+ return floatTag, floatv
+ }
+ }
+ if strings.HasPrefix(plain, "0b") {
+ intv, err := strconv.ParseInt(plain[2:], 2, 64)
+ if err == nil {
+ if intv == int64(int(intv)) {
+ return intTag, int(intv)
+ } else {
+ return intTag, intv
+ }
+ }
+ uintv, err := strconv.ParseUint(plain[2:], 2, 64)
+ if err == nil {
+ return intTag, uintv
+ }
+ } else if strings.HasPrefix(plain, "-0b") {
+ intv, err := strconv.ParseInt("-"+plain[3:], 2, 64)
+ if err == nil {
+ if true || intv == int64(int(intv)) {
+ return intTag, int(intv)
+ } else {
+ return intTag, intv
+ }
+ }
+ }
+ // Octals as introduced in version 1.2 of the spec.
+ // Octals from the 1.1 spec, spelled as 0777, are still
+ // decoded by default in v3 as well for compatibility.
+ // May be dropped in v4 depending on how usage evolves.
+ if strings.HasPrefix(plain, "0o") {
+ intv, err := strconv.ParseInt(plain[2:], 8, 64)
+ if err == nil {
+ if intv == int64(int(intv)) {
+ return intTag, int(intv)
+ } else {
+ return intTag, intv
+ }
+ }
+ uintv, err := strconv.ParseUint(plain[2:], 8, 64)
+ if err == nil {
+ return intTag, uintv
+ }
+ } else if strings.HasPrefix(plain, "-0o") {
+ intv, err := strconv.ParseInt("-"+plain[3:], 8, 64)
+ if err == nil {
+ if true || intv == int64(int(intv)) {
+ return intTag, int(intv)
+ } else {
+ return intTag, intv
+ }
+ }
+ }
+ default:
+ panic("internal error: missing handler for resolver table: " + string(rune(hint)) + " (with " + in + ")")
+ }
+ }
+ return strTag, in
+}
+
+// encodeBase64 encodes s as base64 that is broken up into multiple lines
+// as appropriate for the resulting length.
+func encodeBase64(s string) string {
+ const lineLen = 70
+ encLen := base64.StdEncoding.EncodedLen(len(s))
+ lines := encLen/lineLen + 1
+ buf := make([]byte, encLen*2+lines)
+ in := buf[0:encLen]
+ out := buf[encLen:]
+ base64.StdEncoding.Encode(in, []byte(s))
+ k := 0
+ for i := 0; i < len(in); i += lineLen {
+ j := i + lineLen
+ if j > len(in) {
+ j = len(in)
+ }
+ k += copy(out[k:], in[i:j])
+ if lines > 1 {
+ out[k] = '\n'
+ k++
+ }
+ }
+ return string(out[:k])
+}
+
+// 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
+ // Notable exception: time.Parse cannot handle: "2001-12-14 21:59:43.10 -5"
+ // from the set of examples.
+}
+
+// parseTimestamp parses s as a timestamp string and
+// returns the timestamp and reports whether it succeeded.
+// Timestamp formats are defined at http://yaml.org/type/timestamp.html
+func parseTimestamp(s string) (time.Time, bool) {
+ // TODO write code to check all the formats supported by
+ // http://yaml.org/type/timestamp.html instead of using time.Parse.
+
+ // Quick check: all date formats start with YYYY-.
+ i := 0
+ for ; i < len(s); i++ {
+ if c := s[i]; c < '0' || c > '9' {
+ break
+ }
+ }
+ if i != 4 || i == len(s) || s[i] != '-' {
+ return time.Time{}, false
+ }
+ for _, format := range allowedTimestampFormats {
+ if t, err := time.Parse(format, s); err == nil {
+ return t, true
+ }
+ }
+ return time.Time{}, false
+}
diff --git a/vendor/go.yaml.in/yaml/v3/scannerc.go b/vendor/go.yaml.in/yaml/v3/scannerc.go
new file mode 100644
index 000000000..30b1f0892
--- /dev/null
+++ b/vendor/go.yaml.in/yaml/v3/scannerc.go
@@ -0,0 +1,3040 @@
+//
+// Copyright (c) 2011-2019 Canonical Ltd
+// Copyright (c) 2006-2010 Kirill Simonov
+//
+// 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.
+
+package yaml
+
+import (
+ "bytes"
+ "fmt"
+)
+
+// Introduction
+// ************
+//
+// The following notes assume that you are familiar with the YAML specification
+// (http://yaml.org/spec/1.2/spec.html). We mostly follow it, although in
+// some cases we are less restrictive that it requires.
+//
+// The process of transforming a YAML stream into a sequence of events is
+// divided on two steps: Scanning and Parsing.
+//
+// The Scanner transforms the input stream into a sequence of tokens, while the
+// parser transform the sequence of tokens produced by the Scanner into a
+// sequence of parsing events.
+//
+// The Scanner is rather clever and complicated. The Parser, on the contrary,
+// is a straightforward implementation of a recursive-descendant parser (or,
+// LL(1) parser, as it is usually called).
+//
+// Actually there are two issues of Scanning that might be called "clever", the
+// rest is quite straightforward. The issues are "block collection start" and
+// "simple keys". Both issues are explained below in details.
+//
+// Here the Scanning step is explained and implemented. We start with the list
+// of all the tokens produced by the Scanner together with short descriptions.
+//
+// Now, tokens:
+//
+// STREAM-START(encoding) # The stream start.
+// STREAM-END # The stream end.
+// VERSION-DIRECTIVE(major,minor) # The '%YAML' directive.
+// TAG-DIRECTIVE(handle,prefix) # The '%TAG' directive.
+// DOCUMENT-START # '---'
+// DOCUMENT-END # '...'
+// BLOCK-SEQUENCE-START # Indentation increase denoting a block
+// BLOCK-MAPPING-START # sequence or a block mapping.
+// BLOCK-END # Indentation decrease.
+// FLOW-SEQUENCE-START # '['
+// FLOW-SEQUENCE-END # ']'
+// BLOCK-SEQUENCE-START # '{'
+// BLOCK-SEQUENCE-END # '}'
+// BLOCK-ENTRY # '-'
+// FLOW-ENTRY # ','
+// KEY # '?' or nothing (simple keys).
+// VALUE # ':'
+// ALIAS(anchor) # '*anchor'
+// ANCHOR(anchor) # '&anchor'
+// TAG(handle,suffix) # '!handle!suffix'
+// SCALAR(value,style) # A scalar.
+//
+// The following two tokens are "virtual" tokens denoting the beginning and the
+// end of the stream:
+//
+// STREAM-START(encoding)
+// STREAM-END
+//
+// We pass the information about the input stream encoding with the
+// STREAM-START token.
+//
+// The next two tokens are responsible for tags:
+//
+// VERSION-DIRECTIVE(major,minor)
+// TAG-DIRECTIVE(handle,prefix)
+//
+// Example:
+//
+// %YAML 1.1
+// %TAG ! !foo
+// %TAG !yaml! tag:yaml.org,2002:
+// ---
+//
+// The correspoding sequence of tokens:
+//
+// STREAM-START(utf-8)
+// VERSION-DIRECTIVE(1,1)
+// TAG-DIRECTIVE("!","!foo")
+// TAG-DIRECTIVE("!yaml","tag:yaml.org,2002:")
+// DOCUMENT-START
+// STREAM-END
+//
+// Note that the VERSION-DIRECTIVE and TAG-DIRECTIVE tokens occupy a whole
+// line.
+//
+// The document start and end indicators are represented by:
+//
+// DOCUMENT-START
+// DOCUMENT-END
+//
+// Note that if a YAML stream contains an implicit document (without '---'
+// and '...' indicators), no DOCUMENT-START and DOCUMENT-END tokens will be
+// produced.
+//
+// In the following examples, we present whole documents together with the
+// produced tokens.
+//
+// 1. An implicit document:
+//
+// 'a scalar'
+//
+// Tokens:
+//
+// STREAM-START(utf-8)
+// SCALAR("a scalar",single-quoted)
+// STREAM-END
+//
+// 2. An explicit document:
+//
+// ---
+// 'a scalar'
+// ...
+//
+// Tokens:
+//
+// STREAM-START(utf-8)
+// DOCUMENT-START
+// SCALAR("a scalar",single-quoted)
+// DOCUMENT-END
+// STREAM-END
+//
+// 3. Several documents in a stream:
+//
+// 'a scalar'
+// ---
+// 'another scalar'
+// ---
+// 'yet another scalar'
+//
+// Tokens:
+//
+// STREAM-START(utf-8)
+// SCALAR("a scalar",single-quoted)
+// DOCUMENT-START
+// SCALAR("another scalar",single-quoted)
+// DOCUMENT-START
+// SCALAR("yet another scalar",single-quoted)
+// STREAM-END
+//
+// We have already introduced the SCALAR token above. The following tokens are
+// used to describe aliases, anchors, tag, and scalars:
+//
+// ALIAS(anchor)
+// ANCHOR(anchor)
+// TAG(handle,suffix)
+// SCALAR(value,style)
+//
+// The following series of examples illustrate the usage of these tokens:
+//
+// 1. A recursive sequence:
+//
+// &A [ *A ]
+//
+// Tokens:
+//
+// STREAM-START(utf-8)
+// ANCHOR("A")
+// FLOW-SEQUENCE-START
+// ALIAS("A")
+// FLOW-SEQUENCE-END
+// STREAM-END
+//
+// 2. A tagged scalar:
+//
+// !!float "3.14" # A good approximation.
+//
+// Tokens:
+//
+// STREAM-START(utf-8)
+// TAG("!!","float")
+// SCALAR("3.14",double-quoted)
+// STREAM-END
+//
+// 3. Various scalar styles:
+//
+// --- # Implicit empty plain scalars do not produce tokens.
+// --- a plain scalar
+// --- 'a single-quoted scalar'
+// --- "a double-quoted scalar"
+// --- |-
+// a literal scalar
+// --- >-
+// a folded
+// scalar
+//
+// Tokens:
+//
+// STREAM-START(utf-8)
+// DOCUMENT-START
+// DOCUMENT-START
+// SCALAR("a plain scalar",plain)
+// DOCUMENT-START
+// SCALAR("a single-quoted scalar",single-quoted)
+// DOCUMENT-START
+// SCALAR("a double-quoted scalar",double-quoted)
+// DOCUMENT-START
+// SCALAR("a literal scalar",literal)
+// DOCUMENT-START
+// SCALAR("a folded scalar",folded)
+// STREAM-END
+//
+// Now it's time to review collection-related tokens. We will start with
+// flow collections:
+//
+// FLOW-SEQUENCE-START
+// FLOW-SEQUENCE-END
+// FLOW-MAPPING-START
+// FLOW-MAPPING-END
+// FLOW-ENTRY
+// KEY
+// VALUE
+//
+// The tokens FLOW-SEQUENCE-START, FLOW-SEQUENCE-END, FLOW-MAPPING-START, and
+// FLOW-MAPPING-END represent the indicators '[', ']', '{', and '}'
+// correspondingly. FLOW-ENTRY represent the ',' indicator. Finally the
+// indicators '?' and ':', which are used for denoting mapping keys and values,
+// are represented by the KEY and VALUE tokens.
+//
+// The following examples show flow collections:
+//
+// 1. A flow sequence:
+//
+// [item 1, item 2, item 3]
+//
+// Tokens:
+//
+// STREAM-START(utf-8)
+// FLOW-SEQUENCE-START
+// SCALAR("item 1",plain)
+// FLOW-ENTRY
+// SCALAR("item 2",plain)
+// FLOW-ENTRY
+// SCALAR("item 3",plain)
+// FLOW-SEQUENCE-END
+// STREAM-END
+//
+// 2. A flow mapping:
+//
+// {
+// a simple key: a value, # Note that the KEY token is produced.
+// ? a complex key: another value,
+// }
+//
+// Tokens:
+//
+// STREAM-START(utf-8)
+// FLOW-MAPPING-START
+// KEY
+// SCALAR("a simple key",plain)
+// VALUE
+// SCALAR("a value",plain)
+// FLOW-ENTRY
+// KEY
+// SCALAR("a complex key",plain)
+// VALUE
+// SCALAR("another value",plain)
+// FLOW-ENTRY
+// FLOW-MAPPING-END
+// STREAM-END
+//
+// A simple key is a key which is not denoted by the '?' indicator. Note that
+// the Scanner still produce the KEY token whenever it encounters a simple key.
+//
+// For scanning block collections, the following tokens are used (note that we
+// repeat KEY and VALUE here):
+//
+// BLOCK-SEQUENCE-START
+// BLOCK-MAPPING-START
+// BLOCK-END
+// BLOCK-ENTRY
+// KEY
+// VALUE
+//
+// The tokens BLOCK-SEQUENCE-START and BLOCK-MAPPING-START denote indentation
+// increase that precedes a block collection (cf. the INDENT token in Python).
+// The token BLOCK-END denote indentation decrease that ends a block collection
+// (cf. the DEDENT token in Python). However YAML has some syntax pecularities
+// that makes detections of these tokens more complex.
+//
+// The tokens BLOCK-ENTRY, KEY, and VALUE are used to represent the indicators
+// '-', '?', and ':' correspondingly.
+//
+// The following examples show how the tokens BLOCK-SEQUENCE-START,
+// BLOCK-MAPPING-START, and BLOCK-END are emitted by the Scanner:
+//
+// 1. Block sequences:
+//
+// - item 1
+// - item 2
+// -
+// - item 3.1
+// - item 3.2
+// -
+// key 1: value 1
+// key 2: value 2
+//
+// Tokens:
+//
+// STREAM-START(utf-8)
+// BLOCK-SEQUENCE-START
+// BLOCK-ENTRY
+// SCALAR("item 1",plain)
+// BLOCK-ENTRY
+// SCALAR("item 2",plain)
+// BLOCK-ENTRY
+// BLOCK-SEQUENCE-START
+// BLOCK-ENTRY
+// SCALAR("item 3.1",plain)
+// BLOCK-ENTRY
+// SCALAR("item 3.2",plain)
+// BLOCK-END
+// BLOCK-ENTRY
+// BLOCK-MAPPING-START
+// KEY
+// SCALAR("key 1",plain)
+// VALUE
+// SCALAR("value 1",plain)
+// KEY
+// SCALAR("key 2",plain)
+// VALUE
+// SCALAR("value 2",plain)
+// BLOCK-END
+// BLOCK-END
+// STREAM-END
+//
+// 2. Block mappings:
+//
+// a simple key: a value # The KEY token is produced here.
+// ? a complex key
+// : another value
+// a mapping:
+// key 1: value 1
+// key 2: value 2
+// a sequence:
+// - item 1
+// - item 2
+//
+// Tokens:
+//
+// STREAM-START(utf-8)
+// BLOCK-MAPPING-START
+// KEY
+// SCALAR("a simple key",plain)
+// VALUE
+// SCALAR("a value",plain)
+// KEY
+// SCALAR("a complex key",plain)
+// VALUE
+// SCALAR("another value",plain)
+// KEY
+// SCALAR("a mapping",plain)
+// BLOCK-MAPPING-START
+// KEY
+// SCALAR("key 1",plain)
+// VALUE
+// SCALAR("value 1",plain)
+// KEY
+// SCALAR("key 2",plain)
+// VALUE
+// SCALAR("value 2",plain)
+// BLOCK-END
+// KEY
+// SCALAR("a sequence",plain)
+// VALUE
+// BLOCK-SEQUENCE-START
+// BLOCK-ENTRY
+// SCALAR("item 1",plain)
+// BLOCK-ENTRY
+// SCALAR("item 2",plain)
+// BLOCK-END
+// BLOCK-END
+// STREAM-END
+//
+// YAML does not always require to start a new block collection from a new
+// line. If the current line contains only '-', '?', and ':' indicators, a new
+// block collection may start at the current line. The following examples
+// illustrate this case:
+//
+// 1. Collections in a sequence:
+//
+// - - item 1
+// - item 2
+// - key 1: value 1
+// key 2: value 2
+// - ? complex key
+// : complex value
+//
+// Tokens:
+//
+// STREAM-START(utf-8)
+// BLOCK-SEQUENCE-START
+// BLOCK-ENTRY
+// BLOCK-SEQUENCE-START
+// BLOCK-ENTRY
+// SCALAR("item 1",plain)
+// BLOCK-ENTRY
+// SCALAR("item 2",plain)
+// BLOCK-END
+// BLOCK-ENTRY
+// BLOCK-MAPPING-START
+// KEY
+// SCALAR("key 1",plain)
+// VALUE
+// SCALAR("value 1",plain)
+// KEY
+// SCALAR("key 2",plain)
+// VALUE
+// SCALAR("value 2",plain)
+// BLOCK-END
+// BLOCK-ENTRY
+// BLOCK-MAPPING-START
+// KEY
+// SCALAR("complex key")
+// VALUE
+// SCALAR("complex value")
+// BLOCK-END
+// BLOCK-END
+// STREAM-END
+//
+// 2. Collections in a mapping:
+//
+// ? a sequence
+// : - item 1
+// - item 2
+// ? a mapping
+// : key 1: value 1
+// key 2: value 2
+//
+// Tokens:
+//
+// STREAM-START(utf-8)
+// BLOCK-MAPPING-START
+// KEY
+// SCALAR("a sequence",plain)
+// VALUE
+// BLOCK-SEQUENCE-START
+// BLOCK-ENTRY
+// SCALAR("item 1",plain)
+// BLOCK-ENTRY
+// SCALAR("item 2",plain)
+// BLOCK-END
+// KEY
+// SCALAR("a mapping",plain)
+// VALUE
+// BLOCK-MAPPING-START
+// KEY
+// SCALAR("key 1",plain)
+// VALUE
+// SCALAR("value 1",plain)
+// KEY
+// SCALAR("key 2",plain)
+// VALUE
+// SCALAR("value 2",plain)
+// BLOCK-END
+// BLOCK-END
+// STREAM-END
+//
+// YAML also permits non-indented sequences if they are included into a block
+// mapping. In this case, the token BLOCK-SEQUENCE-START is not produced:
+//
+// key:
+// - item 1 # BLOCK-SEQUENCE-START is NOT produced here.
+// - item 2
+//
+// Tokens:
+//
+// STREAM-START(utf-8)
+// BLOCK-MAPPING-START
+// KEY
+// SCALAR("key",plain)
+// VALUE
+// BLOCK-ENTRY
+// SCALAR("item 1",plain)
+// BLOCK-ENTRY
+// SCALAR("item 2",plain)
+// BLOCK-END
+//
+
+// Ensure that the buffer contains the required number of characters.
+// Return true on success, false on failure (reader error or memory error).
+func cache(parser *yaml_parser_t, length int) bool {
+ // [Go] This was inlined: !cache(A, B) -> unread < B && !update(A, B)
+ return parser.unread >= length || yaml_parser_update_buffer(parser, length)
+}
+
+// Advance the buffer pointer.
+func skip(parser *yaml_parser_t) {
+ if !is_blank(parser.buffer, parser.buffer_pos) {
+ parser.newlines = 0
+ }
+ parser.mark.index++
+ parser.mark.column++
+ parser.unread--
+ parser.buffer_pos += width(parser.buffer[parser.buffer_pos])
+}
+
+func skip_line(parser *yaml_parser_t) {
+ if is_crlf(parser.buffer, parser.buffer_pos) {
+ parser.mark.index += 2
+ parser.mark.column = 0
+ parser.mark.line++
+ parser.unread -= 2
+ parser.buffer_pos += 2
+ parser.newlines++
+ } else if is_break(parser.buffer, parser.buffer_pos) {
+ parser.mark.index++
+ parser.mark.column = 0
+ parser.mark.line++
+ parser.unread--
+ parser.buffer_pos += width(parser.buffer[parser.buffer_pos])
+ parser.newlines++
+ }
+}
+
+// Copy a character to a string buffer and advance pointers.
+func read(parser *yaml_parser_t, s []byte) []byte {
+ if !is_blank(parser.buffer, parser.buffer_pos) {
+ parser.newlines = 0
+ }
+ w := width(parser.buffer[parser.buffer_pos])
+ if w == 0 {
+ panic("invalid character sequence")
+ }
+ if len(s) == 0 {
+ s = make([]byte, 0, 32)
+ }
+ if w == 1 && len(s)+w <= cap(s) {
+ s = s[:len(s)+1]
+ s[len(s)-1] = parser.buffer[parser.buffer_pos]
+ parser.buffer_pos++
+ } else {
+ s = append(s, parser.buffer[parser.buffer_pos:parser.buffer_pos+w]...)
+ parser.buffer_pos += w
+ }
+ parser.mark.index++
+ parser.mark.column++
+ parser.unread--
+ return s
+}
+
+// Copy a line break character to a string buffer and advance pointers.
+func read_line(parser *yaml_parser_t, s []byte) []byte {
+ buf := parser.buffer
+ pos := parser.buffer_pos
+ switch {
+ case buf[pos] == '\r' && buf[pos+1] == '\n':
+ // CR LF . LF
+ s = append(s, '\n')
+ parser.buffer_pos += 2
+ parser.mark.index++
+ parser.unread--
+ case buf[pos] == '\r' || buf[pos] == '\n':
+ // CR|LF . LF
+ s = append(s, '\n')
+ parser.buffer_pos += 1
+ case buf[pos] == '\xC2' && buf[pos+1] == '\x85':
+ // NEL . LF
+ s = append(s, '\n')
+ parser.buffer_pos += 2
+ case buf[pos] == '\xE2' && buf[pos+1] == '\x80' && (buf[pos+2] == '\xA8' || buf[pos+2] == '\xA9'):
+ // LS|PS . LS|PS
+ s = append(s, buf[parser.buffer_pos:pos+3]...)
+ parser.buffer_pos += 3
+ default:
+ return s
+ }
+ parser.mark.index++
+ parser.mark.column = 0
+ parser.mark.line++
+ parser.unread--
+ parser.newlines++
+ return s
+}
+
+// Get the next token.
+func yaml_parser_scan(parser *yaml_parser_t, token *yaml_token_t) bool {
+ // Erase the token object.
+ *token = yaml_token_t{} // [Go] Is this necessary?
+
+ // No tokens after STREAM-END or error.
+ if parser.stream_end_produced || parser.error != yaml_NO_ERROR {
+ return true
+ }
+
+ // Ensure that the tokens queue contains enough tokens.
+ if !parser.token_available {
+ if !yaml_parser_fetch_more_tokens(parser) {
+ return false
+ }
+ }
+
+ // Fetch the next token from the queue.
+ *token = parser.tokens[parser.tokens_head]
+ parser.tokens_head++
+ parser.tokens_parsed++
+ parser.token_available = false
+
+ if token.typ == yaml_STREAM_END_TOKEN {
+ parser.stream_end_produced = true
+ }
+ return true
+}
+
+// Set the scanner error and return false.
+func yaml_parser_set_scanner_error(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string) bool {
+ parser.error = yaml_SCANNER_ERROR
+ parser.context = context
+ parser.context_mark = context_mark
+ parser.problem = problem
+ parser.problem_mark = parser.mark
+ return false
+}
+
+func yaml_parser_set_scanner_tag_error(parser *yaml_parser_t, directive bool, context_mark yaml_mark_t, problem string) bool {
+ context := "while parsing a tag"
+ if directive {
+ context = "while parsing a %TAG directive"
+ }
+ return yaml_parser_set_scanner_error(parser, context, context_mark, problem)
+}
+
+func trace(args ...interface{}) func() {
+ pargs := append([]interface{}{"+++"}, args...)
+ fmt.Println(pargs...)
+ pargs = append([]interface{}{"---"}, args...)
+ return func() { fmt.Println(pargs...) }
+}
+
+// Ensure that the tokens queue contains at least one token which can be
+// returned to the Parser.
+func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool {
+ // While we need more tokens to fetch, do it.
+ for {
+ // [Go] The comment parsing logic requires a lookahead of two tokens
+ // so that foot comments may be parsed in time of associating them
+ // with the tokens that are parsed before them, and also for line
+ // comments to be transformed into head comments in some edge cases.
+ if parser.tokens_head < len(parser.tokens)-2 {
+ // If a potential simple key is at the head position, we need to fetch
+ // the next token to disambiguate it.
+ head_tok_idx, ok := parser.simple_keys_by_tok[parser.tokens_parsed]
+ if !ok {
+ break
+ } else if valid, ok := yaml_simple_key_is_valid(parser, &parser.simple_keys[head_tok_idx]); !ok {
+ return false
+ } else if !valid {
+ break
+ }
+ }
+ // Fetch the next token.
+ if !yaml_parser_fetch_next_token(parser) {
+ return false
+ }
+ }
+
+ parser.token_available = true
+ return true
+}
+
+// The dispatcher for token fetchers.
+func yaml_parser_fetch_next_token(parser *yaml_parser_t) (ok bool) {
+ // Ensure that the buffer is initialized.
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+
+ // Check if we just started scanning. Fetch STREAM-START then.
+ if !parser.stream_start_produced {
+ return yaml_parser_fetch_stream_start(parser)
+ }
+
+ scan_mark := parser.mark
+
+ // Eat whitespaces and comments until we reach the next token.
+ if !yaml_parser_scan_to_next_token(parser) {
+ return false
+ }
+
+ // [Go] While unrolling indents, transform the head comments of prior
+ // indentation levels observed after scan_start into foot comments at
+ // the respective indexes.
+
+ // Check the indentation level against the current column.
+ if !yaml_parser_unroll_indent(parser, parser.mark.column, scan_mark) {
+ return false
+ }
+
+ // Ensure that the buffer contains at least 4 characters. 4 is the length
+ // of the longest indicators ('--- ' and '... ').
+ if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) {
+ return false
+ }
+
+ // Is it the end of the stream?
+ if is_z(parser.buffer, parser.buffer_pos) {
+ return yaml_parser_fetch_stream_end(parser)
+ }
+
+ // Is it a directive?
+ if parser.mark.column == 0 && parser.buffer[parser.buffer_pos] == '%' {
+ return yaml_parser_fetch_directive(parser)
+ }
+
+ buf := parser.buffer
+ pos := parser.buffer_pos
+
+ // Is it the document start indicator?
+ if parser.mark.column == 0 && buf[pos] == '-' && buf[pos+1] == '-' && buf[pos+2] == '-' && is_blankz(buf, pos+3) {
+ return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_START_TOKEN)
+ }
+
+ // Is it the document end indicator?
+ if parser.mark.column == 0 && buf[pos] == '.' && buf[pos+1] == '.' && buf[pos+2] == '.' && is_blankz(buf, pos+3) {
+ return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_END_TOKEN)
+ }
+
+ comment_mark := parser.mark
+ if len(parser.tokens) > 0 && (parser.flow_level == 0 && buf[pos] == ':' || parser.flow_level > 0 && buf[pos] == ',') {
+ // Associate any following comments with the prior token.
+ comment_mark = parser.tokens[len(parser.tokens)-1].start_mark
+ }
+ defer func() {
+ if !ok {
+ return
+ }
+ if len(parser.tokens) > 0 && parser.tokens[len(parser.tokens)-1].typ == yaml_BLOCK_ENTRY_TOKEN {
+ // Sequence indicators alone have no line comments. It becomes
+ // a head comment for whatever follows.
+ return
+ }
+ if !yaml_parser_scan_line_comment(parser, comment_mark) {
+ ok = false
+ return
+ }
+ }()
+
+ // Is it the flow sequence start indicator?
+ if buf[pos] == '[' {
+ return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_SEQUENCE_START_TOKEN)
+ }
+
+ // Is it the flow mapping start indicator?
+ if parser.buffer[parser.buffer_pos] == '{' {
+ return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_MAPPING_START_TOKEN)
+ }
+
+ // Is it the flow sequence end indicator?
+ if parser.buffer[parser.buffer_pos] == ']' {
+ return yaml_parser_fetch_flow_collection_end(parser,
+ yaml_FLOW_SEQUENCE_END_TOKEN)
+ }
+
+ // Is it the flow mapping end indicator?
+ if parser.buffer[parser.buffer_pos] == '}' {
+ return yaml_parser_fetch_flow_collection_end(parser,
+ yaml_FLOW_MAPPING_END_TOKEN)
+ }
+
+ // Is it the flow entry indicator?
+ if parser.buffer[parser.buffer_pos] == ',' {
+ return yaml_parser_fetch_flow_entry(parser)
+ }
+
+ // Is it the block entry indicator?
+ if parser.buffer[parser.buffer_pos] == '-' && is_blankz(parser.buffer, parser.buffer_pos+1) {
+ return yaml_parser_fetch_block_entry(parser)
+ }
+
+ // Is it the key indicator?
+ if parser.buffer[parser.buffer_pos] == '?' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) {
+ return yaml_parser_fetch_key(parser)
+ }
+
+ // Is it the value indicator?
+ if parser.buffer[parser.buffer_pos] == ':' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) {
+ return yaml_parser_fetch_value(parser)
+ }
+
+ // Is it an alias?
+ if parser.buffer[parser.buffer_pos] == '*' {
+ return yaml_parser_fetch_anchor(parser, yaml_ALIAS_TOKEN)
+ }
+
+ // Is it an anchor?
+ if parser.buffer[parser.buffer_pos] == '&' {
+ return yaml_parser_fetch_anchor(parser, yaml_ANCHOR_TOKEN)
+ }
+
+ // Is it a tag?
+ if parser.buffer[parser.buffer_pos] == '!' {
+ return yaml_parser_fetch_tag(parser)
+ }
+
+ // Is it a literal scalar?
+ if parser.buffer[parser.buffer_pos] == '|' && parser.flow_level == 0 {
+ return yaml_parser_fetch_block_scalar(parser, true)
+ }
+
+ // Is it a folded scalar?
+ if parser.buffer[parser.buffer_pos] == '>' && parser.flow_level == 0 {
+ return yaml_parser_fetch_block_scalar(parser, false)
+ }
+
+ // Is it a single-quoted scalar?
+ if parser.buffer[parser.buffer_pos] == '\'' {
+ return yaml_parser_fetch_flow_scalar(parser, true)
+ }
+
+ // Is it a double-quoted scalar?
+ if parser.buffer[parser.buffer_pos] == '"' {
+ return yaml_parser_fetch_flow_scalar(parser, false)
+ }
+
+ // Is it a plain scalar?
+ //
+ // A plain scalar may start with any non-blank characters except
+ //
+ // '-', '?', ':', ',', '[', ']', '{', '}',
+ // '#', '&', '*', '!', '|', '>', '\'', '\"',
+ // '%', '@', '`'.
+ //
+ // In the block context (and, for the '-' indicator, in the flow context
+ // too), it may also start with the characters
+ //
+ // '-', '?', ':'
+ //
+ // if it is followed by a non-space character.
+ //
+ // The last rule is more restrictive than the specification requires.
+ // [Go] TODO Make this logic more reasonable.
+ //switch parser.buffer[parser.buffer_pos] {
+ //case '-', '?', ':', ',', '?', '-', ',', ':', ']', '[', '}', '{', '&', '#', '!', '*', '>', '|', '"', '\'', '@', '%', '-', '`':
+ //}
+ if !(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '-' ||
+ parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':' ||
+ parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '[' ||
+ parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' ||
+ parser.buffer[parser.buffer_pos] == '}' || parser.buffer[parser.buffer_pos] == '#' ||
+ parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '*' ||
+ parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '|' ||
+ parser.buffer[parser.buffer_pos] == '>' || parser.buffer[parser.buffer_pos] == '\'' ||
+ parser.buffer[parser.buffer_pos] == '"' || parser.buffer[parser.buffer_pos] == '%' ||
+ parser.buffer[parser.buffer_pos] == '@' || parser.buffer[parser.buffer_pos] == '`') ||
+ (parser.buffer[parser.buffer_pos] == '-' && !is_blank(parser.buffer, parser.buffer_pos+1)) ||
+ (parser.flow_level == 0 &&
+ (parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':') &&
+ !is_blankz(parser.buffer, parser.buffer_pos+1)) {
+ return yaml_parser_fetch_plain_scalar(parser)
+ }
+
+ // If we don't determine the token type so far, it is an error.
+ return yaml_parser_set_scanner_error(parser,
+ "while scanning for the next token", parser.mark,
+ "found character that cannot start any token")
+}
+
+func yaml_simple_key_is_valid(parser *yaml_parser_t, simple_key *yaml_simple_key_t) (valid, ok bool) {
+ if !simple_key.possible {
+ return false, true
+ }
+
+ // The 1.2 specification says:
+ //
+ // "If the ? indicator is omitted, parsing needs to see past the
+ // implicit key to recognize it as such. To limit the amount of
+ // lookahead required, the “:” indicator must appear at most 1024
+ // Unicode characters beyond the start of the key. In addition, the key
+ // is restricted to a single line."
+ //
+ if simple_key.mark.line < parser.mark.line || simple_key.mark.index+1024 < parser.mark.index {
+ // Check if the potential simple key to be removed is required.
+ if simple_key.required {
+ return false, yaml_parser_set_scanner_error(parser,
+ "while scanning a simple key", simple_key.mark,
+ "could not find expected ':'")
+ }
+ simple_key.possible = false
+ return false, true
+ }
+ return true, true
+}
+
+// Check if a simple key may start at the current position and add it if
+// needed.
+func yaml_parser_save_simple_key(parser *yaml_parser_t) bool {
+ // A simple key is required at the current position if the scanner is in
+ // the block context and the current column coincides with the indentation
+ // level.
+
+ required := parser.flow_level == 0 && parser.indent == parser.mark.column
+
+ //
+ // If the current position may start a simple key, save it.
+ //
+ if parser.simple_key_allowed {
+ simple_key := yaml_simple_key_t{
+ possible: true,
+ required: required,
+ token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head),
+ mark: parser.mark,
+ }
+
+ if !yaml_parser_remove_simple_key(parser) {
+ return false
+ }
+ parser.simple_keys[len(parser.simple_keys)-1] = simple_key
+ parser.simple_keys_by_tok[simple_key.token_number] = len(parser.simple_keys) - 1
+ }
+ return true
+}
+
+// Remove a potential simple key at the current flow level.
+func yaml_parser_remove_simple_key(parser *yaml_parser_t) bool {
+ i := len(parser.simple_keys) - 1
+ if parser.simple_keys[i].possible {
+ // If the key is required, it is an error.
+ if parser.simple_keys[i].required {
+ return yaml_parser_set_scanner_error(parser,
+ "while scanning a simple key", parser.simple_keys[i].mark,
+ "could not find expected ':'")
+ }
+ // Remove the key from the stack.
+ parser.simple_keys[i].possible = false
+ delete(parser.simple_keys_by_tok, parser.simple_keys[i].token_number)
+ }
+ return true
+}
+
+// max_flow_level limits the flow_level
+const max_flow_level = 10000
+
+// Increase the flow level and resize the simple key list if needed.
+func yaml_parser_increase_flow_level(parser *yaml_parser_t) bool {
+ // Reset the simple key on the next level.
+ parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{
+ possible: false,
+ required: false,
+ token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head),
+ mark: parser.mark,
+ })
+
+ // Increase the flow level.
+ parser.flow_level++
+ if parser.flow_level > max_flow_level {
+ return yaml_parser_set_scanner_error(parser,
+ "while increasing flow level", parser.simple_keys[len(parser.simple_keys)-1].mark,
+ fmt.Sprintf("exceeded max depth of %d", max_flow_level))
+ }
+ return true
+}
+
+// Decrease the flow level.
+func yaml_parser_decrease_flow_level(parser *yaml_parser_t) bool {
+ if parser.flow_level > 0 {
+ parser.flow_level--
+ last := len(parser.simple_keys) - 1
+ delete(parser.simple_keys_by_tok, parser.simple_keys[last].token_number)
+ parser.simple_keys = parser.simple_keys[:last]
+ }
+ return true
+}
+
+// max_indents limits the indents stack size
+const max_indents = 10000
+
+// Push the current indentation level to the stack and set the new level
+// the current column is greater than the indentation level. In this case,
+// append or insert the specified token into the token queue.
+func yaml_parser_roll_indent(parser *yaml_parser_t, column, number int, typ yaml_token_type_t, mark yaml_mark_t) bool {
+ // In the flow context, do nothing.
+ if parser.flow_level > 0 {
+ return true
+ }
+
+ if parser.indent < column {
+ // Push the current indentation level to the stack and set the new
+ // indentation level.
+ parser.indents = append(parser.indents, parser.indent)
+ parser.indent = column
+ if len(parser.indents) > max_indents {
+ return yaml_parser_set_scanner_error(parser,
+ "while increasing indent level", parser.simple_keys[len(parser.simple_keys)-1].mark,
+ fmt.Sprintf("exceeded max depth of %d", max_indents))
+ }
+
+ // Create a token and insert it into the queue.
+ token := yaml_token_t{
+ typ: typ,
+ start_mark: mark,
+ end_mark: mark,
+ }
+ if number > -1 {
+ number -= parser.tokens_parsed
+ }
+ yaml_insert_token(parser, number, &token)
+ }
+ return true
+}
+
+// Pop indentation levels from the indents stack until the current level
+// becomes less or equal to the column. For each indentation level, append
+// the BLOCK-END token.
+func yaml_parser_unroll_indent(parser *yaml_parser_t, column int, scan_mark yaml_mark_t) bool {
+ // In the flow context, do nothing.
+ if parser.flow_level > 0 {
+ return true
+ }
+
+ block_mark := scan_mark
+ block_mark.index--
+
+ // Loop through the indentation levels in the stack.
+ for parser.indent > column {
+
+ // [Go] Reposition the end token before potential following
+ // foot comments of parent blocks. For that, search
+ // backwards for recent comments that were at the same
+ // indent as the block that is ending now.
+ stop_index := block_mark.index
+ for i := len(parser.comments) - 1; i >= 0; i-- {
+ comment := &parser.comments[i]
+
+ if comment.end_mark.index < stop_index {
+ // Don't go back beyond the start of the comment/whitespace scan, unless column < 0.
+ // If requested indent column is < 0, then the document is over and everything else
+ // is a foot anyway.
+ break
+ }
+ if comment.start_mark.column == parser.indent+1 {
+ // This is a good match. But maybe there's a former comment
+ // at that same indent level, so keep searching.
+ block_mark = comment.start_mark
+ }
+
+ // While the end of the former comment matches with
+ // the start of the following one, we know there's
+ // nothing in between and scanning is still safe.
+ stop_index = comment.scan_mark.index
+ }
+
+ // Create a token and append it to the queue.
+ token := yaml_token_t{
+ typ: yaml_BLOCK_END_TOKEN,
+ start_mark: block_mark,
+ end_mark: block_mark,
+ }
+ yaml_insert_token(parser, -1, &token)
+
+ // Pop the indentation level.
+ parser.indent = parser.indents[len(parser.indents)-1]
+ parser.indents = parser.indents[:len(parser.indents)-1]
+ }
+ return true
+}
+
+// Initialize the scanner and produce the STREAM-START token.
+func yaml_parser_fetch_stream_start(parser *yaml_parser_t) bool {
+
+ // Set the initial indentation.
+ parser.indent = -1
+
+ // Initialize the simple key stack.
+ parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{})
+
+ parser.simple_keys_by_tok = make(map[int]int)
+
+ // A simple key is allowed at the beginning of the stream.
+ parser.simple_key_allowed = true
+
+ // We have started.
+ parser.stream_start_produced = true
+
+ // Create the STREAM-START token and append it to the queue.
+ token := yaml_token_t{
+ typ: yaml_STREAM_START_TOKEN,
+ start_mark: parser.mark,
+ end_mark: parser.mark,
+ encoding: parser.encoding,
+ }
+ yaml_insert_token(parser, -1, &token)
+ return true
+}
+
+// Produce the STREAM-END token and shut down the scanner.
+func yaml_parser_fetch_stream_end(parser *yaml_parser_t) bool {
+
+ // Force new line.
+ if parser.mark.column != 0 {
+ parser.mark.column = 0
+ parser.mark.line++
+ }
+
+ // Reset the indentation level.
+ if !yaml_parser_unroll_indent(parser, -1, parser.mark) {
+ return false
+ }
+
+ // Reset simple keys.
+ if !yaml_parser_remove_simple_key(parser) {
+ return false
+ }
+
+ parser.simple_key_allowed = false
+
+ // Create the STREAM-END token and append it to the queue.
+ token := yaml_token_t{
+ typ: yaml_STREAM_END_TOKEN,
+ start_mark: parser.mark,
+ end_mark: parser.mark,
+ }
+ yaml_insert_token(parser, -1, &token)
+ return true
+}
+
+// Produce a VERSION-DIRECTIVE or TAG-DIRECTIVE token.
+func yaml_parser_fetch_directive(parser *yaml_parser_t) bool {
+ // Reset the indentation level.
+ if !yaml_parser_unroll_indent(parser, -1, parser.mark) {
+ return false
+ }
+
+ // Reset simple keys.
+ if !yaml_parser_remove_simple_key(parser) {
+ return false
+ }
+
+ parser.simple_key_allowed = false
+
+ // Create the YAML-DIRECTIVE or TAG-DIRECTIVE token.
+ token := yaml_token_t{}
+ if !yaml_parser_scan_directive(parser, &token) {
+ return false
+ }
+ // Append the token to the queue.
+ yaml_insert_token(parser, -1, &token)
+ return true
+}
+
+// Produce the DOCUMENT-START or DOCUMENT-END token.
+func yaml_parser_fetch_document_indicator(parser *yaml_parser_t, typ yaml_token_type_t) bool {
+ // Reset the indentation level.
+ if !yaml_parser_unroll_indent(parser, -1, parser.mark) {
+ return false
+ }
+
+ // Reset simple keys.
+ if !yaml_parser_remove_simple_key(parser) {
+ return false
+ }
+
+ parser.simple_key_allowed = false
+
+ // Consume the token.
+ start_mark := parser.mark
+
+ skip(parser)
+ skip(parser)
+ skip(parser)
+
+ end_mark := parser.mark
+
+ // Create the DOCUMENT-START or DOCUMENT-END token.
+ token := yaml_token_t{
+ typ: typ,
+ start_mark: start_mark,
+ end_mark: end_mark,
+ }
+ // Append the token to the queue.
+ yaml_insert_token(parser, -1, &token)
+ return true
+}
+
+// Produce the FLOW-SEQUENCE-START or FLOW-MAPPING-START token.
+func yaml_parser_fetch_flow_collection_start(parser *yaml_parser_t, typ yaml_token_type_t) bool {
+
+ // The indicators '[' and '{' may start a simple key.
+ if !yaml_parser_save_simple_key(parser) {
+ return false
+ }
+
+ // Increase the flow level.
+ if !yaml_parser_increase_flow_level(parser) {
+ return false
+ }
+
+ // A simple key may follow the indicators '[' and '{'.
+ parser.simple_key_allowed = true
+
+ // Consume the token.
+ start_mark := parser.mark
+ skip(parser)
+ end_mark := parser.mark
+
+ // Create the FLOW-SEQUENCE-START of FLOW-MAPPING-START token.
+ token := yaml_token_t{
+ typ: typ,
+ start_mark: start_mark,
+ end_mark: end_mark,
+ }
+ // Append the token to the queue.
+ yaml_insert_token(parser, -1, &token)
+ return true
+}
+
+// Produce the FLOW-SEQUENCE-END or FLOW-MAPPING-END token.
+func yaml_parser_fetch_flow_collection_end(parser *yaml_parser_t, typ yaml_token_type_t) bool {
+ // Reset any potential simple key on the current flow level.
+ if !yaml_parser_remove_simple_key(parser) {
+ return false
+ }
+
+ // Decrease the flow level.
+ if !yaml_parser_decrease_flow_level(parser) {
+ return false
+ }
+
+ // No simple keys after the indicators ']' and '}'.
+ parser.simple_key_allowed = false
+
+ // Consume the token.
+
+ start_mark := parser.mark
+ skip(parser)
+ end_mark := parser.mark
+
+ // Create the FLOW-SEQUENCE-END of FLOW-MAPPING-END token.
+ token := yaml_token_t{
+ typ: typ,
+ start_mark: start_mark,
+ end_mark: end_mark,
+ }
+ // Append the token to the queue.
+ yaml_insert_token(parser, -1, &token)
+ return true
+}
+
+// Produce the FLOW-ENTRY token.
+func yaml_parser_fetch_flow_entry(parser *yaml_parser_t) bool {
+ // Reset any potential simple keys on the current flow level.
+ if !yaml_parser_remove_simple_key(parser) {
+ return false
+ }
+
+ // Simple keys are allowed after ','.
+ parser.simple_key_allowed = true
+
+ // Consume the token.
+ start_mark := parser.mark
+ skip(parser)
+ end_mark := parser.mark
+
+ // Create the FLOW-ENTRY token and append it to the queue.
+ token := yaml_token_t{
+ typ: yaml_FLOW_ENTRY_TOKEN,
+ start_mark: start_mark,
+ end_mark: end_mark,
+ }
+ yaml_insert_token(parser, -1, &token)
+ return true
+}
+
+// Produce the BLOCK-ENTRY token.
+func yaml_parser_fetch_block_entry(parser *yaml_parser_t) bool {
+ // Check if the scanner is in the block context.
+ if parser.flow_level == 0 {
+ // Check if we are allowed to start a new entry.
+ if !parser.simple_key_allowed {
+ return yaml_parser_set_scanner_error(parser, "", parser.mark,
+ "block sequence entries are not allowed in this context")
+ }
+ // Add the BLOCK-SEQUENCE-START token if needed.
+ if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_SEQUENCE_START_TOKEN, parser.mark) {
+ return false
+ }
+ } else {
+ // It is an error for the '-' indicator to occur in the flow context,
+ // but we let the Parser detect and report about it because the Parser
+ // is able to point to the context.
+ }
+
+ // Reset any potential simple keys on the current flow level.
+ if !yaml_parser_remove_simple_key(parser) {
+ return false
+ }
+
+ // Simple keys are allowed after '-'.
+ parser.simple_key_allowed = true
+
+ // Consume the token.
+ start_mark := parser.mark
+ skip(parser)
+ end_mark := parser.mark
+
+ // Create the BLOCK-ENTRY token and append it to the queue.
+ token := yaml_token_t{
+ typ: yaml_BLOCK_ENTRY_TOKEN,
+ start_mark: start_mark,
+ end_mark: end_mark,
+ }
+ yaml_insert_token(parser, -1, &token)
+ return true
+}
+
+// Produce the KEY token.
+func yaml_parser_fetch_key(parser *yaml_parser_t) bool {
+
+ // In the block context, additional checks are required.
+ if parser.flow_level == 0 {
+ // Check if we are allowed to start a new key (not nessesary simple).
+ if !parser.simple_key_allowed {
+ return yaml_parser_set_scanner_error(parser, "", parser.mark,
+ "mapping keys are not allowed in this context")
+ }
+ // Add the BLOCK-MAPPING-START token if needed.
+ if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) {
+ return false
+ }
+ }
+
+ // Reset any potential simple keys on the current flow level.
+ if !yaml_parser_remove_simple_key(parser) {
+ return false
+ }
+
+ // Simple keys are allowed after '?' in the block context.
+ parser.simple_key_allowed = parser.flow_level == 0
+
+ // Consume the token.
+ start_mark := parser.mark
+ skip(parser)
+ end_mark := parser.mark
+
+ // Create the KEY token and append it to the queue.
+ token := yaml_token_t{
+ typ: yaml_KEY_TOKEN,
+ start_mark: start_mark,
+ end_mark: end_mark,
+ }
+ yaml_insert_token(parser, -1, &token)
+ return true
+}
+
+// Produce the VALUE token.
+func yaml_parser_fetch_value(parser *yaml_parser_t) bool {
+
+ simple_key := &parser.simple_keys[len(parser.simple_keys)-1]
+
+ // Have we found a simple key?
+ if valid, ok := yaml_simple_key_is_valid(parser, simple_key); !ok {
+ return false
+
+ } else if valid {
+
+ // Create the KEY token and insert it into the queue.
+ token := yaml_token_t{
+ typ: yaml_KEY_TOKEN,
+ start_mark: simple_key.mark,
+ end_mark: simple_key.mark,
+ }
+ yaml_insert_token(parser, simple_key.token_number-parser.tokens_parsed, &token)
+
+ // In the block context, we may need to add the BLOCK-MAPPING-START token.
+ if !yaml_parser_roll_indent(parser, simple_key.mark.column,
+ simple_key.token_number,
+ yaml_BLOCK_MAPPING_START_TOKEN, simple_key.mark) {
+ return false
+ }
+
+ // Remove the simple key.
+ simple_key.possible = false
+ delete(parser.simple_keys_by_tok, simple_key.token_number)
+
+ // A simple key cannot follow another simple key.
+ parser.simple_key_allowed = false
+
+ } else {
+ // The ':' indicator follows a complex key.
+
+ // In the block context, extra checks are required.
+ if parser.flow_level == 0 {
+
+ // Check if we are allowed to start a complex value.
+ if !parser.simple_key_allowed {
+ return yaml_parser_set_scanner_error(parser, "", parser.mark,
+ "mapping values are not allowed in this context")
+ }
+
+ // Add the BLOCK-MAPPING-START token if needed.
+ if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) {
+ return false
+ }
+ }
+
+ // Simple keys after ':' are allowed in the block context.
+ parser.simple_key_allowed = parser.flow_level == 0
+ }
+
+ // Consume the token.
+ start_mark := parser.mark
+ skip(parser)
+ end_mark := parser.mark
+
+ // Create the VALUE token and append it to the queue.
+ token := yaml_token_t{
+ typ: yaml_VALUE_TOKEN,
+ start_mark: start_mark,
+ end_mark: end_mark,
+ }
+ yaml_insert_token(parser, -1, &token)
+ return true
+}
+
+// Produce the ALIAS or ANCHOR token.
+func yaml_parser_fetch_anchor(parser *yaml_parser_t, typ yaml_token_type_t) bool {
+ // An anchor or an alias could be a simple key.
+ if !yaml_parser_save_simple_key(parser) {
+ return false
+ }
+
+ // A simple key cannot follow an anchor or an alias.
+ parser.simple_key_allowed = false
+
+ // Create the ALIAS or ANCHOR token and append it to the queue.
+ var token yaml_token_t
+ if !yaml_parser_scan_anchor(parser, &token, typ) {
+ return false
+ }
+ yaml_insert_token(parser, -1, &token)
+ return true
+}
+
+// Produce the TAG token.
+func yaml_parser_fetch_tag(parser *yaml_parser_t) bool {
+ // A tag could be a simple key.
+ if !yaml_parser_save_simple_key(parser) {
+ return false
+ }
+
+ // A simple key cannot follow a tag.
+ parser.simple_key_allowed = false
+
+ // Create the TAG token and append it to the queue.
+ var token yaml_token_t
+ if !yaml_parser_scan_tag(parser, &token) {
+ return false
+ }
+ yaml_insert_token(parser, -1, &token)
+ return true
+}
+
+// Produce the SCALAR(...,literal) or SCALAR(...,folded) tokens.
+func yaml_parser_fetch_block_scalar(parser *yaml_parser_t, literal bool) bool {
+ // Remove any potential simple keys.
+ if !yaml_parser_remove_simple_key(parser) {
+ return false
+ }
+
+ // A simple key may follow a block scalar.
+ parser.simple_key_allowed = true
+
+ // Create the SCALAR token and append it to the queue.
+ var token yaml_token_t
+ if !yaml_parser_scan_block_scalar(parser, &token, literal) {
+ return false
+ }
+ yaml_insert_token(parser, -1, &token)
+ return true
+}
+
+// Produce the SCALAR(...,single-quoted) or SCALAR(...,double-quoted) tokens.
+func yaml_parser_fetch_flow_scalar(parser *yaml_parser_t, single bool) bool {
+ // A plain scalar could be a simple key.
+ if !yaml_parser_save_simple_key(parser) {
+ return false
+ }
+
+ // A simple key cannot follow a flow scalar.
+ parser.simple_key_allowed = false
+
+ // Create the SCALAR token and append it to the queue.
+ var token yaml_token_t
+ if !yaml_parser_scan_flow_scalar(parser, &token, single) {
+ return false
+ }
+ yaml_insert_token(parser, -1, &token)
+ return true
+}
+
+// Produce the SCALAR(...,plain) token.
+func yaml_parser_fetch_plain_scalar(parser *yaml_parser_t) bool {
+ // A plain scalar could be a simple key.
+ if !yaml_parser_save_simple_key(parser) {
+ return false
+ }
+
+ // A simple key cannot follow a flow scalar.
+ parser.simple_key_allowed = false
+
+ // Create the SCALAR token and append it to the queue.
+ var token yaml_token_t
+ if !yaml_parser_scan_plain_scalar(parser, &token) {
+ return false
+ }
+ yaml_insert_token(parser, -1, &token)
+ return true
+}
+
+// Eat whitespaces and comments until the next token is found.
+func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool {
+
+ scan_mark := parser.mark
+
+ // Until the next token is not found.
+ for {
+ // Allow the BOM mark to start a line.
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ if parser.mark.column == 0 && is_bom(parser.buffer, parser.buffer_pos) {
+ skip(parser)
+ }
+
+ // Eat whitespaces.
+ // Tabs are allowed:
+ // - in the flow context
+ // - in the block context, but not at the beginning of the line or
+ // after '-', '?', or ':' (complex value).
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+
+ for parser.buffer[parser.buffer_pos] == ' ' || ((parser.flow_level > 0 || !parser.simple_key_allowed) && parser.buffer[parser.buffer_pos] == '\t') {
+ skip(parser)
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ }
+
+ // Check if we just had a line comment under a sequence entry that
+ // looks more like a header to the following content. Similar to this:
+ //
+ // - # The comment
+ // - Some data
+ //
+ // If so, transform the line comment to a head comment and reposition.
+ if len(parser.comments) > 0 && len(parser.tokens) > 1 {
+ tokenA := parser.tokens[len(parser.tokens)-2]
+ tokenB := parser.tokens[len(parser.tokens)-1]
+ comment := &parser.comments[len(parser.comments)-1]
+ if tokenA.typ == yaml_BLOCK_SEQUENCE_START_TOKEN && tokenB.typ == yaml_BLOCK_ENTRY_TOKEN && len(comment.line) > 0 && !is_break(parser.buffer, parser.buffer_pos) {
+ // If it was in the prior line, reposition so it becomes a
+ // header of the follow up token. Otherwise, keep it in place
+ // so it becomes a header of the former.
+ comment.head = comment.line
+ comment.line = nil
+ if comment.start_mark.line == parser.mark.line-1 {
+ comment.token_mark = parser.mark
+ }
+ }
+ }
+
+ // Eat a comment until a line break.
+ if parser.buffer[parser.buffer_pos] == '#' {
+ if !yaml_parser_scan_comments(parser, scan_mark) {
+ return false
+ }
+ }
+
+ // If it is a line break, eat it.
+ if is_break(parser.buffer, parser.buffer_pos) {
+ if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
+ return false
+ }
+ skip_line(parser)
+
+ // In the block context, a new line may start a simple key.
+ if parser.flow_level == 0 {
+ parser.simple_key_allowed = true
+ }
+ } else {
+ break // We have found a token.
+ }
+ }
+
+ return true
+}
+
+// Scan a YAML-DIRECTIVE or TAG-DIRECTIVE token.
+//
+// Scope:
+//
+// %YAML 1.1 # a comment \n
+// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+// %TAG !yaml! tag:yaml.org,2002: \n
+// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool {
+ // Eat '%'.
+ start_mark := parser.mark
+ skip(parser)
+
+ // Scan the directive name.
+ var name []byte
+ if !yaml_parser_scan_directive_name(parser, start_mark, &name) {
+ return false
+ }
+
+ // Is it a YAML directive?
+ if bytes.Equal(name, []byte("YAML")) {
+ // Scan the VERSION directive value.
+ var major, minor int8
+ if !yaml_parser_scan_version_directive_value(parser, start_mark, &major, &minor) {
+ return false
+ }
+ end_mark := parser.mark
+
+ // Create a VERSION-DIRECTIVE token.
+ *token = yaml_token_t{
+ typ: yaml_VERSION_DIRECTIVE_TOKEN,
+ start_mark: start_mark,
+ end_mark: end_mark,
+ major: major,
+ minor: minor,
+ }
+
+ // Is it a TAG directive?
+ } else if bytes.Equal(name, []byte("TAG")) {
+ // Scan the TAG directive value.
+ var handle, prefix []byte
+ if !yaml_parser_scan_tag_directive_value(parser, start_mark, &handle, &prefix) {
+ return false
+ }
+ end_mark := parser.mark
+
+ // Create a TAG-DIRECTIVE token.
+ *token = yaml_token_t{
+ typ: yaml_TAG_DIRECTIVE_TOKEN,
+ start_mark: start_mark,
+ end_mark: end_mark,
+ value: handle,
+ prefix: prefix,
+ }
+
+ // Unknown directive.
+ } else {
+ yaml_parser_set_scanner_error(parser, "while scanning a directive",
+ start_mark, "found unknown directive name")
+ return false
+ }
+
+ // Eat the rest of the line including any comments.
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+
+ for is_blank(parser.buffer, parser.buffer_pos) {
+ skip(parser)
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ }
+
+ if parser.buffer[parser.buffer_pos] == '#' {
+ // [Go] Discard this inline comment for the time being.
+ //if !yaml_parser_scan_line_comment(parser, start_mark) {
+ // return false
+ //}
+ for !is_breakz(parser.buffer, parser.buffer_pos) {
+ skip(parser)
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ }
+ }
+
+ // Check if we are at the end of the line.
+ if !is_breakz(parser.buffer, parser.buffer_pos) {
+ yaml_parser_set_scanner_error(parser, "while scanning a directive",
+ start_mark, "did not find expected comment or line break")
+ return false
+ }
+
+ // Eat a line break.
+ if is_break(parser.buffer, parser.buffer_pos) {
+ if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
+ return false
+ }
+ skip_line(parser)
+ }
+
+ return true
+}
+
+// Scan the directive name.
+//
+// Scope:
+//
+// %YAML 1.1 # a comment \n
+// ^^^^
+// %TAG !yaml! tag:yaml.org,2002: \n
+// ^^^
+func yaml_parser_scan_directive_name(parser *yaml_parser_t, start_mark yaml_mark_t, name *[]byte) bool {
+ // Consume the directive name.
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+
+ var s []byte
+ for is_alpha(parser.buffer, parser.buffer_pos) {
+ s = read(parser, s)
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ }
+
+ // Check if the name is empty.
+ if len(s) == 0 {
+ yaml_parser_set_scanner_error(parser, "while scanning a directive",
+ start_mark, "could not find expected directive name")
+ return false
+ }
+
+ // Check for an blank character after the name.
+ if !is_blankz(parser.buffer, parser.buffer_pos) {
+ yaml_parser_set_scanner_error(parser, "while scanning a directive",
+ start_mark, "found unexpected non-alphabetical character")
+ return false
+ }
+ *name = s
+ return true
+}
+
+// Scan the value of VERSION-DIRECTIVE.
+//
+// Scope:
+//
+// %YAML 1.1 # a comment \n
+// ^^^^^^
+func yaml_parser_scan_version_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, major, minor *int8) bool {
+ // Eat whitespaces.
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ for is_blank(parser.buffer, parser.buffer_pos) {
+ skip(parser)
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ }
+
+ // Consume the major version number.
+ if !yaml_parser_scan_version_directive_number(parser, start_mark, major) {
+ return false
+ }
+
+ // Eat '.'.
+ if parser.buffer[parser.buffer_pos] != '.' {
+ return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive",
+ start_mark, "did not find expected digit or '.' character")
+ }
+
+ skip(parser)
+
+ // Consume the minor version number.
+ if !yaml_parser_scan_version_directive_number(parser, start_mark, minor) {
+ return false
+ }
+ return true
+}
+
+const max_number_length = 2
+
+// Scan the version number of VERSION-DIRECTIVE.
+//
+// Scope:
+//
+// %YAML 1.1 # a comment \n
+// ^
+// %YAML 1.1 # a comment \n
+// ^
+func yaml_parser_scan_version_directive_number(parser *yaml_parser_t, start_mark yaml_mark_t, number *int8) bool {
+
+ // Repeat while the next character is digit.
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ var value, length int8
+ for is_digit(parser.buffer, parser.buffer_pos) {
+ // Check if the number is too long.
+ length++
+ if length > max_number_length {
+ return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive",
+ start_mark, "found extremely long version number")
+ }
+ value = value*10 + int8(as_digit(parser.buffer, parser.buffer_pos))
+ skip(parser)
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ }
+
+ // Check if the number was present.
+ if length == 0 {
+ return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive",
+ start_mark, "did not find expected version number")
+ }
+ *number = value
+ return true
+}
+
+// Scan the value of a TAG-DIRECTIVE token.
+//
+// Scope:
+//
+// %TAG !yaml! tag:yaml.org,2002: \n
+// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+func yaml_parser_scan_tag_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, handle, prefix *[]byte) bool {
+ var handle_value, prefix_value []byte
+
+ // Eat whitespaces.
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+
+ for is_blank(parser.buffer, parser.buffer_pos) {
+ skip(parser)
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ }
+
+ // Scan a handle.
+ if !yaml_parser_scan_tag_handle(parser, true, start_mark, &handle_value) {
+ return false
+ }
+
+ // Expect a whitespace.
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ if !is_blank(parser.buffer, parser.buffer_pos) {
+ yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive",
+ start_mark, "did not find expected whitespace")
+ return false
+ }
+
+ // Eat whitespaces.
+ for is_blank(parser.buffer, parser.buffer_pos) {
+ skip(parser)
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ }
+
+ // Scan a prefix.
+ if !yaml_parser_scan_tag_uri(parser, true, nil, start_mark, &prefix_value) {
+ return false
+ }
+
+ // Expect a whitespace or line break.
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ if !is_blankz(parser.buffer, parser.buffer_pos) {
+ yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive",
+ start_mark, "did not find expected whitespace or line break")
+ return false
+ }
+
+ *handle = handle_value
+ *prefix = prefix_value
+ return true
+}
+
+func yaml_parser_scan_anchor(parser *yaml_parser_t, token *yaml_token_t, typ yaml_token_type_t) bool {
+ var s []byte
+
+ // Eat the indicator character.
+ start_mark := parser.mark
+ skip(parser)
+
+ // Consume the value.
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+
+ for is_alpha(parser.buffer, parser.buffer_pos) {
+ s = read(parser, s)
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ }
+
+ end_mark := parser.mark
+
+ /*
+ * Check if length of the anchor is greater than 0 and it is followed by
+ * a whitespace character or one of the indicators:
+ *
+ * '?', ':', ',', ']', '}', '%', '@', '`'.
+ */
+
+ if len(s) == 0 ||
+ !(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '?' ||
+ parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == ',' ||
+ parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '}' ||
+ parser.buffer[parser.buffer_pos] == '%' || parser.buffer[parser.buffer_pos] == '@' ||
+ parser.buffer[parser.buffer_pos] == '`') {
+ context := "while scanning an alias"
+ if typ == yaml_ANCHOR_TOKEN {
+ context = "while scanning an anchor"
+ }
+ yaml_parser_set_scanner_error(parser, context, start_mark,
+ "did not find expected alphabetic or numeric character")
+ return false
+ }
+
+ // Create a token.
+ *token = yaml_token_t{
+ typ: typ,
+ start_mark: start_mark,
+ end_mark: end_mark,
+ value: s,
+ }
+
+ return true
+}
+
+/*
+ * Scan a TAG token.
+ */
+
+func yaml_parser_scan_tag(parser *yaml_parser_t, token *yaml_token_t) bool {
+ var handle, suffix []byte
+
+ start_mark := parser.mark
+
+ // Check if the tag is in the canonical form.
+ if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
+ return false
+ }
+
+ if parser.buffer[parser.buffer_pos+1] == '<' {
+ // Keep the handle as ''
+
+ // Eat '!<'
+ skip(parser)
+ skip(parser)
+
+ // Consume the tag value.
+ if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) {
+ return false
+ }
+
+ // Check for '>' and eat it.
+ if parser.buffer[parser.buffer_pos] != '>' {
+ yaml_parser_set_scanner_error(parser, "while scanning a tag",
+ start_mark, "did not find the expected '>'")
+ return false
+ }
+
+ skip(parser)
+ } else {
+ // The tag has either the '!suffix' or the '!handle!suffix' form.
+
+ // First, try to scan a handle.
+ if !yaml_parser_scan_tag_handle(parser, false, start_mark, &handle) {
+ return false
+ }
+
+ // Check if it is, indeed, handle.
+ if handle[0] == '!' && len(handle) > 1 && handle[len(handle)-1] == '!' {
+ // Scan the suffix now.
+ if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) {
+ return false
+ }
+ } else {
+ // It wasn't a handle after all. Scan the rest of the tag.
+ if !yaml_parser_scan_tag_uri(parser, false, handle, start_mark, &suffix) {
+ return false
+ }
+
+ // Set the handle to '!'.
+ handle = []byte{'!'}
+
+ // A special case: the '!' tag. Set the handle to '' and the
+ // suffix to '!'.
+ if len(suffix) == 0 {
+ handle, suffix = suffix, handle
+ }
+ }
+ }
+
+ // Check the character which ends the tag.
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ if !is_blankz(parser.buffer, parser.buffer_pos) {
+ yaml_parser_set_scanner_error(parser, "while scanning a tag",
+ start_mark, "did not find expected whitespace or line break")
+ return false
+ }
+
+ end_mark := parser.mark
+
+ // Create a token.
+ *token = yaml_token_t{
+ typ: yaml_TAG_TOKEN,
+ start_mark: start_mark,
+ end_mark: end_mark,
+ value: handle,
+ suffix: suffix,
+ }
+ return true
+}
+
+// Scan a tag handle.
+func yaml_parser_scan_tag_handle(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, handle *[]byte) bool {
+ // Check the initial '!' character.
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ if parser.buffer[parser.buffer_pos] != '!' {
+ yaml_parser_set_scanner_tag_error(parser, directive,
+ start_mark, "did not find expected '!'")
+ return false
+ }
+
+ var s []byte
+
+ // Copy the '!' character.
+ s = read(parser, s)
+
+ // Copy all subsequent alphabetical and numerical characters.
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ for is_alpha(parser.buffer, parser.buffer_pos) {
+ s = read(parser, s)
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ }
+
+ // Check if the trailing character is '!' and copy it.
+ if parser.buffer[parser.buffer_pos] == '!' {
+ s = read(parser, s)
+ } else {
+ // It's either the '!' tag or not really a tag handle. If it's a %TAG
+ // directive, it's an error. If it's a tag token, it must be a part of URI.
+ if directive && string(s) != "!" {
+ yaml_parser_set_scanner_tag_error(parser, directive,
+ start_mark, "did not find expected '!'")
+ return false
+ }
+ }
+
+ *handle = s
+ return true
+}
+
+// Scan a tag.
+func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte, start_mark yaml_mark_t, uri *[]byte) bool {
+ //size_t length = head ? strlen((char *)head) : 0
+ var s []byte
+ hasTag := len(head) > 0
+
+ // Copy the head if needed.
+ //
+ // Note that we don't copy the leading '!' character.
+ if len(head) > 1 {
+ s = append(s, head[1:]...)
+ }
+
+ // Scan the tag.
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+
+ // The set of characters that may appear in URI is as follows:
+ //
+ // '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&',
+ // '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']',
+ // '%'.
+ // [Go] TODO Convert this into more reasonable logic.
+ for is_alpha(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == ';' ||
+ parser.buffer[parser.buffer_pos] == '/' || parser.buffer[parser.buffer_pos] == '?' ||
+ parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == '@' ||
+ parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '=' ||
+ parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '$' ||
+ parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '.' ||
+ parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '~' ||
+ parser.buffer[parser.buffer_pos] == '*' || parser.buffer[parser.buffer_pos] == '\'' ||
+ parser.buffer[parser.buffer_pos] == '(' || parser.buffer[parser.buffer_pos] == ')' ||
+ parser.buffer[parser.buffer_pos] == '[' || parser.buffer[parser.buffer_pos] == ']' ||
+ parser.buffer[parser.buffer_pos] == '%' {
+ // Check if it is a URI-escape sequence.
+ if parser.buffer[parser.buffer_pos] == '%' {
+ if !yaml_parser_scan_uri_escapes(parser, directive, start_mark, &s) {
+ return false
+ }
+ } else {
+ s = read(parser, s)
+ }
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ hasTag = true
+ }
+
+ if !hasTag {
+ yaml_parser_set_scanner_tag_error(parser, directive,
+ start_mark, "did not find expected tag URI")
+ return false
+ }
+ *uri = s
+ return true
+}
+
+// Decode an URI-escape sequence corresponding to a single UTF-8 character.
+func yaml_parser_scan_uri_escapes(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, s *[]byte) bool {
+
+ // Decode the required number of characters.
+ w := 1024
+ for w > 0 {
+ // Check for a URI-escaped octet.
+ if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) {
+ return false
+ }
+
+ if !(parser.buffer[parser.buffer_pos] == '%' &&
+ is_hex(parser.buffer, parser.buffer_pos+1) &&
+ is_hex(parser.buffer, parser.buffer_pos+2)) {
+ return yaml_parser_set_scanner_tag_error(parser, directive,
+ start_mark, "did not find URI escaped octet")
+ }
+
+ // Get the octet.
+ octet := byte((as_hex(parser.buffer, parser.buffer_pos+1) << 4) + as_hex(parser.buffer, parser.buffer_pos+2))
+
+ // If it is the leading octet, determine the length of the UTF-8 sequence.
+ if w == 1024 {
+ w = width(octet)
+ if w == 0 {
+ return yaml_parser_set_scanner_tag_error(parser, directive,
+ start_mark, "found an incorrect leading UTF-8 octet")
+ }
+ } else {
+ // Check if the trailing octet is correct.
+ if octet&0xC0 != 0x80 {
+ return yaml_parser_set_scanner_tag_error(parser, directive,
+ start_mark, "found an incorrect trailing UTF-8 octet")
+ }
+ }
+
+ // Copy the octet and move the pointers.
+ *s = append(*s, octet)
+ skip(parser)
+ skip(parser)
+ skip(parser)
+ w--
+ }
+ return true
+}
+
+// Scan a block scalar.
+func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, literal bool) bool {
+ // Eat the indicator '|' or '>'.
+ start_mark := parser.mark
+ skip(parser)
+
+ // Scan the additional block scalar indicators.
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+
+ // Check for a chomping indicator.
+ var chomping, increment int
+ if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' {
+ // Set the chomping method and eat the indicator.
+ if parser.buffer[parser.buffer_pos] == '+' {
+ chomping = +1
+ } else {
+ chomping = -1
+ }
+ skip(parser)
+
+ // Check for an indentation indicator.
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ if is_digit(parser.buffer, parser.buffer_pos) {
+ // Check that the indentation is greater than 0.
+ if parser.buffer[parser.buffer_pos] == '0' {
+ yaml_parser_set_scanner_error(parser, "while scanning a block scalar",
+ start_mark, "found an indentation indicator equal to 0")
+ return false
+ }
+
+ // Get the indentation level and eat the indicator.
+ increment = as_digit(parser.buffer, parser.buffer_pos)
+ skip(parser)
+ }
+
+ } else if is_digit(parser.buffer, parser.buffer_pos) {
+ // Do the same as above, but in the opposite order.
+
+ if parser.buffer[parser.buffer_pos] == '0' {
+ yaml_parser_set_scanner_error(parser, "while scanning a block scalar",
+ start_mark, "found an indentation indicator equal to 0")
+ return false
+ }
+ increment = as_digit(parser.buffer, parser.buffer_pos)
+ skip(parser)
+
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' {
+ if parser.buffer[parser.buffer_pos] == '+' {
+ chomping = +1
+ } else {
+ chomping = -1
+ }
+ skip(parser)
+ }
+ }
+
+ // Eat whitespaces and comments to the end of the line.
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ for is_blank(parser.buffer, parser.buffer_pos) {
+ skip(parser)
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ }
+ if parser.buffer[parser.buffer_pos] == '#' {
+ if !yaml_parser_scan_line_comment(parser, start_mark) {
+ return false
+ }
+ for !is_breakz(parser.buffer, parser.buffer_pos) {
+ skip(parser)
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ }
+ }
+
+ // Check if we are at the end of the line.
+ if !is_breakz(parser.buffer, parser.buffer_pos) {
+ yaml_parser_set_scanner_error(parser, "while scanning a block scalar",
+ start_mark, "did not find expected comment or line break")
+ return false
+ }
+
+ // Eat a line break.
+ if is_break(parser.buffer, parser.buffer_pos) {
+ if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
+ return false
+ }
+ skip_line(parser)
+ }
+
+ end_mark := parser.mark
+
+ // Set the indentation level if it was specified.
+ var indent int
+ if increment > 0 {
+ if parser.indent >= 0 {
+ indent = parser.indent + increment
+ } else {
+ indent = increment
+ }
+ }
+
+ // Scan the leading line breaks and determine the indentation level if needed.
+ var s, leading_break, trailing_breaks []byte
+ if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) {
+ return false
+ }
+
+ // Scan the block scalar content.
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ var leading_blank, trailing_blank bool
+ for parser.mark.column == indent && !is_z(parser.buffer, parser.buffer_pos) {
+ // We are at the beginning of a non-empty line.
+
+ // Is it a trailing whitespace?
+ trailing_blank = is_blank(parser.buffer, parser.buffer_pos)
+
+ // Check if we need to fold the leading line break.
+ if !literal && !leading_blank && !trailing_blank && len(leading_break) > 0 && leading_break[0] == '\n' {
+ // Do we need to join the lines by space?
+ if len(trailing_breaks) == 0 {
+ s = append(s, ' ')
+ }
+ } else {
+ s = append(s, leading_break...)
+ }
+ leading_break = leading_break[:0]
+
+ // Append the remaining line breaks.
+ s = append(s, trailing_breaks...)
+ trailing_breaks = trailing_breaks[:0]
+
+ // Is it a leading whitespace?
+ leading_blank = is_blank(parser.buffer, parser.buffer_pos)
+
+ // Consume the current line.
+ for !is_breakz(parser.buffer, parser.buffer_pos) {
+ s = read(parser, s)
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ }
+
+ // Consume the line break.
+ if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
+ return false
+ }
+
+ leading_break = read_line(parser, leading_break)
+
+ // Eat the following indentation spaces and line breaks.
+ if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) {
+ return false
+ }
+ }
+
+ // Chomp the tail.
+ if chomping != -1 {
+ s = append(s, leading_break...)
+ }
+ if chomping == 1 {
+ s = append(s, trailing_breaks...)
+ }
+
+ // Create a token.
+ *token = yaml_token_t{
+ typ: yaml_SCALAR_TOKEN,
+ start_mark: start_mark,
+ end_mark: end_mark,
+ value: s,
+ style: yaml_LITERAL_SCALAR_STYLE,
+ }
+ if !literal {
+ token.style = yaml_FOLDED_SCALAR_STYLE
+ }
+ return true
+}
+
+// Scan indentation spaces and line breaks for a block scalar. Determine the
+// indentation level if needed.
+func yaml_parser_scan_block_scalar_breaks(parser *yaml_parser_t, indent *int, breaks *[]byte, start_mark yaml_mark_t, end_mark *yaml_mark_t) bool {
+ *end_mark = parser.mark
+
+ // Eat the indentation spaces and line breaks.
+ max_indent := 0
+ for {
+ // Eat the indentation spaces.
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ for (*indent == 0 || parser.mark.column < *indent) && is_space(parser.buffer, parser.buffer_pos) {
+ skip(parser)
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ }
+ if parser.mark.column > max_indent {
+ max_indent = parser.mark.column
+ }
+
+ // Check for a tab character messing the indentation.
+ if (*indent == 0 || parser.mark.column < *indent) && is_tab(parser.buffer, parser.buffer_pos) {
+ return yaml_parser_set_scanner_error(parser, "while scanning a block scalar",
+ start_mark, "found a tab character where an indentation space is expected")
+ }
+
+ // Have we found a non-empty line?
+ if !is_break(parser.buffer, parser.buffer_pos) {
+ break
+ }
+
+ // Consume the line break.
+ if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
+ return false
+ }
+ // [Go] Should really be returning breaks instead.
+ *breaks = read_line(parser, *breaks)
+ *end_mark = parser.mark
+ }
+
+ // Determine the indentation level if needed.
+ if *indent == 0 {
+ *indent = max_indent
+ if *indent < parser.indent+1 {
+ *indent = parser.indent + 1
+ }
+ if *indent < 1 {
+ *indent = 1
+ }
+ }
+ return true
+}
+
+// Scan a quoted scalar.
+func yaml_parser_scan_flow_scalar(parser *yaml_parser_t, token *yaml_token_t, single bool) bool {
+ // Eat the left quote.
+ start_mark := parser.mark
+ skip(parser)
+
+ // Consume the content of the quoted scalar.
+ var s, leading_break, trailing_breaks, whitespaces []byte
+ for {
+ // Check that there are no document indicators at the beginning of the line.
+ if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) {
+ return false
+ }
+
+ if parser.mark.column == 0 &&
+ ((parser.buffer[parser.buffer_pos+0] == '-' &&
+ parser.buffer[parser.buffer_pos+1] == '-' &&
+ parser.buffer[parser.buffer_pos+2] == '-') ||
+ (parser.buffer[parser.buffer_pos+0] == '.' &&
+ parser.buffer[parser.buffer_pos+1] == '.' &&
+ parser.buffer[parser.buffer_pos+2] == '.')) &&
+ is_blankz(parser.buffer, parser.buffer_pos+3) {
+ yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar",
+ start_mark, "found unexpected document indicator")
+ return false
+ }
+
+ // Check for EOF.
+ if is_z(parser.buffer, parser.buffer_pos) {
+ yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar",
+ start_mark, "found unexpected end of stream")
+ return false
+ }
+
+ // Consume non-blank characters.
+ leading_blanks := false
+ for !is_blankz(parser.buffer, parser.buffer_pos) {
+ if single && parser.buffer[parser.buffer_pos] == '\'' && parser.buffer[parser.buffer_pos+1] == '\'' {
+ // Is is an escaped single quote.
+ s = append(s, '\'')
+ skip(parser)
+ skip(parser)
+
+ } else if single && parser.buffer[parser.buffer_pos] == '\'' {
+ // It is a right single quote.
+ break
+ } else if !single && parser.buffer[parser.buffer_pos] == '"' {
+ // It is a right double quote.
+ break
+
+ } else if !single && parser.buffer[parser.buffer_pos] == '\\' && is_break(parser.buffer, parser.buffer_pos+1) {
+ // It is an escaped line break.
+ if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) {
+ return false
+ }
+ skip(parser)
+ skip_line(parser)
+ leading_blanks = true
+ break
+
+ } else if !single && parser.buffer[parser.buffer_pos] == '\\' {
+ // It is an escape sequence.
+ code_length := 0
+
+ // Check the escape character.
+ switch parser.buffer[parser.buffer_pos+1] {
+ case '0':
+ s = append(s, 0)
+ case 'a':
+ s = append(s, '\x07')
+ case 'b':
+ s = append(s, '\x08')
+ case 't', '\t':
+ s = append(s, '\x09')
+ case 'n':
+ s = append(s, '\x0A')
+ case 'v':
+ s = append(s, '\x0B')
+ case 'f':
+ s = append(s, '\x0C')
+ case 'r':
+ s = append(s, '\x0D')
+ case 'e':
+ s = append(s, '\x1B')
+ case ' ':
+ s = append(s, '\x20')
+ case '"':
+ s = append(s, '"')
+ case '\'':
+ s = append(s, '\'')
+ case '\\':
+ s = append(s, '\\')
+ case 'N': // NEL (#x85)
+ s = append(s, '\xC2')
+ s = append(s, '\x85')
+ case '_': // #xA0
+ s = append(s, '\xC2')
+ s = append(s, '\xA0')
+ case 'L': // LS (#x2028)
+ s = append(s, '\xE2')
+ s = append(s, '\x80')
+ s = append(s, '\xA8')
+ case 'P': // PS (#x2029)
+ s = append(s, '\xE2')
+ s = append(s, '\x80')
+ s = append(s, '\xA9')
+ case 'x':
+ code_length = 2
+ case 'u':
+ code_length = 4
+ case 'U':
+ code_length = 8
+ default:
+ yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar",
+ start_mark, "found unknown escape character")
+ return false
+ }
+
+ skip(parser)
+ skip(parser)
+
+ // Consume an arbitrary escape code.
+ if code_length > 0 {
+ var value int
+
+ // Scan the character value.
+ if parser.unread < code_length && !yaml_parser_update_buffer(parser, code_length) {
+ return false
+ }
+ for k := 0; k < code_length; k++ {
+ if !is_hex(parser.buffer, parser.buffer_pos+k) {
+ yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar",
+ start_mark, "did not find expected hexdecimal number")
+ return false
+ }
+ value = (value << 4) + as_hex(parser.buffer, parser.buffer_pos+k)
+ }
+
+ // Check the value and write the character.
+ if (value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF {
+ yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar",
+ start_mark, "found invalid Unicode character escape code")
+ return false
+ }
+ if value <= 0x7F {
+ s = append(s, byte(value))
+ } else if value <= 0x7FF {
+ s = append(s, byte(0xC0+(value>>6)))
+ s = append(s, byte(0x80+(value&0x3F)))
+ } else if value <= 0xFFFF {
+ s = append(s, byte(0xE0+(value>>12)))
+ s = append(s, byte(0x80+((value>>6)&0x3F)))
+ s = append(s, byte(0x80+(value&0x3F)))
+ } else {
+ s = append(s, byte(0xF0+(value>>18)))
+ s = append(s, byte(0x80+((value>>12)&0x3F)))
+ s = append(s, byte(0x80+((value>>6)&0x3F)))
+ s = append(s, byte(0x80+(value&0x3F)))
+ }
+
+ // Advance the pointer.
+ for k := 0; k < code_length; k++ {
+ skip(parser)
+ }
+ }
+ } else {
+ // It is a non-escaped non-blank character.
+ s = read(parser, s)
+ }
+ if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
+ return false
+ }
+ }
+
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+
+ // Check if we are at the end of the scalar.
+ if single {
+ if parser.buffer[parser.buffer_pos] == '\'' {
+ break
+ }
+ } else {
+ if parser.buffer[parser.buffer_pos] == '"' {
+ break
+ }
+ }
+
+ // Consume blank characters.
+ for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) {
+ if is_blank(parser.buffer, parser.buffer_pos) {
+ // Consume a space or a tab character.
+ if !leading_blanks {
+ whitespaces = read(parser, whitespaces)
+ } else {
+ skip(parser)
+ }
+ } else {
+ if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
+ return false
+ }
+
+ // Check if it is a first line break.
+ if !leading_blanks {
+ whitespaces = whitespaces[:0]
+ leading_break = read_line(parser, leading_break)
+ leading_blanks = true
+ } else {
+ trailing_breaks = read_line(parser, trailing_breaks)
+ }
+ }
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ }
+
+ // Join the whitespaces or fold line breaks.
+ if leading_blanks {
+ // Do we need to fold line breaks?
+ if len(leading_break) > 0 && leading_break[0] == '\n' {
+ if len(trailing_breaks) == 0 {
+ s = append(s, ' ')
+ } else {
+ s = append(s, trailing_breaks...)
+ }
+ } else {
+ s = append(s, leading_break...)
+ s = append(s, trailing_breaks...)
+ }
+ trailing_breaks = trailing_breaks[:0]
+ leading_break = leading_break[:0]
+ } else {
+ s = append(s, whitespaces...)
+ whitespaces = whitespaces[:0]
+ }
+ }
+
+ // Eat the right quote.
+ skip(parser)
+ end_mark := parser.mark
+
+ // Create a token.
+ *token = yaml_token_t{
+ typ: yaml_SCALAR_TOKEN,
+ start_mark: start_mark,
+ end_mark: end_mark,
+ value: s,
+ style: yaml_SINGLE_QUOTED_SCALAR_STYLE,
+ }
+ if !single {
+ token.style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
+ }
+ return true
+}
+
+// Scan a plain scalar.
+func yaml_parser_scan_plain_scalar(parser *yaml_parser_t, token *yaml_token_t) bool {
+
+ var s, leading_break, trailing_breaks, whitespaces []byte
+ var leading_blanks bool
+ var indent = parser.indent + 1
+
+ start_mark := parser.mark
+ end_mark := parser.mark
+
+ // Consume the content of the plain scalar.
+ for {
+ // Check for a document indicator.
+ if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) {
+ return false
+ }
+ if parser.mark.column == 0 &&
+ ((parser.buffer[parser.buffer_pos+0] == '-' &&
+ parser.buffer[parser.buffer_pos+1] == '-' &&
+ parser.buffer[parser.buffer_pos+2] == '-') ||
+ (parser.buffer[parser.buffer_pos+0] == '.' &&
+ parser.buffer[parser.buffer_pos+1] == '.' &&
+ parser.buffer[parser.buffer_pos+2] == '.')) &&
+ is_blankz(parser.buffer, parser.buffer_pos+3) {
+ break
+ }
+
+ // Check for a comment.
+ if parser.buffer[parser.buffer_pos] == '#' {
+ break
+ }
+
+ // Consume non-blank characters.
+ for !is_blankz(parser.buffer, parser.buffer_pos) {
+
+ // Check for indicators that may end a plain scalar.
+ if (parser.buffer[parser.buffer_pos] == ':' && is_blankz(parser.buffer, parser.buffer_pos+1)) ||
+ (parser.flow_level > 0 &&
+ (parser.buffer[parser.buffer_pos] == ',' ||
+ parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == '[' ||
+ parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' ||
+ parser.buffer[parser.buffer_pos] == '}')) {
+ break
+ }
+
+ // Check if we need to join whitespaces and breaks.
+ if leading_blanks || len(whitespaces) > 0 {
+ if leading_blanks {
+ // Do we need to fold line breaks?
+ if leading_break[0] == '\n' {
+ if len(trailing_breaks) == 0 {
+ s = append(s, ' ')
+ } else {
+ s = append(s, trailing_breaks...)
+ }
+ } else {
+ s = append(s, leading_break...)
+ s = append(s, trailing_breaks...)
+ }
+ trailing_breaks = trailing_breaks[:0]
+ leading_break = leading_break[:0]
+ leading_blanks = false
+ } else {
+ s = append(s, whitespaces...)
+ whitespaces = whitespaces[:0]
+ }
+ }
+
+ // Copy the character.
+ s = read(parser, s)
+
+ end_mark = parser.mark
+ if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
+ return false
+ }
+ }
+
+ // Is it the end?
+ if !(is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos)) {
+ break
+ }
+
+ // Consume blank characters.
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+
+ for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) {
+ if is_blank(parser.buffer, parser.buffer_pos) {
+
+ // Check for tab characters that abuse indentation.
+ if leading_blanks && parser.mark.column < indent && is_tab(parser.buffer, parser.buffer_pos) {
+ yaml_parser_set_scanner_error(parser, "while scanning a plain scalar",
+ start_mark, "found a tab character that violates indentation")
+ return false
+ }
+
+ // Consume a space or a tab character.
+ if !leading_blanks {
+ whitespaces = read(parser, whitespaces)
+ } else {
+ skip(parser)
+ }
+ } else {
+ if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
+ return false
+ }
+
+ // Check if it is a first line break.
+ if !leading_blanks {
+ whitespaces = whitespaces[:0]
+ leading_break = read_line(parser, leading_break)
+ leading_blanks = true
+ } else {
+ trailing_breaks = read_line(parser, trailing_breaks)
+ }
+ }
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ }
+
+ // Check indentation level.
+ if parser.flow_level == 0 && parser.mark.column < indent {
+ break
+ }
+ }
+
+ // Create a token.
+ *token = yaml_token_t{
+ typ: yaml_SCALAR_TOKEN,
+ start_mark: start_mark,
+ end_mark: end_mark,
+ value: s,
+ style: yaml_PLAIN_SCALAR_STYLE,
+ }
+
+ // Note that we change the 'simple_key_allowed' flag.
+ if leading_blanks {
+ parser.simple_key_allowed = true
+ }
+ return true
+}
+
+func yaml_parser_scan_line_comment(parser *yaml_parser_t, token_mark yaml_mark_t) bool {
+ if parser.newlines > 0 {
+ return true
+ }
+
+ var start_mark yaml_mark_t
+ var text []byte
+
+ for peek := 0; peek < 512; peek++ {
+ if parser.unread < peek+1 && !yaml_parser_update_buffer(parser, peek+1) {
+ break
+ }
+ if is_blank(parser.buffer, parser.buffer_pos+peek) {
+ continue
+ }
+ if parser.buffer[parser.buffer_pos+peek] == '#' {
+ seen := parser.mark.index + peek
+ for {
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ if is_breakz(parser.buffer, parser.buffer_pos) {
+ if parser.mark.index >= seen {
+ break
+ }
+ if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
+ return false
+ }
+ skip_line(parser)
+ } else if parser.mark.index >= seen {
+ if len(text) == 0 {
+ start_mark = parser.mark
+ }
+ text = read(parser, text)
+ } else {
+ skip(parser)
+ }
+ }
+ }
+ break
+ }
+ if len(text) > 0 {
+ parser.comments = append(parser.comments, yaml_comment_t{
+ token_mark: token_mark,
+ start_mark: start_mark,
+ line: text,
+ })
+ }
+ return true
+}
+
+func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) bool {
+ token := parser.tokens[len(parser.tokens)-1]
+
+ if token.typ == yaml_FLOW_ENTRY_TOKEN && len(parser.tokens) > 1 {
+ token = parser.tokens[len(parser.tokens)-2]
+ }
+
+ var token_mark = token.start_mark
+ var start_mark yaml_mark_t
+ var next_indent = parser.indent
+ if next_indent < 0 {
+ next_indent = 0
+ }
+
+ var recent_empty = false
+ var first_empty = parser.newlines <= 1
+
+ var line = parser.mark.line
+ var column = parser.mark.column
+
+ var text []byte
+
+ // The foot line is the place where a comment must start to
+ // still be considered as a foot of the prior content.
+ // If there's some content in the currently parsed line, then
+ // the foot is the line below it.
+ var foot_line = -1
+ if scan_mark.line > 0 {
+ foot_line = parser.mark.line - parser.newlines + 1
+ if parser.newlines == 0 && parser.mark.column > 1 {
+ foot_line++
+ }
+ }
+
+ var peek = 0
+ for ; peek < 512; peek++ {
+ if parser.unread < peek+1 && !yaml_parser_update_buffer(parser, peek+1) {
+ break
+ }
+ column++
+ if is_blank(parser.buffer, parser.buffer_pos+peek) {
+ continue
+ }
+ c := parser.buffer[parser.buffer_pos+peek]
+ var close_flow = parser.flow_level > 0 && (c == ']' || c == '}')
+ if close_flow || is_breakz(parser.buffer, parser.buffer_pos+peek) {
+ // Got line break or terminator.
+ if close_flow || !recent_empty {
+ if close_flow || first_empty && (start_mark.line == foot_line && token.typ != yaml_VALUE_TOKEN || start_mark.column-1 < next_indent) {
+ // This is the first empty line and there were no empty lines before,
+ // so this initial part of the comment is a foot of the prior token
+ // instead of being a head for the following one. Split it up.
+ // Alternatively, this might also be the last comment inside a flow
+ // scope, so it must be a footer.
+ if len(text) > 0 {
+ if start_mark.column-1 < next_indent {
+ // If dedented it's unrelated to the prior token.
+ token_mark = start_mark
+ }
+ parser.comments = append(parser.comments, yaml_comment_t{
+ scan_mark: scan_mark,
+ token_mark: token_mark,
+ start_mark: start_mark,
+ end_mark: yaml_mark_t{parser.mark.index + peek, line, column},
+ foot: text,
+ })
+ scan_mark = yaml_mark_t{parser.mark.index + peek, line, column}
+ token_mark = scan_mark
+ text = nil
+ }
+ } else {
+ if len(text) > 0 && parser.buffer[parser.buffer_pos+peek] != 0 {
+ text = append(text, '\n')
+ }
+ }
+ }
+ if !is_break(parser.buffer, parser.buffer_pos+peek) {
+ break
+ }
+ first_empty = false
+ recent_empty = true
+ column = 0
+ line++
+ continue
+ }
+
+ if len(text) > 0 && (close_flow || column-1 < next_indent && column != start_mark.column) {
+ // The comment at the different indentation is a foot of the
+ // preceding data rather than a head of the upcoming one.
+ parser.comments = append(parser.comments, yaml_comment_t{
+ scan_mark: scan_mark,
+ token_mark: token_mark,
+ start_mark: start_mark,
+ end_mark: yaml_mark_t{parser.mark.index + peek, line, column},
+ foot: text,
+ })
+ scan_mark = yaml_mark_t{parser.mark.index + peek, line, column}
+ token_mark = scan_mark
+ text = nil
+ }
+
+ if parser.buffer[parser.buffer_pos+peek] != '#' {
+ break
+ }
+
+ if len(text) == 0 {
+ start_mark = yaml_mark_t{parser.mark.index + peek, line, column}
+ } else {
+ text = append(text, '\n')
+ }
+
+ recent_empty = false
+
+ // Consume until after the consumed comment line.
+ seen := parser.mark.index + peek
+ for {
+ if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+ return false
+ }
+ if is_breakz(parser.buffer, parser.buffer_pos) {
+ if parser.mark.index >= seen {
+ break
+ }
+ if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
+ return false
+ }
+ skip_line(parser)
+ } else if parser.mark.index >= seen {
+ text = read(parser, text)
+ } else {
+ skip(parser)
+ }
+ }
+
+ peek = 0
+ column = 0
+ line = parser.mark.line
+ next_indent = parser.indent
+ if next_indent < 0 {
+ next_indent = 0
+ }
+ }
+
+ if len(text) > 0 {
+ parser.comments = append(parser.comments, yaml_comment_t{
+ scan_mark: scan_mark,
+ token_mark: start_mark,
+ start_mark: start_mark,
+ end_mark: yaml_mark_t{parser.mark.index + peek - 1, line, column},
+ head: text,
+ })
+ }
+ return true
+}
diff --git a/vendor/go.yaml.in/yaml/v3/sorter.go b/vendor/go.yaml.in/yaml/v3/sorter.go
new file mode 100644
index 000000000..9210ece7e
--- /dev/null
+++ b/vendor/go.yaml.in/yaml/v3/sorter.go
@@ -0,0 +1,134 @@
+//
+// Copyright (c) 2011-2019 Canonical Ltd
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package yaml
+
+import (
+ "reflect"
+ "unicode"
+)
+
+type keyList []reflect.Value
+
+func (l keyList) Len() int { return len(l) }
+func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
+func (l keyList) Less(i, j int) bool {
+ a := l[i]
+ b := l[j]
+ ak := a.Kind()
+ bk := b.Kind()
+ for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() {
+ a = a.Elem()
+ ak = a.Kind()
+ }
+ for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() {
+ b = b.Elem()
+ bk = b.Kind()
+ }
+ af, aok := keyFloat(a)
+ bf, bok := keyFloat(b)
+ if aok && bok {
+ if af != bf {
+ return af < bf
+ }
+ if ak != bk {
+ return ak < bk
+ }
+ return numLess(a, b)
+ }
+ if ak != reflect.String || bk != reflect.String {
+ return ak < bk
+ }
+ ar, br := []rune(a.String()), []rune(b.String())
+ digits := false
+ for i := 0; i < len(ar) && i < len(br); i++ {
+ if ar[i] == br[i] {
+ digits = unicode.IsDigit(ar[i])
+ continue
+ }
+ al := unicode.IsLetter(ar[i])
+ bl := unicode.IsLetter(br[i])
+ if al && bl {
+ return ar[i] < br[i]
+ }
+ if al || bl {
+ if digits {
+ return al
+ } else {
+ return bl
+ }
+ }
+ var ai, bi int
+ var an, bn int64
+ if ar[i] == '0' || br[i] == '0' {
+ for j := i - 1; j >= 0 && unicode.IsDigit(ar[j]); j-- {
+ if ar[j] != '0' {
+ an = 1
+ bn = 1
+ break
+ }
+ }
+ }
+ for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ {
+ an = an*10 + int64(ar[ai]-'0')
+ }
+ for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ {
+ bn = bn*10 + int64(br[bi]-'0')
+ }
+ if an != bn {
+ return an < bn
+ }
+ if ai != bi {
+ return ai < bi
+ }
+ return ar[i] < br[i]
+ }
+ return len(ar) < len(br)
+}
+
+// keyFloat returns a float value for v if it is a number/bool
+// and whether it is a number/bool or not.
+func keyFloat(v reflect.Value) (f float64, ok bool) {
+ switch v.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return float64(v.Int()), true
+ case reflect.Float32, reflect.Float64:
+ return v.Float(), true
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return float64(v.Uint()), true
+ case reflect.Bool:
+ if v.Bool() {
+ return 1, true
+ }
+ return 0, true
+ }
+ return 0, false
+}
+
+// numLess returns whether a < b.
+// a and b must necessarily have the same kind.
+func numLess(a, b reflect.Value) bool {
+ switch a.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return a.Int() < b.Int()
+ case reflect.Float32, reflect.Float64:
+ return a.Float() < b.Float()
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return a.Uint() < b.Uint()
+ case reflect.Bool:
+ return !a.Bool() && b.Bool()
+ }
+ panic("not a number")
+}
diff --git a/vendor/go.yaml.in/yaml/v3/writerc.go b/vendor/go.yaml.in/yaml/v3/writerc.go
new file mode 100644
index 000000000..266d0b092
--- /dev/null
+++ b/vendor/go.yaml.in/yaml/v3/writerc.go
@@ -0,0 +1,48 @@
+//
+// Copyright (c) 2011-2019 Canonical Ltd
+// Copyright (c) 2006-2010 Kirill Simonov
+//
+// 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.
+
+package yaml
+
+// Set the writer error and return false.
+func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool {
+ emitter.error = yaml_WRITER_ERROR
+ emitter.problem = problem
+ return false
+}
+
+// Flush the output buffer.
+func yaml_emitter_flush(emitter *yaml_emitter_t) bool {
+ if emitter.write_handler == nil {
+ panic("write handler not set")
+ }
+
+ // Check if the buffer is empty.
+ if emitter.buffer_pos == 0 {
+ return true
+ }
+
+ if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil {
+ return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error())
+ }
+ emitter.buffer_pos = 0
+ return true
+}
diff --git a/vendor/go.yaml.in/yaml/v3/yaml.go b/vendor/go.yaml.in/yaml/v3/yaml.go
new file mode 100644
index 000000000..0b101cd20
--- /dev/null
+++ b/vendor/go.yaml.in/yaml/v3/yaml.go
@@ -0,0 +1,703 @@
+//
+// Copyright (c) 2011-2019 Canonical Ltd
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package yaml implements YAML support for the Go language.
+//
+// Source code and other details for the project are available at GitHub:
+//
+// https://github.com/yaml/go-yaml
+package yaml
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "reflect"
+ "strings"
+ "sync"
+ "unicode/utf8"
+)
+
+// The Unmarshaler interface may be implemented by types to customize their
+// behavior when being unmarshaled from a YAML document.
+type Unmarshaler interface {
+ UnmarshalYAML(value *Node) error
+}
+
+type obsoleteUnmarshaler interface {
+ UnmarshalYAML(unmarshal func(interface{}) error) error
+}
+
+// The Marshaler 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 Marshaler interface {
+ MarshalYAML() (interface{}, error)
+}
+
+// Unmarshal decodes the first document found within the in byte slice
+// and assigns decoded values into the out value.
+//
+// Maps and pointers (to a struct, string, int, etc) are accepted as out
+// values. If an internal pointer within a struct is not initialized,
+// the yaml package will initialize it if necessary for unmarshalling
+// the provided data. The out parameter must not be nil.
+//
+// The type of the decoded values should be compatible with the respective
+// values in out. If one or more values cannot be decoded due to a type
+// mismatches, decoding continues partially until the end of the YAML
+// content, and a *yaml.TypeError is returned with details for all
+// missed values.
+//
+// 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(in []byte, out interface{}) (err error) {
+ return unmarshal(in, out, false)
+}
+
+// A Decoder reads and decodes YAML values from an input stream.
+type Decoder struct {
+ parser *parser
+ knownFields bool
+}
+
+// NewDecoder returns a new decoder that reads from r.
+//
+// The decoder introduces its own buffering and may read
+// data from r beyond the YAML values requested.
+func NewDecoder(r io.Reader) *Decoder {
+ return &Decoder{
+ parser: newParserFromReader(r),
+ }
+}
+
+// KnownFields ensures that the keys in decoded mappings to
+// exist as fields in the struct being decoded into.
+func (dec *Decoder) KnownFields(enable bool) {
+ dec.knownFields = enable
+}
+
+// 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 (dec *Decoder) Decode(v interface{}) (err error) {
+ d := newDecoder()
+ d.knownFields = dec.knownFields
+ defer handleErr(&err)
+ node := dec.parser.parse()
+ if node == nil {
+ return io.EOF
+ }
+ out := reflect.ValueOf(v)
+ if out.Kind() == reflect.Ptr && !out.IsNil() {
+ out = out.Elem()
+ }
+ d.unmarshal(node, out)
+ if len(d.terrors) > 0 {
+ return &TypeError{d.terrors}
+ }
+ return nil
+}
+
+// Decode decodes the node and stores its data into the value pointed to by v.
+//
+// See the documentation for Unmarshal for details about the
+// conversion of YAML into a Go value.
+func (n *Node) Decode(v interface{}) (err error) {
+ d := newDecoder()
+ defer handleErr(&err)
+ out := reflect.ValueOf(v)
+ if out.Kind() == reflect.Ptr && !out.IsNil() {
+ out = out.Elem()
+ }
+ d.unmarshal(n, out)
+ if len(d.terrors) > 0 {
+ return &TypeError{d.terrors}
+ }
+ return nil
+}
+
+func unmarshal(in []byte, out interface{}, strict bool) (err error) {
+ defer handleErr(&err)
+ d := newDecoder()
+ p := newParser(in)
+ defer p.destroy()
+ node := p.parse()
+ if node != nil {
+ v := reflect.ValueOf(out)
+ if v.Kind() == reflect.Ptr && !v.IsNil() {
+ v = v.Elem()
+ }
+ d.unmarshal(node, v)
+ }
+ if len(d.terrors) > 0 {
+ return &TypeError{d.terrors}
+ }
+ return nil
+}
+
+// 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 excluded if IsZero 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.
+//
+// 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(in interface{}) (out []byte, err error) {
+ defer handleErr(&err)
+ e := newEncoder()
+ defer e.destroy()
+ e.marshalDoc("", reflect.ValueOf(in))
+ e.finish()
+ out = e.out
+ return
+}
+
+// An Encoder writes YAML values to an output stream.
+type Encoder struct {
+ encoder *encoder
+}
+
+// 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) *Encoder {
+ return &Encoder{
+ encoder: newEncoderWithWriter(w),
+ }
+}
+
+// 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{}) (err error) {
+ defer handleErr(&err)
+ e.encoder.marshalDoc("", reflect.ValueOf(v))
+ return nil
+}
+
+// Encode encodes value v and stores its representation in n.
+//
+// See the documentation for Marshal for details about the
+// conversion of Go values into YAML.
+func (n *Node) Encode(v interface{}) (err error) {
+ defer handleErr(&err)
+ e := newEncoder()
+ defer e.destroy()
+ e.marshalDoc("", reflect.ValueOf(v))
+ e.finish()
+ p := newParser(e.out)
+ p.textless = true
+ defer p.destroy()
+ doc := p.parse()
+ *n = *doc.Content[0]
+ return nil
+}
+
+// SetIndent changes the used indentation used when encoding.
+func (e *Encoder) SetIndent(spaces int) {
+ if spaces < 0 {
+ panic("yaml: cannot indent to a negative number of spaces")
+ }
+ e.encoder.indent = spaces
+}
+
+// CompactSeqIndent makes it so that '- ' is considered part of the indentation.
+func (e *Encoder) CompactSeqIndent() {
+ e.encoder.emitter.compact_sequence_indent = true
+}
+
+// DefaultSeqIndent makes it so that '- ' is not considered part of the indentation.
+func (e *Encoder) DefaultSeqIndent() {
+ e.encoder.emitter.compact_sequence_indent = false
+}
+
+// Close closes the encoder by writing any remaining data.
+// It does not write a stream terminating string "...".
+func (e *Encoder) Close() (err error) {
+ defer handleErr(&err)
+ e.encoder.finish()
+ return nil
+}
+
+func handleErr(err *error) {
+ if v := recover(); v != nil {
+ if e, ok := v.(yamlError); ok {
+ *err = e.err
+ } else {
+ panic(v)
+ }
+ }
+}
+
+type yamlError struct {
+ err error
+}
+
+func fail(err error) {
+ panic(yamlError{err})
+}
+
+func failf(format string, args ...interface{}) {
+ panic(yamlError{fmt.Errorf("yaml: "+format, args...)})
+}
+
+// A TypeError is returned by Unmarshal when one or more fields in
+// the YAML document cannot be properly decoded into the requested
+// types. When this error is returned, the value is still
+// unmarshaled partially.
+type TypeError struct {
+ Errors []string
+}
+
+func (e *TypeError) Error() string {
+ return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n "))
+}
+
+type Kind uint32
+
+const (
+ DocumentNode Kind = 1 << iota
+ SequenceNode
+ MappingNode
+ ScalarNode
+ AliasNode
+)
+
+type Style uint32
+
+const (
+ TaggedStyle Style = 1 << iota
+ DoubleQuotedStyle
+ SingleQuotedStyle
+ LiteralStyle
+ FoldedStyle
+ FlowStyle
+)
+
+// Node represents an element in the YAML document hierarchy. While documents
+// are typically encoded and decoded into higher level types, such as structs
+// and maps, Node is an intermediate representation that allows detailed
+// control over the content being decoded or encoded.
+//
+// It's worth noting that although Node offers access into details such as
+// line numbers, colums, and comments, the content when re-encoded will not
+// have its original textual representation preserved. An effort is made to
+// render the data plesantly, and to preserve comments near the data they
+// describe, though.
+//
+// Values that make use of the Node type interact with the yaml package in the
+// same way any other type would do, by encoding and decoding yaml data
+// directly or indirectly into them.
+//
+// For example:
+//
+// var person struct {
+// Name string
+// Address yaml.Node
+// }
+// err := yaml.Unmarshal(data, &person)
+//
+// Or by itself:
+//
+// var person Node
+// err := yaml.Unmarshal(data, &person)
+type Node struct {
+ // Kind defines whether the node is a document, a mapping, a sequence,
+ // a scalar value, or an alias to another node. The specific data type of
+ // scalar nodes may be obtained via the ShortTag and LongTag methods.
+ Kind Kind
+
+ // Style allows customizing the apperance of the node in the tree.
+ Style Style
+
+ // Tag holds the YAML tag defining the data type for the value.
+ // When decoding, this field will always be set to the resolved tag,
+ // even when it wasn't explicitly provided in the YAML content.
+ // When encoding, if this field is unset the value type will be
+ // implied from the node properties, and if it is set, it will only
+ // be serialized into the representation if TaggedStyle is used or
+ // the implicit tag diverges from the provided one.
+ Tag string
+
+ // Value holds the unescaped and unquoted represenation of the value.
+ Value string
+
+ // Anchor holds the anchor name for this node, which allows aliases to point to it.
+ Anchor string
+
+ // Alias holds the node that this alias points to. Only valid when Kind is AliasNode.
+ Alias *Node
+
+ // Content holds contained nodes for documents, mappings, and sequences.
+ Content []*Node
+
+ // HeadComment holds any comments in the lines preceding the node and
+ // not separated by an empty line.
+ HeadComment string
+
+ // LineComment holds any comments at the end of the line where the node is in.
+ LineComment string
+
+ // FootComment holds any comments following the node and before empty lines.
+ FootComment string
+
+ // Line and Column hold the node position in the decoded YAML text.
+ // These fields are not respected when encoding the node.
+ Line int
+ Column int
+}
+
+// IsZero returns whether the node has all of its fields unset.
+func (n *Node) IsZero() bool {
+ return n.Kind == 0 && n.Style == 0 && n.Tag == "" && n.Value == "" && n.Anchor == "" && n.Alias == nil && n.Content == nil &&
+ n.HeadComment == "" && n.LineComment == "" && n.FootComment == "" && n.Line == 0 && n.Column == 0
+}
+
+// LongTag returns the long form of the tag that indicates the data type for
+// the node. If the Tag field isn't explicitly defined, one will be computed
+// based on the node properties.
+func (n *Node) LongTag() string {
+ return longTag(n.ShortTag())
+}
+
+// ShortTag returns the short form of the YAML tag that indicates data type for
+// the node. If the Tag field isn't explicitly defined, one will be computed
+// based on the node properties.
+func (n *Node) ShortTag() string {
+ if n.indicatedString() {
+ return strTag
+ }
+ if n.Tag == "" || n.Tag == "!" {
+ switch n.Kind {
+ case MappingNode:
+ return mapTag
+ case SequenceNode:
+ return seqTag
+ case AliasNode:
+ if n.Alias != nil {
+ return n.Alias.ShortTag()
+ }
+ case ScalarNode:
+ tag, _ := resolve("", n.Value)
+ return tag
+ case 0:
+ // Special case to make the zero value convenient.
+ if n.IsZero() {
+ return nullTag
+ }
+ }
+ return ""
+ }
+ return shortTag(n.Tag)
+}
+
+func (n *Node) indicatedString() bool {
+ return n.Kind == ScalarNode &&
+ (shortTag(n.Tag) == strTag ||
+ (n.Tag == "" || n.Tag == "!") && n.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0)
+}
+
+// SetString is a convenience function that sets the node to a string value
+// and defines its style in a pleasant way depending on its content.
+func (n *Node) SetString(s string) {
+ n.Kind = ScalarNode
+ if utf8.ValidString(s) {
+ n.Value = s
+ n.Tag = strTag
+ } else {
+ n.Value = encodeBase64(s)
+ n.Tag = binaryTag
+ }
+ if strings.Contains(n.Value, "\n") {
+ n.Style = LiteralStyle
+ }
+}
+
+// --------------------------------------------------------------------------
+// Maintain a mapping of keys to structure field indexes
+
+// The code in this section was copied from mgo/bson.
+
+// structInfo holds details for the serialization of fields of
+// a given struct.
+type structInfo struct {
+ FieldsMap map[string]fieldInfo
+ FieldsList []fieldInfo
+
+ // InlineMap is the number of the field in the struct that
+ // contains an ,inline map, or -1 if there's none.
+ InlineMap int
+
+ // InlineUnmarshalers holds indexes to inlined fields that
+ // contain unmarshaler values.
+ InlineUnmarshalers [][]int
+}
+
+type fieldInfo struct {
+ Key string
+ Num int
+ OmitEmpty bool
+ Flow bool
+ // Id holds the unique field identifier, so we can cheaply
+ // check for field duplicates without maintaining an extra map.
+ Id int
+
+ // Inline holds the field index if the field is part of an inlined struct.
+ Inline []int
+}
+
+var structMap = make(map[reflect.Type]*structInfo)
+var fieldMapMutex sync.RWMutex
+var unmarshalerType reflect.Type
+
+func init() {
+ var v Unmarshaler
+ unmarshalerType = reflect.ValueOf(&v).Elem().Type()
+}
+
+func getStructInfo(st reflect.Type) (*structInfo, error) {
+ fieldMapMutex.RLock()
+ sinfo, found := structMap[st]
+ fieldMapMutex.RUnlock()
+ if found {
+ return sinfo, nil
+ }
+
+ n := st.NumField()
+ fieldsMap := make(map[string]fieldInfo)
+ fieldsList := make([]fieldInfo, 0, n)
+ inlineMap := -1
+ inlineUnmarshalers := [][]int(nil)
+ for i := 0; i != n; i++ {
+ field := st.Field(i)
+ if field.PkgPath != "" && !field.Anonymous {
+ continue // Private field
+ }
+
+ info := fieldInfo{Num: i}
+
+ tag := field.Tag.Get("yaml")
+ if tag == "" && strings.Index(string(field.Tag), ":") < 0 {
+ tag = string(field.Tag)
+ }
+ if tag == "-" {
+ continue
+ }
+
+ inline := false
+ fields := strings.Split(tag, ",")
+ if len(fields) > 1 {
+ for _, flag := range fields[1:] {
+ switch flag {
+ case "omitempty":
+ info.OmitEmpty = true
+ case "flow":
+ info.Flow = true
+ case "inline":
+ inline = true
+ default:
+ return nil, errors.New(fmt.Sprintf("unsupported flag %q in tag %q of type %s", flag, tag, st))
+ }
+ }
+ tag = fields[0]
+ }
+
+ if inline {
+ switch field.Type.Kind() {
+ case reflect.Map:
+ if inlineMap >= 0 {
+ return nil, errors.New("multiple ,inline maps in struct " + st.String())
+ }
+ if field.Type.Key() != reflect.TypeOf("") {
+ return nil, errors.New("option ,inline needs a map with string keys in struct " + st.String())
+ }
+ inlineMap = info.Num
+ case reflect.Struct, reflect.Ptr:
+ ftype := field.Type
+ for ftype.Kind() == reflect.Ptr {
+ ftype = ftype.Elem()
+ }
+ if ftype.Kind() != reflect.Struct {
+ return nil, errors.New("option ,inline may only be used on a struct or map field")
+ }
+ if reflect.PtrTo(ftype).Implements(unmarshalerType) {
+ inlineUnmarshalers = append(inlineUnmarshalers, []int{i})
+ } else {
+ sinfo, err := getStructInfo(ftype)
+ if err != nil {
+ return nil, err
+ }
+ for _, index := range sinfo.InlineUnmarshalers {
+ inlineUnmarshalers = append(inlineUnmarshalers, append([]int{i}, index...))
+ }
+ for _, finfo := range sinfo.FieldsList {
+ if _, found := fieldsMap[finfo.Key]; found {
+ msg := "duplicated key '" + finfo.Key + "' in struct " + st.String()
+ return nil, errors.New(msg)
+ }
+ if finfo.Inline == nil {
+ finfo.Inline = []int{i, finfo.Num}
+ } else {
+ finfo.Inline = append([]int{i}, finfo.Inline...)
+ }
+ finfo.Id = len(fieldsList)
+ fieldsMap[finfo.Key] = finfo
+ fieldsList = append(fieldsList, finfo)
+ }
+ }
+ default:
+ return nil, errors.New("option ,inline may only be used on a struct or map field")
+ }
+ continue
+ }
+
+ if tag != "" {
+ info.Key = tag
+ } else {
+ info.Key = strings.ToLower(field.Name)
+ }
+
+ if _, found = fieldsMap[info.Key]; found {
+ msg := "duplicated key '" + info.Key + "' in struct " + st.String()
+ return nil, errors.New(msg)
+ }
+
+ info.Id = len(fieldsList)
+ fieldsList = append(fieldsList, info)
+ fieldsMap[info.Key] = info
+ }
+
+ sinfo = &structInfo{
+ FieldsMap: fieldsMap,
+ FieldsList: fieldsList,
+ InlineMap: inlineMap,
+ InlineUnmarshalers: inlineUnmarshalers,
+ }
+
+ fieldMapMutex.Lock()
+ structMap[st] = sinfo
+ fieldMapMutex.Unlock()
+ return sinfo, nil
+}
+
+// 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 isZero(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 !isZero(v.Field(i)) {
+ return false
+ }
+ }
+ return true
+ }
+ return false
+}
diff --git a/vendor/go.yaml.in/yaml/v3/yamlh.go b/vendor/go.yaml.in/yaml/v3/yamlh.go
new file mode 100644
index 000000000..f59aa40f6
--- /dev/null
+++ b/vendor/go.yaml.in/yaml/v3/yamlh.go
@@ -0,0 +1,811 @@
+//
+// Copyright (c) 2011-2019 Canonical Ltd
+// Copyright (c) 2006-2010 Kirill Simonov
+//
+// 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.
+
+package yaml
+
+import (
+ "fmt"
+ "io"
+)
+
+// The version directive data.
+type yaml_version_directive_t struct {
+ major int8 // The major version number.
+ minor int8 // The minor version number.
+}
+
+// The tag directive data.
+type yaml_tag_directive_t struct {
+ handle []byte // The tag handle.
+ prefix []byte // The tag prefix.
+}
+
+type yaml_encoding_t int
+
+// The stream encoding.
+const (
+ // Let the parser choose the encoding.
+ yaml_ANY_ENCODING yaml_encoding_t = iota
+
+ yaml_UTF8_ENCODING // The default UTF-8 encoding.
+ yaml_UTF16LE_ENCODING // The UTF-16-LE encoding with BOM.
+ yaml_UTF16BE_ENCODING // The UTF-16-BE encoding with BOM.
+)
+
+type yaml_break_t int
+
+// Line break types.
+const (
+ // Let the parser choose the break type.
+ yaml_ANY_BREAK yaml_break_t = iota
+
+ yaml_CR_BREAK // Use CR for line breaks (Mac style).
+ yaml_LN_BREAK // Use LN for line breaks (Unix style).
+ yaml_CRLN_BREAK // Use CR LN for line breaks (DOS style).
+)
+
+type yaml_error_type_t int
+
+// Many bad things could happen with the parser and emitter.
+const (
+ // No error is produced.
+ yaml_NO_ERROR yaml_error_type_t = iota
+
+ yaml_MEMORY_ERROR // Cannot allocate or reallocate a block of memory.
+ yaml_READER_ERROR // Cannot read or decode the input stream.
+ yaml_SCANNER_ERROR // Cannot scan the input stream.
+ yaml_PARSER_ERROR // Cannot parse the input stream.
+ yaml_COMPOSER_ERROR // Cannot compose a YAML document.
+ yaml_WRITER_ERROR // Cannot write to the output stream.
+ yaml_EMITTER_ERROR // Cannot emit a YAML stream.
+)
+
+// The pointer position.
+type yaml_mark_t struct {
+ index int // The position index.
+ line int // The position line.
+ column int // The position column.
+}
+
+// Node Styles
+
+type yaml_style_t int8
+
+type yaml_scalar_style_t yaml_style_t
+
+// Scalar styles.
+const (
+ // Let the emitter choose the style.
+ yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = 0
+
+ yaml_PLAIN_SCALAR_STYLE yaml_scalar_style_t = 1 << iota // The plain scalar style.
+ yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style.
+ yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style.
+ yaml_LITERAL_SCALAR_STYLE // The literal scalar style.
+ yaml_FOLDED_SCALAR_STYLE // The folded scalar style.
+)
+
+type yaml_sequence_style_t yaml_style_t
+
+// Sequence styles.
+const (
+ // Let the emitter choose the style.
+ yaml_ANY_SEQUENCE_STYLE yaml_sequence_style_t = iota
+
+ yaml_BLOCK_SEQUENCE_STYLE // The block sequence style.
+ yaml_FLOW_SEQUENCE_STYLE // The flow sequence style.
+)
+
+type yaml_mapping_style_t yaml_style_t
+
+// Mapping styles.
+const (
+ // Let the emitter choose the style.
+ yaml_ANY_MAPPING_STYLE yaml_mapping_style_t = iota
+
+ yaml_BLOCK_MAPPING_STYLE // The block mapping style.
+ yaml_FLOW_MAPPING_STYLE // The flow mapping style.
+)
+
+// Tokens
+
+type yaml_token_type_t int
+
+// Token types.
+const (
+ // An empty token.
+ yaml_NO_TOKEN yaml_token_type_t = iota
+
+ yaml_STREAM_START_TOKEN // A STREAM-START token.
+ yaml_STREAM_END_TOKEN // A STREAM-END token.
+
+ yaml_VERSION_DIRECTIVE_TOKEN // A VERSION-DIRECTIVE token.
+ yaml_TAG_DIRECTIVE_TOKEN // A TAG-DIRECTIVE token.
+ yaml_DOCUMENT_START_TOKEN // A DOCUMENT-START token.
+ yaml_DOCUMENT_END_TOKEN // A DOCUMENT-END token.
+
+ yaml_BLOCK_SEQUENCE_START_TOKEN // A BLOCK-SEQUENCE-START token.
+ yaml_BLOCK_MAPPING_START_TOKEN // A BLOCK-SEQUENCE-END token.
+ yaml_BLOCK_END_TOKEN // A BLOCK-END token.
+
+ yaml_FLOW_SEQUENCE_START_TOKEN // A FLOW-SEQUENCE-START token.
+ yaml_FLOW_SEQUENCE_END_TOKEN // A FLOW-SEQUENCE-END token.
+ yaml_FLOW_MAPPING_START_TOKEN // A FLOW-MAPPING-START token.
+ yaml_FLOW_MAPPING_END_TOKEN // A FLOW-MAPPING-END token.
+
+ yaml_BLOCK_ENTRY_TOKEN // A BLOCK-ENTRY token.
+ yaml_FLOW_ENTRY_TOKEN // A FLOW-ENTRY token.
+ yaml_KEY_TOKEN // A KEY token.
+ yaml_VALUE_TOKEN // A VALUE token.
+
+ yaml_ALIAS_TOKEN // An ALIAS token.
+ yaml_ANCHOR_TOKEN // An ANCHOR token.
+ yaml_TAG_TOKEN // A TAG token.
+ yaml_SCALAR_TOKEN // A SCALAR token.
+)
+
+func (tt yaml_token_type_t) String() string {
+ switch tt {
+ case yaml_NO_TOKEN:
+ return "yaml_NO_TOKEN"
+ case yaml_STREAM_START_TOKEN:
+ return "yaml_STREAM_START_TOKEN"
+ case yaml_STREAM_END_TOKEN:
+ return "yaml_STREAM_END_TOKEN"
+ case yaml_VERSION_DIRECTIVE_TOKEN:
+ return "yaml_VERSION_DIRECTIVE_TOKEN"
+ case yaml_TAG_DIRECTIVE_TOKEN:
+ return "yaml_TAG_DIRECTIVE_TOKEN"
+ case yaml_DOCUMENT_START_TOKEN:
+ return "yaml_DOCUMENT_START_TOKEN"
+ case yaml_DOCUMENT_END_TOKEN:
+ return "yaml_DOCUMENT_END_TOKEN"
+ case yaml_BLOCK_SEQUENCE_START_TOKEN:
+ return "yaml_BLOCK_SEQUENCE_START_TOKEN"
+ case yaml_BLOCK_MAPPING_START_TOKEN:
+ return "yaml_BLOCK_MAPPING_START_TOKEN"
+ case yaml_BLOCK_END_TOKEN:
+ return "yaml_BLOCK_END_TOKEN"
+ case yaml_FLOW_SEQUENCE_START_TOKEN:
+ return "yaml_FLOW_SEQUENCE_START_TOKEN"
+ case yaml_FLOW_SEQUENCE_END_TOKEN:
+ return "yaml_FLOW_SEQUENCE_END_TOKEN"
+ case yaml_FLOW_MAPPING_START_TOKEN:
+ return "yaml_FLOW_MAPPING_START_TOKEN"
+ case yaml_FLOW_MAPPING_END_TOKEN:
+ return "yaml_FLOW_MAPPING_END_TOKEN"
+ case yaml_BLOCK_ENTRY_TOKEN:
+ return "yaml_BLOCK_ENTRY_TOKEN"
+ case yaml_FLOW_ENTRY_TOKEN:
+ return "yaml_FLOW_ENTRY_TOKEN"
+ case yaml_KEY_TOKEN:
+ return "yaml_KEY_TOKEN"
+ case yaml_VALUE_TOKEN:
+ return "yaml_VALUE_TOKEN"
+ case yaml_ALIAS_TOKEN:
+ return "yaml_ALIAS_TOKEN"
+ case yaml_ANCHOR_TOKEN:
+ return "yaml_ANCHOR_TOKEN"
+ case yaml_TAG_TOKEN:
+ return "yaml_TAG_TOKEN"
+ case yaml_SCALAR_TOKEN:
+ return "yaml_SCALAR_TOKEN"
+ }
+ return ""
+}
+
+// The token structure.
+type yaml_token_t struct {
+ // The token type.
+ typ yaml_token_type_t
+
+ // The start/end of the token.
+ start_mark, end_mark yaml_mark_t
+
+ // The stream encoding (for yaml_STREAM_START_TOKEN).
+ encoding yaml_encoding_t
+
+ // The alias/anchor/scalar value or tag/tag directive handle
+ // (for yaml_ALIAS_TOKEN, yaml_ANCHOR_TOKEN, yaml_SCALAR_TOKEN, yaml_TAG_TOKEN, yaml_TAG_DIRECTIVE_TOKEN).
+ value []byte
+
+ // The tag suffix (for yaml_TAG_TOKEN).
+ suffix []byte
+
+ // The tag directive prefix (for yaml_TAG_DIRECTIVE_TOKEN).
+ prefix []byte
+
+ // The scalar style (for yaml_SCALAR_TOKEN).
+ style yaml_scalar_style_t
+
+ // The version directive major/minor (for yaml_VERSION_DIRECTIVE_TOKEN).
+ major, minor int8
+}
+
+// Events
+
+type yaml_event_type_t int8
+
+// Event types.
+const (
+ // An empty event.
+ yaml_NO_EVENT yaml_event_type_t = iota
+
+ yaml_STREAM_START_EVENT // A STREAM-START event.
+ yaml_STREAM_END_EVENT // A STREAM-END event.
+ yaml_DOCUMENT_START_EVENT // A DOCUMENT-START event.
+ yaml_DOCUMENT_END_EVENT // A DOCUMENT-END event.
+ yaml_ALIAS_EVENT // An ALIAS event.
+ yaml_SCALAR_EVENT // A SCALAR event.
+ yaml_SEQUENCE_START_EVENT // A SEQUENCE-START event.
+ yaml_SEQUENCE_END_EVENT // A SEQUENCE-END event.
+ yaml_MAPPING_START_EVENT // A MAPPING-START event.
+ yaml_MAPPING_END_EVENT // A MAPPING-END event.
+ yaml_TAIL_COMMENT_EVENT
+)
+
+var eventStrings = []string{
+ yaml_NO_EVENT: "none",
+ yaml_STREAM_START_EVENT: "stream start",
+ yaml_STREAM_END_EVENT: "stream end",
+ yaml_DOCUMENT_START_EVENT: "document start",
+ yaml_DOCUMENT_END_EVENT: "document end",
+ yaml_ALIAS_EVENT: "alias",
+ yaml_SCALAR_EVENT: "scalar",
+ yaml_SEQUENCE_START_EVENT: "sequence start",
+ yaml_SEQUENCE_END_EVENT: "sequence end",
+ yaml_MAPPING_START_EVENT: "mapping start",
+ yaml_MAPPING_END_EVENT: "mapping end",
+ yaml_TAIL_COMMENT_EVENT: "tail comment",
+}
+
+func (e yaml_event_type_t) String() string {
+ if e < 0 || int(e) >= len(eventStrings) {
+ return fmt.Sprintf("unknown event %d", e)
+ }
+ return eventStrings[e]
+}
+
+// The event structure.
+type yaml_event_t struct {
+
+ // The event type.
+ typ yaml_event_type_t
+
+ // The start and end of the event.
+ start_mark, end_mark yaml_mark_t
+
+ // The document encoding (for yaml_STREAM_START_EVENT).
+ encoding yaml_encoding_t
+
+ // The version directive (for yaml_DOCUMENT_START_EVENT).
+ version_directive *yaml_version_directive_t
+
+ // The list of tag directives (for yaml_DOCUMENT_START_EVENT).
+ tag_directives []yaml_tag_directive_t
+
+ // The comments
+ head_comment []byte
+ line_comment []byte
+ foot_comment []byte
+ tail_comment []byte
+
+ // The anchor (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_ALIAS_EVENT).
+ anchor []byte
+
+ // The tag (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT).
+ tag []byte
+
+ // The scalar value (for yaml_SCALAR_EVENT).
+ value []byte
+
+ // Is the document start/end indicator implicit, or the tag optional?
+ // (for yaml_DOCUMENT_START_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_SCALAR_EVENT).
+ implicit bool
+
+ // Is the tag optional for any non-plain style? (for yaml_SCALAR_EVENT).
+ quoted_implicit bool
+
+ // The style (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT).
+ style yaml_style_t
+}
+
+func (e *yaml_event_t) scalar_style() yaml_scalar_style_t { return yaml_scalar_style_t(e.style) }
+func (e *yaml_event_t) sequence_style() yaml_sequence_style_t { return yaml_sequence_style_t(e.style) }
+func (e *yaml_event_t) mapping_style() yaml_mapping_style_t { return yaml_mapping_style_t(e.style) }
+
+// Nodes
+
+const (
+ yaml_NULL_TAG = "tag:yaml.org,2002:null" // The tag !!null with the only possible value: null.
+ yaml_BOOL_TAG = "tag:yaml.org,2002:bool" // The tag !!bool with the values: true and false.
+ yaml_STR_TAG = "tag:yaml.org,2002:str" // The tag !!str for string values.
+ yaml_INT_TAG = "tag:yaml.org,2002:int" // The tag !!int for integer values.
+ yaml_FLOAT_TAG = "tag:yaml.org,2002:float" // The tag !!float for float values.
+ yaml_TIMESTAMP_TAG = "tag:yaml.org,2002:timestamp" // The tag !!timestamp for date and time values.
+
+ yaml_SEQ_TAG = "tag:yaml.org,2002:seq" // The tag !!seq is used to denote sequences.
+ yaml_MAP_TAG = "tag:yaml.org,2002:map" // The tag !!map is used to denote mapping.
+
+ // Not in original libyaml.
+ yaml_BINARY_TAG = "tag:yaml.org,2002:binary"
+ yaml_MERGE_TAG = "tag:yaml.org,2002:merge"
+
+ yaml_DEFAULT_SCALAR_TAG = yaml_STR_TAG // The default scalar tag is !!str.
+ yaml_DEFAULT_SEQUENCE_TAG = yaml_SEQ_TAG // The default sequence tag is !!seq.
+ yaml_DEFAULT_MAPPING_TAG = yaml_MAP_TAG // The default mapping tag is !!map.
+)
+
+type yaml_node_type_t int
+
+// Node types.
+const (
+ // An empty node.
+ yaml_NO_NODE yaml_node_type_t = iota
+
+ yaml_SCALAR_NODE // A scalar node.
+ yaml_SEQUENCE_NODE // A sequence node.
+ yaml_MAPPING_NODE // A mapping node.
+)
+
+// An element of a sequence node.
+type yaml_node_item_t int
+
+// An element of a mapping node.
+type yaml_node_pair_t struct {
+ key int // The key of the element.
+ value int // The value of the element.
+}
+
+// The node structure.
+type yaml_node_t struct {
+ typ yaml_node_type_t // The node type.
+ tag []byte // The node tag.
+
+ // The node data.
+
+ // The scalar parameters (for yaml_SCALAR_NODE).
+ scalar struct {
+ value []byte // The scalar value.
+ length int // The length of the scalar value.
+ style yaml_scalar_style_t // The scalar style.
+ }
+
+ // The sequence parameters (for YAML_SEQUENCE_NODE).
+ sequence struct {
+ items_data []yaml_node_item_t // The stack of sequence items.
+ style yaml_sequence_style_t // The sequence style.
+ }
+
+ // The mapping parameters (for yaml_MAPPING_NODE).
+ mapping struct {
+ pairs_data []yaml_node_pair_t // The stack of mapping pairs (key, value).
+ pairs_start *yaml_node_pair_t // The beginning of the stack.
+ pairs_end *yaml_node_pair_t // The end of the stack.
+ pairs_top *yaml_node_pair_t // The top of the stack.
+ style yaml_mapping_style_t // The mapping style.
+ }
+
+ start_mark yaml_mark_t // The beginning of the node.
+ end_mark yaml_mark_t // The end of the node.
+
+}
+
+// The document structure.
+type yaml_document_t struct {
+
+ // The document nodes.
+ nodes []yaml_node_t
+
+ // The version directive.
+ version_directive *yaml_version_directive_t
+
+ // The list of tag directives.
+ tag_directives_data []yaml_tag_directive_t
+ tag_directives_start int // The beginning of the tag directives list.
+ tag_directives_end int // The end of the tag directives list.
+
+ start_implicit int // Is the document start indicator implicit?
+ end_implicit int // Is the document end indicator implicit?
+
+ // The start/end of the document.
+ start_mark, end_mark yaml_mark_t
+}
+
+// The prototype of a read handler.
+//
+// The read handler is called when the parser needs to read more bytes from the
+// source. The handler should write not more than size bytes to the buffer.
+// The number of written bytes should be set to the size_read variable.
+//
+// [in,out] data A pointer to an application data specified by
+//
+// yaml_parser_set_input().
+//
+// [out] buffer The buffer to write the data from the source.
+// [in] size The size of the buffer.
+// [out] size_read The actual number of bytes read from the source.
+//
+// On success, the handler should return 1. If the handler failed,
+// the returned value should be 0. On EOF, the handler should set the
+// size_read to 0 and return 1.
+type yaml_read_handler_t func(parser *yaml_parser_t, buffer []byte) (n int, err error)
+
+// This structure holds information about a potential simple key.
+type yaml_simple_key_t struct {
+ possible bool // Is a simple key possible?
+ required bool // Is a simple key required?
+ token_number int // The number of the token.
+ mark yaml_mark_t // The position mark.
+}
+
+// The states of the parser.
+type yaml_parser_state_t int
+
+const (
+ yaml_PARSE_STREAM_START_STATE yaml_parser_state_t = iota
+
+ yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE // Expect the beginning of an implicit document.
+ yaml_PARSE_DOCUMENT_START_STATE // Expect DOCUMENT-START.
+ yaml_PARSE_DOCUMENT_CONTENT_STATE // Expect the content of a document.
+ yaml_PARSE_DOCUMENT_END_STATE // Expect DOCUMENT-END.
+ yaml_PARSE_BLOCK_NODE_STATE // Expect a block node.
+ yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE // Expect a block node or indentless sequence.
+ yaml_PARSE_FLOW_NODE_STATE // Expect a flow node.
+ yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a block sequence.
+ yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE // Expect an entry of a block sequence.
+ yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE // Expect an entry of an indentless sequence.
+ yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping.
+ yaml_PARSE_BLOCK_MAPPING_KEY_STATE // Expect a block mapping key.
+ yaml_PARSE_BLOCK_MAPPING_VALUE_STATE // Expect a block mapping value.
+ yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a flow sequence.
+ yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE // Expect an entry of a flow sequence.
+ yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE // Expect a key of an ordered mapping.
+ yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE // Expect a value of an ordered mapping.
+ yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE // Expect the and of an ordered mapping entry.
+ yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping.
+ yaml_PARSE_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping.
+ yaml_PARSE_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping.
+ yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE // Expect an empty value of a flow mapping.
+ yaml_PARSE_END_STATE // Expect nothing.
+)
+
+func (ps yaml_parser_state_t) String() string {
+ switch ps {
+ case yaml_PARSE_STREAM_START_STATE:
+ return "yaml_PARSE_STREAM_START_STATE"
+ case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE:
+ return "yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE"
+ case yaml_PARSE_DOCUMENT_START_STATE:
+ return "yaml_PARSE_DOCUMENT_START_STATE"
+ case yaml_PARSE_DOCUMENT_CONTENT_STATE:
+ return "yaml_PARSE_DOCUMENT_CONTENT_STATE"
+ case yaml_PARSE_DOCUMENT_END_STATE:
+ return "yaml_PARSE_DOCUMENT_END_STATE"
+ case yaml_PARSE_BLOCK_NODE_STATE:
+ return "yaml_PARSE_BLOCK_NODE_STATE"
+ case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE:
+ return "yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE"
+ case yaml_PARSE_FLOW_NODE_STATE:
+ return "yaml_PARSE_FLOW_NODE_STATE"
+ case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE:
+ return "yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE"
+ case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE:
+ return "yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE"
+ case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE:
+ return "yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE"
+ case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE:
+ return "yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE"
+ case yaml_PARSE_BLOCK_MAPPING_KEY_STATE:
+ return "yaml_PARSE_BLOCK_MAPPING_KEY_STATE"
+ case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE:
+ return "yaml_PARSE_BLOCK_MAPPING_VALUE_STATE"
+ case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE:
+ return "yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE"
+ case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE:
+ return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE"
+ case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE:
+ return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE"
+ case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE:
+ return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE"
+ case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE:
+ return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE"
+ case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE:
+ return "yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE"
+ case yaml_PARSE_FLOW_MAPPING_KEY_STATE:
+ return "yaml_PARSE_FLOW_MAPPING_KEY_STATE"
+ case yaml_PARSE_FLOW_MAPPING_VALUE_STATE:
+ return "yaml_PARSE_FLOW_MAPPING_VALUE_STATE"
+ case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE:
+ return "yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE"
+ case yaml_PARSE_END_STATE:
+ return "yaml_PARSE_END_STATE"
+ }
+ return ""
+}
+
+// This structure holds aliases data.
+type yaml_alias_data_t struct {
+ anchor []byte // The anchor.
+ index int // The node id.
+ mark yaml_mark_t // The anchor mark.
+}
+
+// The parser structure.
+//
+// All members are internal. Manage the structure using the
+// yaml_parser_ family of functions.
+type yaml_parser_t struct {
+
+ // Error handling
+
+ error yaml_error_type_t // Error type.
+
+ problem string // Error description.
+
+ // The byte about which the problem occurred.
+ problem_offset int
+ problem_value int
+ problem_mark yaml_mark_t
+
+ // The error context.
+ context string
+ context_mark yaml_mark_t
+
+ // Reader stuff
+
+ read_handler yaml_read_handler_t // Read handler.
+
+ input_reader io.Reader // File input data.
+ input []byte // String input data.
+ input_pos int
+
+ eof bool // EOF flag
+
+ buffer []byte // The working buffer.
+ buffer_pos int // The current position of the buffer.
+
+ unread int // The number of unread characters in the buffer.
+
+ newlines int // The number of line breaks since last non-break/non-blank character
+
+ raw_buffer []byte // The raw buffer.
+ raw_buffer_pos int // The current position of the buffer.
+
+ encoding yaml_encoding_t // The input encoding.
+
+ offset int // The offset of the current position (in bytes).
+ mark yaml_mark_t // The mark of the current position.
+
+ // Comments
+
+ head_comment []byte // The current head comments
+ line_comment []byte // The current line comments
+ foot_comment []byte // The current foot comments
+ tail_comment []byte // Foot comment that happens at the end of a block.
+ stem_comment []byte // Comment in item preceding a nested structure (list inside list item, etc)
+
+ comments []yaml_comment_t // The folded comments for all parsed tokens
+ comments_head int
+
+ // Scanner stuff
+
+ stream_start_produced bool // Have we started to scan the input stream?
+ stream_end_produced bool // Have we reached the end of the input stream?
+
+ flow_level int // The number of unclosed '[' and '{' indicators.
+
+ tokens []yaml_token_t // The tokens queue.
+ tokens_head int // The head of the tokens queue.
+ tokens_parsed int // The number of tokens fetched from the queue.
+ token_available bool // Does the tokens queue contain a token ready for dequeueing.
+
+ indent int // The current indentation level.
+ indents []int // The indentation levels stack.
+
+ simple_key_allowed bool // May a simple key occur at the current position?
+ simple_keys []yaml_simple_key_t // The stack of simple keys.
+ simple_keys_by_tok map[int]int // possible simple_key indexes indexed by token_number
+
+ // Parser stuff
+
+ state yaml_parser_state_t // The current parser state.
+ states []yaml_parser_state_t // The parser states stack.
+ marks []yaml_mark_t // The stack of marks.
+ tag_directives []yaml_tag_directive_t // The list of TAG directives.
+
+ // Dumper stuff
+
+ aliases []yaml_alias_data_t // The alias data.
+
+ document *yaml_document_t // The currently parsed document.
+}
+
+type yaml_comment_t struct {
+ scan_mark yaml_mark_t // Position where scanning for comments started
+ token_mark yaml_mark_t // Position after which tokens will be associated with this comment
+ start_mark yaml_mark_t // Position of '#' comment mark
+ end_mark yaml_mark_t // Position where comment terminated
+
+ head []byte
+ line []byte
+ foot []byte
+}
+
+// Emitter Definitions
+
+// The prototype of a write handler.
+//
+// The write handler is called when the emitter needs to flush the accumulated
+// characters to the output. The handler should write @a size bytes of the
+// @a buffer to the output.
+//
+// @param[in,out] data A pointer to an application data specified by
+//
+// yaml_emitter_set_output().
+//
+// @param[in] buffer The buffer with bytes to be written.
+// @param[in] size The size of the buffer.
+//
+// @returns On success, the handler should return @c 1. If the handler failed,
+// the returned value should be @c 0.
+type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error
+
+type yaml_emitter_state_t int
+
+// The emitter states.
+const (
+ // Expect STREAM-START.
+ yaml_EMIT_STREAM_START_STATE yaml_emitter_state_t = iota
+
+ yaml_EMIT_FIRST_DOCUMENT_START_STATE // Expect the first DOCUMENT-START or STREAM-END.
+ yaml_EMIT_DOCUMENT_START_STATE // Expect DOCUMENT-START or STREAM-END.
+ yaml_EMIT_DOCUMENT_CONTENT_STATE // Expect the content of a document.
+ yaml_EMIT_DOCUMENT_END_STATE // Expect DOCUMENT-END.
+ yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a flow sequence.
+ yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE // Expect the next item of a flow sequence, with the comma already written out
+ yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE // Expect an item of a flow sequence.
+ yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping.
+ yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE // Expect the next key of a flow mapping, with the comma already written out
+ yaml_EMIT_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping.
+ yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a flow mapping.
+ yaml_EMIT_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping.
+ yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a block sequence.
+ yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE // Expect an item of a block sequence.
+ yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping.
+ yaml_EMIT_BLOCK_MAPPING_KEY_STATE // Expect the key of a block mapping.
+ yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a block mapping.
+ yaml_EMIT_BLOCK_MAPPING_VALUE_STATE // Expect a value of a block mapping.
+ yaml_EMIT_END_STATE // Expect nothing.
+)
+
+// The emitter structure.
+//
+// All members are internal. Manage the structure using the @c yaml_emitter_
+// family of functions.
+type yaml_emitter_t struct {
+
+ // Error handling
+
+ error yaml_error_type_t // Error type.
+ problem string // Error description.
+
+ // Writer stuff
+
+ write_handler yaml_write_handler_t // Write handler.
+
+ output_buffer *[]byte // String output data.
+ output_writer io.Writer // File output data.
+
+ buffer []byte // The working buffer.
+ buffer_pos int // The current position of the buffer.
+
+ raw_buffer []byte // The raw buffer.
+ raw_buffer_pos int // The current position of the buffer.
+
+ encoding yaml_encoding_t // The stream encoding.
+
+ // Emitter stuff
+
+ canonical bool // If the output is in the canonical style?
+ best_indent int // The number of indentation spaces.
+ best_width int // The preferred width of the output lines.
+ unicode bool // Allow unescaped non-ASCII characters?
+ line_break yaml_break_t // The preferred line break.
+
+ state yaml_emitter_state_t // The current emitter state.
+ states []yaml_emitter_state_t // The stack of states.
+
+ events []yaml_event_t // The event queue.
+ events_head int // The head of the event queue.
+
+ indents []int // The stack of indentation levels.
+
+ tag_directives []yaml_tag_directive_t // The list of tag directives.
+
+ indent int // The current indentation level.
+
+ compact_sequence_indent bool // Is '- ' is considered part of the indentation for sequence elements?
+
+ flow_level int // The current flow level.
+
+ root_context bool // Is it the document root context?
+ sequence_context bool // Is it a sequence context?
+ mapping_context bool // Is it a mapping context?
+ simple_key_context bool // Is it a simple mapping key context?
+
+ line int // The current line.
+ column int // The current column.
+ whitespace bool // If the last character was a whitespace?
+ indention bool // If the last character was an indentation character (' ', '-', '?', ':')?
+ open_ended bool // If an explicit document end is required?
+
+ space_above bool // Is there's an empty line above?
+ foot_indent int // The indent used to write the foot comment above, or -1 if none.
+
+ // Anchor analysis.
+ anchor_data struct {
+ anchor []byte // The anchor value.
+ alias bool // Is it an alias?
+ }
+
+ // Tag analysis.
+ tag_data struct {
+ handle []byte // The tag handle.
+ suffix []byte // The tag suffix.
+ }
+
+ // Scalar analysis.
+ scalar_data struct {
+ value []byte // The scalar value.
+ multiline bool // Does the scalar contain line breaks?
+ flow_plain_allowed bool // Can the scalar be expessed in the flow plain style?
+ block_plain_allowed bool // Can the scalar be expressed in the block plain style?
+ single_quoted_allowed bool // Can the scalar be expressed in the single quoted style?
+ block_allowed bool // Can the scalar be expressed in the literal or folded styles?
+ style yaml_scalar_style_t // The output style.
+ }
+
+ // Comments
+ head_comment []byte
+ line_comment []byte
+ foot_comment []byte
+ tail_comment []byte
+
+ key_line_comment []byte
+
+ // Dumper stuff
+
+ opened bool // If the stream was already opened?
+ closed bool // If the stream was already closed?
+
+ // The information associated with the document nodes.
+ anchors *struct {
+ references int // The number of references.
+ anchor int // The anchor id.
+ serialized bool // If the node has been emitted?
+ }
+
+ last_anchor_id int // The last assigned anchor id.
+
+ document *yaml_document_t // The currently emitted document.
+}
diff --git a/vendor/go.yaml.in/yaml/v3/yamlprivateh.go b/vendor/go.yaml.in/yaml/v3/yamlprivateh.go
new file mode 100644
index 000000000..dea1ba961
--- /dev/null
+++ b/vendor/go.yaml.in/yaml/v3/yamlprivateh.go
@@ -0,0 +1,198 @@
+//
+// Copyright (c) 2011-2019 Canonical Ltd
+// Copyright (c) 2006-2010 Kirill Simonov
+//
+// 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.
+
+package yaml
+
+const (
+ // The size of the input raw buffer.
+ input_raw_buffer_size = 512
+
+ // The size of the input buffer.
+ // It should be possible to decode the whole raw buffer.
+ input_buffer_size = input_raw_buffer_size * 3
+
+ // The size of the output buffer.
+ output_buffer_size = 128
+
+ // The size of the output raw buffer.
+ // It should be possible to encode the whole output buffer.
+ output_raw_buffer_size = (output_buffer_size*2 + 2)
+
+ // The size of other stacks and queues.
+ initial_stack_size = 16
+ initial_queue_size = 16
+ initial_string_size = 16
+)
+
+// Check if the character at the specified position is an alphabetical
+// character, a digit, '_', or '-'.
+func is_alpha(b []byte, i int) bool {
+ return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-'
+}
+
+// Check if the character at the specified position is a digit.
+func is_digit(b []byte, i int) bool {
+ return b[i] >= '0' && b[i] <= '9'
+}
+
+// Get the value of a digit.
+func as_digit(b []byte, i int) int {
+ return int(b[i]) - '0'
+}
+
+// Check if the character at the specified position is a hex-digit.
+func is_hex(b []byte, i int) bool {
+ return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f'
+}
+
+// Get the value of a hex-digit.
+func as_hex(b []byte, i int) int {
+ bi := b[i]
+ if bi >= 'A' && bi <= 'F' {
+ return int(bi) - 'A' + 10
+ }
+ if bi >= 'a' && bi <= 'f' {
+ return int(bi) - 'a' + 10
+ }
+ return int(bi) - '0'
+}
+
+// Check if the character is ASCII.
+func is_ascii(b []byte, i int) bool {
+ return b[i] <= 0x7F
+}
+
+// Check if the character at the start of the buffer can be printed unescaped.
+func is_printable(b []byte, i int) bool {
+ return ((b[i] == 0x0A) || // . == #x0A
+ (b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E
+ (b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF
+ (b[i] > 0xC2 && b[i] < 0xED) ||
+ (b[i] == 0xED && b[i+1] < 0xA0) ||
+ (b[i] == 0xEE) ||
+ (b[i] == 0xEF && // #xE000 <= . <= #xFFFD
+ !(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF
+ !(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF))))
+}
+
+// Check if the character at the specified position is NUL.
+func is_z(b []byte, i int) bool {
+ return b[i] == 0x00
+}
+
+// Check if the beginning of the buffer is a BOM.
+func is_bom(b []byte, i int) bool {
+ return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF
+}
+
+// Check if the character at the specified position is space.
+func is_space(b []byte, i int) bool {
+ return b[i] == ' '
+}
+
+// Check if the character at the specified position is tab.
+func is_tab(b []byte, i int) bool {
+ return b[i] == '\t'
+}
+
+// Check if the character at the specified position is blank (space or tab).
+func is_blank(b []byte, i int) bool {
+ //return is_space(b, i) || is_tab(b, i)
+ return b[i] == ' ' || b[i] == '\t'
+}
+
+// Check if the character at the specified position is a line break.
+func is_break(b []byte, i int) bool {
+ return (b[i] == '\r' || // CR (#xD)
+ b[i] == '\n' || // LF (#xA)
+ b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85)
+ b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028)
+ b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029)
+}
+
+func is_crlf(b []byte, i int) bool {
+ return b[i] == '\r' && b[i+1] == '\n'
+}
+
+// Check if the character is a line break or NUL.
+func is_breakz(b []byte, i int) bool {
+ //return is_break(b, i) || is_z(b, i)
+ return (
+ // is_break:
+ b[i] == '\r' || // CR (#xD)
+ b[i] == '\n' || // LF (#xA)
+ b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85)
+ b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028)
+ b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029)
+ // is_z:
+ b[i] == 0)
+}
+
+// Check if the character is a line break, space, or NUL.
+func is_spacez(b []byte, i int) bool {
+ //return is_space(b, i) || is_breakz(b, i)
+ return (
+ // is_space:
+ b[i] == ' ' ||
+ // is_breakz:
+ b[i] == '\r' || // CR (#xD)
+ b[i] == '\n' || // LF (#xA)
+ b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85)
+ b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028)
+ b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029)
+ b[i] == 0)
+}
+
+// Check if the character is a line break, space, tab, or NUL.
+func is_blankz(b []byte, i int) bool {
+ //return is_blank(b, i) || is_breakz(b, i)
+ return (
+ // is_blank:
+ b[i] == ' ' || b[i] == '\t' ||
+ // is_breakz:
+ b[i] == '\r' || // CR (#xD)
+ b[i] == '\n' || // LF (#xA)
+ b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85)
+ b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028)
+ b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029)
+ b[i] == 0)
+}
+
+// Determine the width of the character.
+func width(b byte) int {
+ // Don't replace these by a switch without first
+ // confirming that it is being inlined.
+ if b&0x80 == 0x00 {
+ return 1
+ }
+ if b&0xE0 == 0xC0 {
+ return 2
+ }
+ if b&0xF0 == 0xE0 {
+ return 3
+ }
+ if b&0xF8 == 0xF0 {
+ return 4
+ }
+ return 0
+
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index eab2cf96a..16f13a0b2 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -54,7 +54,7 @@ github.com/dustin/go-humanize
# github.com/felixge/httpsnoop v1.0.4
## explicit; go 1.13
github.com/felixge/httpsnoop
-# github.com/fsnotify/fsnotify v1.8.0
+# github.com/fsnotify/fsnotify v1.9.0
## explicit; go 1.17
github.com/fsnotify/fsnotify
github.com/fsnotify/fsnotify/internal
@@ -65,7 +65,7 @@ github.com/go-graphite/gocairo/cairo
## explicit; go 1.16
github.com/go-graphite/protocol/carbonapi_v2_pb
github.com/go-graphite/protocol/carbonapi_v3_pb
-# github.com/go-viper/mapstructure/v2 v2.2.1
+# github.com/go-viper/mapstructure/v2 v2.4.0
## explicit; go 1.18
github.com/go-viper/mapstructure/v2
github.com/go-viper/mapstructure/v2/internal/errors
@@ -121,7 +121,7 @@ github.com/natefinch/atomic
# github.com/openhistogram/circonusllhist v0.4.0
## explicit; go 1.16
github.com/openhistogram/circonusllhist
-# github.com/pelletier/go-toml/v2 v2.2.3
+# github.com/pelletier/go-toml/v2 v2.2.4
## explicit; go 1.21.0
github.com/pelletier/go-toml/v2
github.com/pelletier/go-toml/v2/internal/characters
@@ -137,38 +137,38 @@ github.com/pmezard/go-difflib/difflib
# github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529
## explicit; go 1.12
github.com/rs/dnscache
-# github.com/sagikazarmark/locafero v0.7.0
-## explicit; go 1.21
+# github.com/sagikazarmark/locafero v0.11.0
+## explicit; go 1.23.0
github.com/sagikazarmark/locafero
# github.com/satori/go.uuid v1.2.0
## explicit
github.com/satori/go.uuid
-# github.com/sourcegraph/conc v0.3.0
-## explicit; go 1.19
+# github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8
+## explicit; go 1.20
github.com/sourcegraph/conc
-github.com/sourcegraph/conc/internal/multierror
-github.com/sourcegraph/conc/iter
github.com/sourcegraph/conc/panics
-# github.com/spf13/afero v1.12.0
-## explicit; go 1.21
+github.com/sourcegraph/conc/pool
+# github.com/spf13/afero v1.15.0
+## explicit; go 1.23.0
github.com/spf13/afero
github.com/spf13/afero/internal/common
github.com/spf13/afero/mem
-# github.com/spf13/cast v1.7.1
-## explicit; go 1.19
+# github.com/spf13/cast v1.10.0
+## explicit; go 1.21.0
github.com/spf13/cast
-# github.com/spf13/pflag v1.0.6
+github.com/spf13/cast/internal
+# github.com/spf13/pflag v1.0.10
## explicit; go 1.12
github.com/spf13/pflag
-# github.com/spf13/viper v1.20.1
-## explicit; go 1.21.0
+# github.com/spf13/viper v1.21.0
+## explicit; go 1.23.0
github.com/spf13/viper
github.com/spf13/viper/internal/encoding/dotenv
github.com/spf13/viper/internal/encoding/json
github.com/spf13/viper/internal/encoding/toml
github.com/spf13/viper/internal/encoding/yaml
github.com/spf13/viper/internal/features
-# github.com/stretchr/testify v1.10.0
+# github.com/stretchr/testify v1.11.1
## explicit; go 1.17
github.com/stretchr/testify/assert
github.com/stretchr/testify/assert/yaml
@@ -211,17 +211,20 @@ go.uber.org/zap/internal/exit
go.uber.org/zap/internal/pool
go.uber.org/zap/internal/stacktrace
go.uber.org/zap/zapcore
+# go.yaml.in/yaml/v3 v3.0.4
+## explicit; go 1.16
+go.yaml.in/yaml/v3
# golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f
## explicit; go 1.20
-# golang.org/x/sync v0.10.0
-## explicit; go 1.18
+# golang.org/x/sync v0.16.0
+## explicit; go 1.23.0
golang.org/x/sync/singleflight
# golang.org/x/sys v0.29.0
## explicit; go 1.18
golang.org/x/sys/unix
golang.org/x/sys/windows
-# golang.org/x/text v0.21.0
-## explicit; go 1.18
+# golang.org/x/text v0.28.0
+## explicit; go 1.23.0
golang.org/x/text/encoding
golang.org/x/text/encoding/internal
golang.org/x/text/encoding/internal/identifier