vendor: update hcl dependencies
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
This commit is contained in:
@@ -24,9 +24,9 @@ require (
|
|||||||
github.com/google/go-dap v0.12.0
|
github.com/google/go-dap v0.12.0
|
||||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/hashicorp/go-cty-funcs v0.0.0-20250210171435-dda779884a9f
|
github.com/hashicorp/go-cty-funcs v0.0.0-20250818135842-6aab67130928
|
||||||
github.com/hashicorp/go-multierror v1.1.1
|
github.com/hashicorp/go-multierror v1.1.1
|
||||||
github.com/hashicorp/hcl/v2 v2.23.0
|
github.com/hashicorp/hcl/v2 v2.24.0
|
||||||
github.com/in-toto/in-toto-golang v0.9.0
|
github.com/in-toto/in-toto-golang v0.9.0
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||||
github.com/moby/buildkit v0.24.0
|
github.com/moby/buildkit v0.24.0
|
||||||
@@ -48,7 +48,7 @@ require (
|
|||||||
github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f
|
github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f
|
||||||
github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0
|
github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0
|
||||||
github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250408171107-3dd17559e117
|
github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250408171107-3dd17559e117
|
||||||
github.com/zclconf/go-cty v1.16.2
|
github.com/zclconf/go-cty v1.17.0
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0
|
||||||
go.opentelemetry.io/otel v1.35.0
|
go.opentelemetry.io/otel v1.35.0
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0
|
||||||
@@ -59,8 +59,8 @@ require (
|
|||||||
golang.org/x/mod v0.24.0
|
golang.org/x/mod v0.24.0
|
||||||
golang.org/x/sync v0.16.0
|
golang.org/x/sync v0.16.0
|
||||||
golang.org/x/sys v0.33.0
|
golang.org/x/sys v0.33.0
|
||||||
golang.org/x/term v0.31.0
|
golang.org/x/term v0.32.0
|
||||||
golang.org/x/text v0.24.0
|
golang.org/x/text v0.25.0
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a
|
||||||
google.golang.org/grpc v1.72.2
|
google.golang.org/grpc v1.72.2
|
||||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1
|
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1
|
||||||
@@ -122,7 +122,7 @@ require (
|
|||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||||
github.com/mattn/go-shellwords v1.0.12 // indirect
|
github.com/mattn/go-shellwords v1.0.12 // indirect
|
||||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
|
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||||
github.com/moby/locker v1.0.1 // indirect
|
github.com/moby/locker v1.0.1 // indirect
|
||||||
github.com/moby/patternmatcher v0.6.0 // indirect
|
github.com/moby/patternmatcher v0.6.0 // indirect
|
||||||
@@ -158,7 +158,7 @@ require (
|
|||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 // indirect
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
|
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
|
||||||
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
||||||
golang.org/x/crypto v0.37.0 // indirect
|
golang.org/x/crypto v0.38.0 // indirect
|
||||||
golang.org/x/net v0.39.0 // indirect
|
golang.org/x/net v0.39.0 // indirect
|
||||||
golang.org/x/oauth2 v0.29.0 // indirect
|
golang.org/x/oauth2 v0.29.0 // indirect
|
||||||
golang.org/x/time v0.11.0 // indirect
|
golang.org/x/time v0.11.0 // indirect
|
||||||
|
|||||||
@@ -203,12 +203,12 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY
|
|||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||||
github.com/hashicorp/go-cty-funcs v0.0.0-20250210171435-dda779884a9f h1:c+rwTuPxlEHiskGDCl53snuw344sDO/4KU5KSoWhieI=
|
github.com/hashicorp/go-cty-funcs v0.0.0-20250818135842-6aab67130928 h1:NFpfJOqEV8DUhomtXx9A0FfcMg7OCYRugdyw6Ar/Y7s=
|
||||||
github.com/hashicorp/go-cty-funcs v0.0.0-20250210171435-dda779884a9f/go.mod h1:0AzBnolcBFa/tA5iF378GgeeiZ955EdVdC4TuxJ66m4=
|
github.com/hashicorp/go-cty-funcs v0.0.0-20250818135842-6aab67130928/go.mod h1:YC9ASYt9Z9sQEAtzCe+yaAzi3E7wcxfRphDXtwZoWC0=
|
||||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
github.com/hashicorp/hcl/v2 v2.23.0 h1:Fphj1/gCylPxHutVSEOf2fBOh1VE4AuLV7+kbJf3qos=
|
github.com/hashicorp/hcl/v2 v2.24.0 h1:2QJdZ454DSsYGoaE6QheQZjtKZSUs9Nh2izTWiwQxvE=
|
||||||
github.com/hashicorp/hcl/v2 v2.23.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
|
github.com/hashicorp/hcl/v2 v2.24.0/go.mod h1:oGoO1FIQYfn/AgyOhlg9qLC6/nOJPX3qGbkZpYAcqfM=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU=
|
github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU=
|
||||||
github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo=
|
github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo=
|
||||||
@@ -250,8 +250,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
|
|||||||
github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||||
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
|
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
|
||||||
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
|
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
@@ -403,8 +403,8 @@ github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8
|
|||||||
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
|
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/zclconf/go-cty v1.16.2 h1:LAJSwc3v81IRBZyUVQDUdZ7hs3SYs9jv0eZJDWHD/70=
|
github.com/zclconf/go-cty v1.17.0 h1:seZvECve6XX4tmnvRzWtJNHdscMtYEx5R7bnnVyd/d0=
|
||||||
github.com/zclconf/go-cty v1.16.2/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
|
github.com/zclconf/go-cty v1.17.0/go.mod h1:wqFzcImaLTI6A5HfsRwB0nj5n0MRZFwmey8YoFPPs3U=
|
||||||
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
|
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
|
||||||
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
|
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
|
||||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||||
@@ -451,8 +451,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
|||||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||||
@@ -485,12 +485,12 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
|
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||||
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
|||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
1.23
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
# Copyright (c) HashiCorp, Inc.
|
||||||
|
# SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
version: "2"
|
||||||
|
issues:
|
||||||
|
max-issues-per-linter: 0 # show all issues found by each linter
|
||||||
|
max-same-issues: 0 # don't ignore same issues
|
||||||
|
linters:
|
||||||
|
exclusions:
|
||||||
|
rules:
|
||||||
|
- path: hclsyntax/scan_string_lit.go # generated file, ignore errors
|
||||||
|
linters:
|
||||||
|
- unused
|
||||||
|
- staticcheck
|
||||||
|
- path: hclsyntax/scan_tokens.go # generated file, ignore errors
|
||||||
|
linters:
|
||||||
|
- unused
|
||||||
|
- staticcheck
|
||||||
+18
@@ -1,5 +1,23 @@
|
|||||||
# HCL Changelog
|
# HCL Changelog
|
||||||
|
|
||||||
|
## v2.24.0 (July 7, 2025)
|
||||||
|
|
||||||
|
### Enhancements
|
||||||
|
|
||||||
|
* Add support for decoding block and attribute source ranges when using `gohcl`. ([#703](https://github.com/hashicorp/hcl/pull/703))
|
||||||
|
* hclsyntax: Detect and reject invalid nested splat result. ([#724](https://github.com/hashicorp/hcl/pull/724))
|
||||||
|
|
||||||
|
### Bugs Fixed
|
||||||
|
|
||||||
|
* Correct handling of unknown objects in Index function. ([#763](https://github.com/hashicorp/hcl/pull/763))
|
||||||
|
|
||||||
|
## v2.23.0 (November 15, 2024)
|
||||||
|
|
||||||
|
### Bugs Fixed
|
||||||
|
|
||||||
|
* Preserve marks when traversing through unknown values. ([#699](https://github.com/hashicorp/hcl/pull/699))
|
||||||
|
* Retain marks through conditional and for expressions. ([#710](https://github.com/hashicorp/hcl/pull/710))
|
||||||
|
|
||||||
## v2.22.0 (August 26, 2024)
|
## v2.22.0 (August 26, 2024)
|
||||||
|
|
||||||
### Enhancements
|
### Enhancements
|
||||||
|
|||||||
+4
-4
@@ -106,10 +106,10 @@ func (d *Diagnostic) Error() string {
|
|||||||
// APIs that normally deal in vanilla Go errors.
|
// APIs that normally deal in vanilla Go errors.
|
||||||
func (d Diagnostics) Error() string {
|
func (d Diagnostics) Error() string {
|
||||||
count := len(d)
|
count := len(d)
|
||||||
switch {
|
switch count {
|
||||||
case count == 0:
|
case 0:
|
||||||
return "no diagnostics"
|
return "no diagnostics"
|
||||||
case count == 1:
|
case 1:
|
||||||
return d[0].Error()
|
return d[0].Error()
|
||||||
default:
|
default:
|
||||||
return fmt.Sprintf("%s, and %d other diagnostic(s)", d[0].Error(), count-1)
|
return fmt.Sprintf("%s, and %d other diagnostic(s)", d[0].Error(), count-1)
|
||||||
@@ -121,7 +121,7 @@ func (d Diagnostics) Error() string {
|
|||||||
// This is provided as a convenience for returning from a function that
|
// This is provided as a convenience for returning from a function that
|
||||||
// collects and then returns a set of diagnostics:
|
// collects and then returns a set of diagnostics:
|
||||||
//
|
//
|
||||||
// return nil, diags.Append(&hcl.Diagnostic{ ... })
|
// return nil, diags.Append(&hcl.Diagnostic{ ... })
|
||||||
//
|
//
|
||||||
// Note that this modifies the array underlying the diagnostics slice, so
|
// Note that this modifies the array underlying the diagnostics slice, so
|
||||||
// must be used carefully within a single codepath. It is incorrect (and rude)
|
// must be used carefully within a single codepath. It is incorrect (and rude)
|
||||||
|
|||||||
+43
-12
@@ -71,7 +71,10 @@ func (w *diagnosticTextWriter) WriteDiagnostic(diag *Diagnostic) error {
|
|||||||
severityStr = "???????"
|
severityStr = "???????"
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(w.wr, "%s%s%s: %s\n\n", colorCode, severityStr, resetCode, diag.Summary)
|
_, err := fmt.Fprintf(w.wr, "%s%s%s: %s\n\n", colorCode, severityStr, resetCode, diag.Summary)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("write failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
if diag.Subject != nil {
|
if diag.Subject != nil {
|
||||||
snipRange := *diag.Subject
|
snipRange := *diag.Subject
|
||||||
@@ -97,7 +100,10 @@ func (w *diagnosticTextWriter) WriteDiagnostic(diag *Diagnostic) error {
|
|||||||
|
|
||||||
file := w.files[diag.Subject.Filename]
|
file := w.files[diag.Subject.Filename]
|
||||||
if file == nil || file.Bytes == nil {
|
if file == nil || file.Bytes == nil {
|
||||||
fmt.Fprintf(w.wr, " on %s line %d:\n (source code not available)\n\n", diag.Subject.Filename, diag.Subject.Start.Line)
|
_, err = fmt.Fprintf(w.wr, " on %s line %d:\n (source code not available)\n\n", diag.Subject.Filename, diag.Subject.Start.Line)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("write failed: %w", err)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
var contextLine string
|
var contextLine string
|
||||||
@@ -108,7 +114,10 @@ func (w *diagnosticTextWriter) WriteDiagnostic(diag *Diagnostic) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(w.wr, " on %s line %d%s:\n", diag.Subject.Filename, diag.Subject.Start.Line, contextLine)
|
_, err = fmt.Fprintf(w.wr, " on %s line %d%s:\n", diag.Subject.Filename, diag.Subject.Start.Line, contextLine)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("write failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
src := file.Bytes
|
src := file.Bytes
|
||||||
sc := NewRangeScanner(src, diag.Subject.Filename, bufio.ScanLines)
|
sc := NewRangeScanner(src, diag.Subject.Filename, bufio.ScanLines)
|
||||||
@@ -121,23 +130,32 @@ func (w *diagnosticTextWriter) WriteDiagnostic(diag *Diagnostic) error {
|
|||||||
|
|
||||||
beforeRange, highlightedRange, afterRange := lineRange.PartitionAround(highlightRange)
|
beforeRange, highlightedRange, afterRange := lineRange.PartitionAround(highlightRange)
|
||||||
if highlightedRange.Empty() {
|
if highlightedRange.Empty() {
|
||||||
fmt.Fprintf(w.wr, "%4d: %s\n", lineRange.Start.Line, sc.Bytes())
|
_, err = fmt.Fprintf(w.wr, "%4d: %s\n", lineRange.Start.Line, sc.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("write failed: %w", err)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
before := beforeRange.SliceBytes(src)
|
before := beforeRange.SliceBytes(src)
|
||||||
highlighted := highlightedRange.SliceBytes(src)
|
highlighted := highlightedRange.SliceBytes(src)
|
||||||
after := afterRange.SliceBytes(src)
|
after := afterRange.SliceBytes(src)
|
||||||
fmt.Fprintf(
|
_, err = fmt.Fprintf(
|
||||||
w.wr, "%4d: %s%s%s%s%s\n",
|
w.wr, "%4d: %s%s%s%s%s\n",
|
||||||
lineRange.Start.Line,
|
lineRange.Start.Line,
|
||||||
before,
|
before,
|
||||||
highlightCode, highlighted, resetCode,
|
highlightCode, highlighted, resetCode,
|
||||||
after,
|
after,
|
||||||
)
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("write failed: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
w.wr.Write([]byte{'\n'})
|
_, err = w.wr.Write([]byte{'\n'})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("write failed: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if diag.Expression != nil && diag.EvalContext != nil {
|
if diag.Expression != nil && diag.EvalContext != nil {
|
||||||
@@ -182,16 +200,26 @@ func (w *diagnosticTextWriter) WriteDiagnostic(diag *Diagnostic) error {
|
|||||||
for i, stmt := range stmts {
|
for i, stmt := range stmts {
|
||||||
switch i {
|
switch i {
|
||||||
case 0:
|
case 0:
|
||||||
w.wr.Write([]byte{'w', 'i', 't', 'h', ' '})
|
_, err = w.wr.Write([]byte{'w', 'i', 't', 'h', ' '})
|
||||||
default:
|
default:
|
||||||
w.wr.Write([]byte{' ', ' ', ' ', ' ', ' '})
|
_, err = w.wr.Write([]byte{' ', ' ', ' ', ' ', ' '})
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("write failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = w.wr.Write([]byte(stmt))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("write failed: %w", err)
|
||||||
}
|
}
|
||||||
w.wr.Write([]byte(stmt))
|
|
||||||
switch i {
|
switch i {
|
||||||
case last:
|
case last:
|
||||||
w.wr.Write([]byte{'.', '\n', '\n'})
|
_, err = w.wr.Write([]byte{'.', '\n', '\n'})
|
||||||
default:
|
default:
|
||||||
w.wr.Write([]byte{',', '\n'})
|
_, err = w.wr.Write([]byte{',', '\n'})
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("write failed: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -202,7 +230,10 @@ func (w *diagnosticTextWriter) WriteDiagnostic(diag *Diagnostic) error {
|
|||||||
if w.width != 0 {
|
if w.width != 0 {
|
||||||
detail = wordwrap.WrapString(detail, w.width)
|
detail = wordwrap.WrapString(detail, w.width)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w.wr, "%s\n\n", detail)
|
_, err = fmt.Fprintf(w.wr, "%s\n\n", detail)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("write failed: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
+16
-16
@@ -9,25 +9,25 @@
|
|||||||
// configurations in either native HCL syntax or JSON syntax into a Go struct
|
// configurations in either native HCL syntax or JSON syntax into a Go struct
|
||||||
// type:
|
// type:
|
||||||
//
|
//
|
||||||
// package main
|
// package main
|
||||||
//
|
//
|
||||||
// import (
|
// import (
|
||||||
// "log"
|
// "log"
|
||||||
// "github.com/hashicorp/hcl/v2/hclsimple"
|
// "github.com/hashicorp/hcl/v2/hclsimple"
|
||||||
// )
|
// )
|
||||||
//
|
//
|
||||||
// type Config struct {
|
// type Config struct {
|
||||||
// LogLevel string `hcl:"log_level"`
|
// LogLevel string `hcl:"log_level"`
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// func main() {
|
// func main() {
|
||||||
// var config Config
|
// var config Config
|
||||||
// err := hclsimple.DecodeFile("config.hcl", nil, &config)
|
// err := hclsimple.DecodeFile("config.hcl", nil, &config)
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// log.Fatalf("Failed to load configuration: %s", err)
|
// log.Fatalf("Failed to load configuration: %s", err)
|
||||||
// }
|
// }
|
||||||
// log.Printf("Configuration is %#v", config)
|
// log.Printf("Configuration is %#v", config)
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// If your application needs more control over the evaluation of the
|
// If your application needs more control over the evaluation of the
|
||||||
// configuration, you can use the functions in the subdirectories hclparse,
|
// configuration, you can use the functions in the subdirectories hclparse,
|
||||||
|
|||||||
+3
-3
@@ -8,7 +8,7 @@
|
|||||||
// use any of the usual language operators. This is similar to type expressions
|
// use any of the usual language operators. This is similar to type expressions
|
||||||
// in statically-typed programming languages.
|
// in statically-typed programming languages.
|
||||||
//
|
//
|
||||||
// variable "example" {
|
// variable "example" {
|
||||||
// type = list(string)
|
// type = list(string)
|
||||||
// }
|
// }
|
||||||
package typeexpr
|
package typeexpr
|
||||||
|
|||||||
+2
-1
@@ -46,7 +46,8 @@ func TypeConstraintFromVal(v cty.Value) cty.Type {
|
|||||||
// ConvertFunc is a cty function that implements type conversions.
|
// ConvertFunc is a cty function that implements type conversions.
|
||||||
//
|
//
|
||||||
// Its signature is as follows:
|
// Its signature is as follows:
|
||||||
// convert(value, type_constraint)
|
//
|
||||||
|
// convert(value, type_constraint)
|
||||||
//
|
//
|
||||||
// ...where type_constraint is a type constraint expression as defined by
|
// ...where type_constraint is a type constraint expression as defined by
|
||||||
// typeexpr.TypeConstraint.
|
// typeexpr.TypeConstraint.
|
||||||
|
|||||||
+33
-8
@@ -9,9 +9,10 @@ import (
|
|||||||
|
|
||||||
"github.com/zclconf/go-cty/cty"
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
|
||||||
"github.com/hashicorp/hcl/v2"
|
|
||||||
"github.com/zclconf/go-cty/cty/convert"
|
"github.com/zclconf/go-cty/cty/convert"
|
||||||
"github.com/zclconf/go-cty/cty/gocty"
|
"github.com/zclconf/go-cty/cty/gocty"
|
||||||
|
|
||||||
|
"github.com/hashicorp/hcl/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DecodeBody extracts the configuration within the given body into the given
|
// DecodeBody extracts the configuration within the given body into the given
|
||||||
@@ -110,7 +111,7 @@ func decodeBodyToStruct(body hcl.Body, ctx *hcl.EvalContext, val reflect.Value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// As a special case, if the target is of type hcl.Expression then
|
// As a special case, if the target is of type hcl.Expression then
|
||||||
// we'll assign an actual expression that evalues to a cty null,
|
// we'll assign an actual expression that evaluates to a cty null,
|
||||||
// so the caller can deal with it within the cty realm rather
|
// so the caller can deal with it within the cty realm rather
|
||||||
// than within the Go realm.
|
// than within the Go realm.
|
||||||
synthExpr := hcl.StaticExpr(cty.NullVal(cty.DynamicPseudoType), body.MissingItemRange())
|
synthExpr := hcl.StaticExpr(cty.NullVal(cty.DynamicPseudoType), body.MissingItemRange())
|
||||||
@@ -118,6 +119,18 @@ func decodeBodyToStruct(body hcl.Body, ctx *hcl.EvalContext, val reflect.Value)
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if attrRange, exists := tags.AttributeRange[name]; exists {
|
||||||
|
val.Field(attrRange).Set(reflect.ValueOf(attr.Range))
|
||||||
|
}
|
||||||
|
|
||||||
|
if attrNameRange, exists := tags.AttributeNameRange[name]; exists {
|
||||||
|
val.Field(attrNameRange).Set(reflect.ValueOf(attr.NameRange))
|
||||||
|
}
|
||||||
|
|
||||||
|
if attrValueRange, exists := tags.AttributeValueRange[name]; exists {
|
||||||
|
val.Field(attrValueRange).Set(reflect.ValueOf(attr.Expr.Range()))
|
||||||
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case attrType.AssignableTo(field.Type):
|
case attrType.AssignableTo(field.Type):
|
||||||
fieldV.Set(reflect.ValueOf(attr))
|
fieldV.Set(reflect.ValueOf(attr))
|
||||||
@@ -182,7 +195,7 @@ func decodeBodyToStruct(body hcl.Body, ctx *hcl.EvalContext, val reflect.Value)
|
|||||||
case isSlice:
|
case isSlice:
|
||||||
elemType := ty
|
elemType := ty
|
||||||
if isPtr {
|
if isPtr {
|
||||||
elemType = reflect.PtrTo(ty)
|
elemType = reflect.PointerTo(ty)
|
||||||
}
|
}
|
||||||
sli := val.Field(fieldIdx)
|
sli := val.Field(fieldIdx)
|
||||||
if sli.IsNil() {
|
if sli.IsNil() {
|
||||||
@@ -263,14 +276,26 @@ func decodeBodyToMap(body hcl.Body, ctx *hcl.EvalContext, v reflect.Value) hcl.D
|
|||||||
func decodeBlockToValue(block *hcl.Block, ctx *hcl.EvalContext, v reflect.Value) hcl.Diagnostics {
|
func decodeBlockToValue(block *hcl.Block, ctx *hcl.EvalContext, v reflect.Value) hcl.Diagnostics {
|
||||||
diags := decodeBodyToValue(block.Body, ctx, v)
|
diags := decodeBodyToValue(block.Body, ctx, v)
|
||||||
|
|
||||||
if len(block.Labels) > 0 {
|
blockTags := getFieldTags(v.Type())
|
||||||
blockTags := getFieldTags(v.Type())
|
for li, lv := range block.Labels {
|
||||||
for li, lv := range block.Labels {
|
lfieldIdx := blockTags.Labels[li].FieldIndex
|
||||||
lfieldIdx := blockTags.Labels[li].FieldIndex
|
lfieldName := blockTags.Labels[li].Name
|
||||||
v.Field(lfieldIdx).Set(reflect.ValueOf(lv))
|
|
||||||
|
v.Field(lfieldIdx).Set(reflect.ValueOf(lv))
|
||||||
|
|
||||||
|
if ix, exists := blockTags.LabelRange[lfieldName]; exists {
|
||||||
|
v.Field(ix).Set(reflect.ValueOf(block.LabelRanges[li]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if blockTags.TypeRange != nil {
|
||||||
|
v.Field(*blockTags.TypeRange).Set(reflect.ValueOf(block.TypeRange))
|
||||||
|
}
|
||||||
|
|
||||||
|
if blockTags.DefRange != nil {
|
||||||
|
v.Field(*blockTags.DefRange).Set(reflect.ValueOf(block.DefRange))
|
||||||
|
}
|
||||||
|
|
||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+38
-8
@@ -10,18 +10,18 @@
|
|||||||
// A struct field tag scheme is used, similar to other decoding and
|
// A struct field tag scheme is used, similar to other decoding and
|
||||||
// unmarshalling libraries. The tags are formatted as in the following example:
|
// unmarshalling libraries. The tags are formatted as in the following example:
|
||||||
//
|
//
|
||||||
// ThingType string `hcl:"thing_type,attr"`
|
// ThingType string `hcl:"thing_type,attr"`
|
||||||
//
|
//
|
||||||
// Within each tag there are two comma-separated tokens. The first is the
|
// Within each tag there are two comma-separated tokens. The first is the
|
||||||
// name of the corresponding construct in configuration, while the second
|
// name of the corresponding construct in configuration, while the second
|
||||||
// is a keyword giving the kind of construct expected. The following
|
// is a keyword giving the kind of construct expected. The following
|
||||||
// kind keywords are supported:
|
// kind keywords are supported:
|
||||||
//
|
//
|
||||||
// attr (the default) indicates that the value is to be populated from an attribute
|
// attr (the default) indicates that the value is to be populated from an attribute
|
||||||
// block indicates that the value is to populated from a block
|
// block indicates that the value is to populated from a block
|
||||||
// label indicates that the value is to populated from a block label
|
// label indicates that the value is to populated from a block label
|
||||||
// optional is the same as attr, but the field is optional
|
// optional is the same as attr, but the field is optional
|
||||||
// remain indicates that the value is to be populated from the remaining body after populating other fields
|
// remain indicates that the value is to be populated from the remaining body after populating other fields
|
||||||
//
|
//
|
||||||
// "attr" fields may either be of type *hcl.Expression, in which case the raw
|
// "attr" fields may either be of type *hcl.Expression, in which case the raw
|
||||||
// expression is assigned, or of any type accepted by gocty, in which case
|
// expression is assigned, or of any type accepted by gocty, in which case
|
||||||
@@ -40,8 +40,9 @@
|
|||||||
//
|
//
|
||||||
// "label" fields are considered only in a struct used as the type of a field
|
// "label" fields are considered only in a struct used as the type of a field
|
||||||
// marked as "block", and are used sequentially to capture the labels of
|
// marked as "block", and are used sequentially to capture the labels of
|
||||||
// the blocks being decoded. In this case, the name token is used only as
|
// the blocks being decoded. In this case, the name token is used (a) as
|
||||||
// an identifier for the label in diagnostic messages.
|
// an identifier for the label in diagnostic messages and (b) to match the
|
||||||
|
// which with the equivalent "label_range" field (if it exists).
|
||||||
//
|
//
|
||||||
// "optional" fields behave like "attr" fields, but they are optional
|
// "optional" fields behave like "attr" fields, but they are optional
|
||||||
// and will not give parsing errors if they are missing.
|
// and will not give parsing errors if they are missing.
|
||||||
@@ -52,6 +53,35 @@
|
|||||||
// present then any attributes or blocks not matched by another valid tag
|
// present then any attributes or blocks not matched by another valid tag
|
||||||
// will cause an error diagnostic.
|
// will cause an error diagnostic.
|
||||||
//
|
//
|
||||||
|
// "def_range" can be placed on a single field that must be of type hcl.Range.
|
||||||
|
// This field is only considered in a struct used as the type of a field marked
|
||||||
|
// as "block", and is used to capture the range of the block's definition.
|
||||||
|
//
|
||||||
|
// "type_range" can be placed on a single field that must be of type hcl.Range.
|
||||||
|
// This field is only considered in a struct used as the type of a field marked
|
||||||
|
// as "block", and is used to capture the range of the block's type label.
|
||||||
|
//
|
||||||
|
// "label_range" can be placed on multiple fields that must be of type
|
||||||
|
// hcl.Range. This field is only considered in a struct used as the type of a
|
||||||
|
// field marked as "block", and is used to capture the range of the block's
|
||||||
|
// labels. The name token is used to match with the equivalent "label" field
|
||||||
|
// that this range will specify.
|
||||||
|
//
|
||||||
|
// "attr_range" can be placed on multiple fields that must be of type hcl.Range.
|
||||||
|
// This field will be assigned the complete hcl.Range for the attribute with
|
||||||
|
// the corresponding name. The name token is used to match with the name of the
|
||||||
|
// attribute that this range will specify.
|
||||||
|
//
|
||||||
|
// "attr_name_range" can be placed on multiple fields that must be of type
|
||||||
|
// hcl.Range. This field will be assigned the hcl.Range for the name of the
|
||||||
|
// attribute with the corresponding name. The name token is used to match with
|
||||||
|
// the name of the attribute that this range will specify.
|
||||||
|
//
|
||||||
|
// "attr_value_range" can be placed on multiple fields that must be of type
|
||||||
|
// hcl.Range. This field will be assigned the hcl.Range for the value of the
|
||||||
|
// attribute with the corresponding name. The name token is used to match with
|
||||||
|
// the name of the attribute that this range will specify.
|
||||||
|
//
|
||||||
// Only a subset of this tagging/typing vocabulary is supported for the
|
// Only a subset of this tagging/typing vocabulary is supported for the
|
||||||
// "Encode" family of functions. See the EncodeIntoBody docs for full details
|
// "Encode" family of functions. See the EncodeIntoBody docs for full details
|
||||||
// on the constraints there.
|
// on the constraints there.
|
||||||
|
|||||||
+36
-3
@@ -118,18 +118,31 @@ type fieldTags struct {
|
|||||||
Remain *int
|
Remain *int
|
||||||
Body *int
|
Body *int
|
||||||
Optional map[string]bool
|
Optional map[string]bool
|
||||||
|
|
||||||
|
AttributeRange map[string]int
|
||||||
|
AttributeNameRange map[string]int
|
||||||
|
AttributeValueRange map[string]int
|
||||||
|
|
||||||
|
DefRange *int
|
||||||
|
TypeRange *int
|
||||||
|
LabelRange map[string]int
|
||||||
}
|
}
|
||||||
|
|
||||||
type labelField struct {
|
type labelField struct {
|
||||||
FieldIndex int
|
FieldIndex int
|
||||||
|
RangeIndex int
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFieldTags(ty reflect.Type) *fieldTags {
|
func getFieldTags(ty reflect.Type) *fieldTags {
|
||||||
ret := &fieldTags{
|
ret := &fieldTags{
|
||||||
Attributes: map[string]int{},
|
Attributes: map[string]int{},
|
||||||
Blocks: map[string]int{},
|
Blocks: map[string]int{},
|
||||||
Optional: map[string]bool{},
|
Optional: map[string]bool{},
|
||||||
|
AttributeRange: map[string]int{},
|
||||||
|
AttributeNameRange: map[string]int{},
|
||||||
|
AttributeValueRange: map[string]int{},
|
||||||
|
LabelRange: map[string]int{},
|
||||||
}
|
}
|
||||||
|
|
||||||
ct := ty.NumField()
|
ct := ty.NumField()
|
||||||
@@ -175,6 +188,26 @@ func getFieldTags(ty reflect.Type) *fieldTags {
|
|||||||
case "optional":
|
case "optional":
|
||||||
ret.Attributes[name] = i
|
ret.Attributes[name] = i
|
||||||
ret.Optional[name] = true
|
ret.Optional[name] = true
|
||||||
|
case "def_range":
|
||||||
|
if ret.DefRange != nil {
|
||||||
|
panic("only one 'def_range' tag is permitted")
|
||||||
|
}
|
||||||
|
idx := i // copy, because this loop will continue assigning to i
|
||||||
|
ret.DefRange = &idx
|
||||||
|
case "type_range":
|
||||||
|
if ret.TypeRange != nil {
|
||||||
|
panic("only one 'type_range' tag is permitted")
|
||||||
|
}
|
||||||
|
idx := i // copy, because this loop will continue assigning to i
|
||||||
|
ret.TypeRange = &idx
|
||||||
|
case "label_range":
|
||||||
|
ret.LabelRange[name] = i
|
||||||
|
case "attr_range":
|
||||||
|
ret.AttributeRange[name] = i
|
||||||
|
case "attr_name_range":
|
||||||
|
ret.AttributeNameRange[name] = i
|
||||||
|
case "attr_value_range":
|
||||||
|
ret.AttributeValueRange[name] = i
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("invalid hcl field tag kind %q on %s %q", kind, field.Type.String(), field.Name))
|
panic(fmt.Sprintf("invalid hcl field tag kind %q on %s %q", kind, field.Type.String(), field.Name))
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -14,6 +14,6 @@ var victimBody hcl.Body
|
|||||||
|
|
||||||
var exprType = reflect.TypeOf(&victimExpr).Elem()
|
var exprType = reflect.TypeOf(&victimExpr).Elem()
|
||||||
var bodyType = reflect.TypeOf(&victimBody).Elem()
|
var bodyType = reflect.TypeOf(&victimBody).Elem()
|
||||||
var blockType = reflect.TypeOf((*hcl.Block)(nil))
|
|
||||||
var attrType = reflect.TypeOf((*hcl.Attribute)(nil))
|
var attrType = reflect.TypeOf((*hcl.Attribute)(nil))
|
||||||
var attrsType = reflect.TypeOf(hcl.Attributes(nil))
|
var attrsType = reflect.TypeOf(hcl.Attributes(nil))
|
||||||
|
|||||||
+2
-2
@@ -17,7 +17,7 @@ package hclparse
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"os"
|
||||||
|
|
||||||
"github.com/hashicorp/hcl/v2"
|
"github.com/hashicorp/hcl/v2"
|
||||||
"github.com/hashicorp/hcl/v2/hclsyntax"
|
"github.com/hashicorp/hcl/v2/hclsyntax"
|
||||||
@@ -70,7 +70,7 @@ func (p *Parser) ParseHCLFile(filename string) (*hcl.File, hcl.Diagnostics) {
|
|||||||
return existing, nil
|
return existing, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
src, err := ioutil.ReadFile(filename)
|
src, err := os.ReadFile(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, hcl.Diagnostics{
|
return nil, hcl.Diagnostics{
|
||||||
{
|
{
|
||||||
|
|||||||
+17
@@ -1796,6 +1796,7 @@ func (e *SplatExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||||||
// both to tuples/lists and to other values, and in the latter case
|
// both to tuples/lists and to other values, and in the latter case
|
||||||
// the value will be treated as an implicit single-item tuple, or as
|
// the value will be treated as an implicit single-item tuple, or as
|
||||||
// an empty tuple if the value is null.
|
// an empty tuple if the value is null.
|
||||||
|
//nolint:staticcheck // QF1001: Demorgan's law wouldn't improve readability.
|
||||||
autoUpgrade := !(sourceTy.IsTupleType() || sourceTy.IsListType() || sourceTy.IsSetType())
|
autoUpgrade := !(sourceTy.IsTupleType() || sourceTy.IsListType() || sourceTy.IsSetType())
|
||||||
|
|
||||||
if sourceVal.IsNull() {
|
if sourceVal.IsNull() {
|
||||||
@@ -1938,6 +1939,22 @@ func (e *SplatExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
|||||||
diags = append(diags, tyDiags...)
|
diags = append(diags, tyDiags...)
|
||||||
return cty.ListValEmpty(ty.ElementType()).WithMarks(marks), diags
|
return cty.ListValEmpty(ty.ElementType()).WithMarks(marks), diags
|
||||||
}
|
}
|
||||||
|
// Unfortunately it's possible for a nested splat on scalar values to
|
||||||
|
// generate non-homogenously-typed vals, and we discovered this bad
|
||||||
|
// interaction after the two conflicting behaviors were both
|
||||||
|
// well-established so it isn't clear how to change them without
|
||||||
|
// breaking existing code. Therefore we just make that an error for
|
||||||
|
// now, to avoid crashing trying to constuct an impossible list.
|
||||||
|
if !cty.CanListVal(vals) {
|
||||||
|
diags = append(diags, &hcl.Diagnostic{
|
||||||
|
Severity: hcl.DiagError,
|
||||||
|
Summary: "Invalid nested splat expressions",
|
||||||
|
Detail: "The second level of splat expression produced elements of different types, so it isn't possible to construct a valid list to represent the top-level result.\n\nConsider using a for expression instead, to produce a tuple-typed result which can therefore have non-homogenous element types.",
|
||||||
|
Subject: e.Each.Range().Ptr(),
|
||||||
|
Context: e.Range().Ptr(), // encourage a diagnostic renderer to also include the "source" part of the expression in its code snippet
|
||||||
|
})
|
||||||
|
return cty.DynamicVal, diags
|
||||||
|
}
|
||||||
return cty.ListVal(vals).WithMarks(marks), diags
|
return cty.ListVal(vals).WithMarks(marks), diags
|
||||||
default:
|
default:
|
||||||
return cty.TupleVal(vals).WithMarks(marks), diags
|
return cty.TupleVal(vals).WithMarks(marks), diags
|
||||||
|
|||||||
+101
-7
@@ -16,16 +16,86 @@ import (
|
|||||||
type Operation struct {
|
type Operation struct {
|
||||||
Impl function.Function
|
Impl function.Function
|
||||||
Type cty.Type
|
Type cty.Type
|
||||||
|
|
||||||
|
// ShortCircuit is an optional callback for binary operations which, if set,
|
||||||
|
// will be called with the result of evaluating the LHS and RHS expressions
|
||||||
|
// and their individual diagnostics. The LHS and RHS values are guaranteed
|
||||||
|
// to be unmarked and of the correct type.
|
||||||
|
//
|
||||||
|
// ShortCircuit may return cty.NilVal to allow evaluation to proceed as
|
||||||
|
// normal, or it may return a non-nil value with diagnostics to return
|
||||||
|
// before the main Impl is called. The returned diagnostics should match
|
||||||
|
// the side of the Operation which was taken.
|
||||||
|
ShortCircuit func(lhs, rhs cty.Value, lhsDiags, rhsDiags hcl.Diagnostics) (cty.Value, hcl.Diagnostics)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
OpLogicalOr = &Operation{
|
OpLogicalOr = &Operation{
|
||||||
Impl: stdlib.OrFunc,
|
Impl: stdlib.OrFunc,
|
||||||
Type: cty.Bool,
|
Type: cty.Bool,
|
||||||
|
|
||||||
|
ShortCircuit: func(lhs, rhs cty.Value, lhsDiags, rhsDiags hcl.Diagnostics) (cty.Value, hcl.Diagnostics) {
|
||||||
|
switch {
|
||||||
|
// if both are unknown, we don't short circuit anything
|
||||||
|
case !lhs.IsKnown() && !rhs.IsKnown():
|
||||||
|
// short-circuit left-to-right when encountering a good unknown
|
||||||
|
// value and both are unknown.
|
||||||
|
if !lhsDiags.HasErrors() {
|
||||||
|
return cty.UnknownVal(cty.Bool).RefineNotNull(), lhsDiags
|
||||||
|
}
|
||||||
|
// If the LHS has an error, the RHS might too. Don't
|
||||||
|
// short-circuit so both diags get collected.
|
||||||
|
return cty.NilVal, nil
|
||||||
|
|
||||||
|
// for ||, a single true is the controlling condition
|
||||||
|
case lhs.IsKnown() && lhs.True():
|
||||||
|
return cty.True, lhsDiags
|
||||||
|
case rhs.IsKnown() && rhs.True():
|
||||||
|
return cty.True, rhsDiags
|
||||||
|
|
||||||
|
// if the opposing side is false we can't short-circuit based on
|
||||||
|
// boolean logic, so an unknown becomes the controlling condition
|
||||||
|
case !lhs.IsKnown() && rhs.False():
|
||||||
|
return cty.UnknownVal(cty.Bool).RefineNotNull(), lhsDiags
|
||||||
|
case !rhs.IsKnown() && lhs.False():
|
||||||
|
return cty.UnknownVal(cty.Bool).RefineNotNull(), rhsDiags
|
||||||
|
}
|
||||||
|
|
||||||
|
return cty.NilVal, nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
OpLogicalAnd = &Operation{
|
OpLogicalAnd = &Operation{
|
||||||
Impl: stdlib.AndFunc,
|
Impl: stdlib.AndFunc,
|
||||||
Type: cty.Bool,
|
Type: cty.Bool,
|
||||||
|
|
||||||
|
ShortCircuit: func(lhs, rhs cty.Value, lhsDiags, rhsDiags hcl.Diagnostics) (cty.Value, hcl.Diagnostics) {
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case !lhs.IsKnown() && !rhs.IsKnown():
|
||||||
|
// short-circuit left-to-right when encountering a good unknown
|
||||||
|
// value and both are unknown.
|
||||||
|
if !lhsDiags.HasErrors() {
|
||||||
|
return cty.UnknownVal(cty.Bool).RefineNotNull(), lhsDiags
|
||||||
|
}
|
||||||
|
// If the LHS has an error, the RHS might too. Don't
|
||||||
|
// short-circuit so both diags get collected.
|
||||||
|
return cty.NilVal, nil
|
||||||
|
|
||||||
|
// For &&, a single false is the controlling condition
|
||||||
|
case lhs.IsKnown() && lhs.False():
|
||||||
|
return cty.False, lhsDiags
|
||||||
|
case rhs.IsKnown() && rhs.False():
|
||||||
|
return cty.False, rhsDiags
|
||||||
|
|
||||||
|
// if the opposing side is true we can't short-circuit based on
|
||||||
|
// boolean logic, so an unknown becomes the controlling condition
|
||||||
|
case !lhs.IsKnown() && rhs.True():
|
||||||
|
return cty.UnknownVal(cty.Bool).RefineNotNull(), lhsDiags
|
||||||
|
case !rhs.IsKnown() && lhs.True():
|
||||||
|
return cty.UnknownVal(cty.Bool).RefineNotNull(), rhsDiags
|
||||||
|
}
|
||||||
|
return cty.NilVal, nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
OpLogicalNot = &Operation{
|
OpLogicalNot = &Operation{
|
||||||
Impl: stdlib.NotFunc,
|
Impl: stdlib.NotFunc,
|
||||||
@@ -145,10 +215,6 @@ func (e *BinaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics)
|
|||||||
var diags hcl.Diagnostics
|
var diags hcl.Diagnostics
|
||||||
|
|
||||||
givenLHSVal, lhsDiags := e.LHS.Value(ctx)
|
givenLHSVal, lhsDiags := e.LHS.Value(ctx)
|
||||||
givenRHSVal, rhsDiags := e.RHS.Value(ctx)
|
|
||||||
diags = append(diags, lhsDiags...)
|
|
||||||
diags = append(diags, rhsDiags...)
|
|
||||||
|
|
||||||
lhsVal, err := convert.Convert(givenLHSVal, lhsParam.Type)
|
lhsVal, err := convert.Convert(givenLHSVal, lhsParam.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
diags = append(diags, &hcl.Diagnostic{
|
diags = append(diags, &hcl.Diagnostic{
|
||||||
@@ -161,6 +227,8 @@ func (e *BinaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics)
|
|||||||
EvalContext: ctx,
|
EvalContext: ctx,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
givenRHSVal, rhsDiags := e.RHS.Value(ctx)
|
||||||
rhsVal, err := convert.Convert(givenRHSVal, rhsParam.Type)
|
rhsVal, err := convert.Convert(givenRHSVal, rhsParam.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
diags = append(diags, &hcl.Diagnostic{
|
diags = append(diags, &hcl.Diagnostic{
|
||||||
@@ -174,12 +242,38 @@ func (e *BinaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics)
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// diags so far only contains conversion errors, which should cover
|
||||||
|
// incorrect parameter types.
|
||||||
if diags.HasErrors() {
|
if diags.HasErrors() {
|
||||||
// Don't actually try the call if we have errors already, since the
|
// Add the rest of the diagnostic in case that helps the user, but keep
|
||||||
// this will probably just produce a confusing duplicative diagnostic.
|
// them separate as we continue for short-circuit handling.
|
||||||
|
diags = append(diags, lhsDiags...)
|
||||||
|
diags = append(diags, rhsDiags...)
|
||||||
return cty.UnknownVal(e.Op.Type), diags
|
return cty.UnknownVal(e.Op.Type), diags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lhsVal, lhsMarks := lhsVal.Unmark()
|
||||||
|
rhsVal, rhsMarks := rhsVal.Unmark()
|
||||||
|
|
||||||
|
if e.Op.ShortCircuit != nil {
|
||||||
|
forceResult, diags := e.Op.ShortCircuit(lhsVal, rhsVal, lhsDiags, rhsDiags)
|
||||||
|
if forceResult != cty.NilVal {
|
||||||
|
// It would be technically more correct to insert rhs diagnostics if
|
||||||
|
// forceResult is not known since we didn't really short-circuit. That
|
||||||
|
// would however not match the behavior of conditional expressions which
|
||||||
|
// do drop all diagnostics from the unevaluated expressions
|
||||||
|
return forceResult.WithMarks(lhsMarks, rhsMarks), diags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diags = append(diags, lhsDiags...)
|
||||||
|
diags = append(diags, rhsDiags...)
|
||||||
|
if diags.HasErrors() {
|
||||||
|
// Don't actually try the call if we have errors, since the this will
|
||||||
|
// probably just produce confusing duplicate diagnostics.
|
||||||
|
return cty.UnknownVal(e.Op.Type).WithMarks(lhsMarks, rhsMarks), diags
|
||||||
|
}
|
||||||
|
|
||||||
args := []cty.Value{lhsVal, rhsVal}
|
args := []cty.Value{lhsVal, rhsVal}
|
||||||
result, err := impl.Call(args)
|
result, err := impl.Call(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -195,7 +289,7 @@ func (e *BinaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics)
|
|||||||
return cty.UnknownVal(e.Op.Type), diags
|
return cty.UnknownVal(e.Op.Type), diags
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, diags
|
return result.WithMarks(lhsMarks, rhsMarks), diags
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *BinaryOpExpr) Range() hcl.Range {
|
func (e *BinaryOpExpr) Range() hcl.Range {
|
||||||
|
|||||||
+3
-5
@@ -184,11 +184,9 @@ func (e *TemplateJoinExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
|
|||||||
|
|
||||||
if val.IsNull() {
|
if val.IsNull() {
|
||||||
diags = append(diags, &hcl.Diagnostic{
|
diags = append(diags, &hcl.Diagnostic{
|
||||||
Severity: hcl.DiagError,
|
Severity: hcl.DiagError,
|
||||||
Summary: "Invalid template interpolation value",
|
Summary: "Invalid template interpolation value",
|
||||||
Detail: fmt.Sprintf(
|
Detail: "An iteration result is null. Cannot include a null value in a string template.",
|
||||||
"An iteration result is null. Cannot include a null value in a string template.",
|
|
||||||
),
|
|
||||||
Subject: e.Range().Ptr(),
|
Subject: e.Range().Ptr(),
|
||||||
Expression: e,
|
Expression: e,
|
||||||
EvalContext: ctx,
|
EvalContext: ctx,
|
||||||
|
|||||||
+1
-1
@@ -42,7 +42,7 @@ type Body struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Assert that *Body implements hcl.Body
|
// Assert that *Body implements hcl.Body
|
||||||
var assertBodyImplBody hcl.Body = &Body{}
|
var _ hcl.Body = &Body{}
|
||||||
|
|
||||||
func (b *Body) walkChildNodes(w internalWalkFunc) {
|
func (b *Body) walkChildNodes(w internalWalkFunc) {
|
||||||
w(b.Attributes)
|
w(b.Attributes)
|
||||||
|
|||||||
+1
@@ -20,6 +20,7 @@ func Variables(expr Expression) []hcl.Traversal {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//nolint:errcheck // FIXME: Propogate diagnostics/errors upward.
|
||||||
Walk(expr, walker)
|
Walk(expr, walker)
|
||||||
|
|
||||||
return vars
|
return vars
|
||||||
|
|||||||
+4
-7
@@ -37,7 +37,7 @@ func (f *File) Body() *Body {
|
|||||||
// The tokens first have a simple formatting pass applied that adjusts only
|
// The tokens first have a simple formatting pass applied that adjusts only
|
||||||
// the spaces between them.
|
// the spaces between them.
|
||||||
func (f *File) WriteTo(wr io.Writer) (int64, error) {
|
func (f *File) WriteTo(wr io.Writer) (int64, error) {
|
||||||
tokens := f.inTree.children.BuildTokens(nil)
|
tokens := f.children.BuildTokens(nil)
|
||||||
format(tokens)
|
format(tokens)
|
||||||
return tokens.WriteTo(wr)
|
return tokens.WriteTo(wr)
|
||||||
}
|
}
|
||||||
@@ -47,6 +47,7 @@ func (f *File) WriteTo(wr io.Writer) (int64, error) {
|
|||||||
// the AST API, these will be reflected in the result.
|
// the AST API, these will be reflected in the result.
|
||||||
func (f *File) Bytes() []byte {
|
func (f *File) Bytes() []byte {
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
|
//nolint:errcheck // FIXME: Propogate errors upward.
|
||||||
f.WriteTo(buf)
|
f.WriteTo(buf)
|
||||||
return buf.Bytes()
|
return buf.Bytes()
|
||||||
}
|
}
|
||||||
@@ -54,7 +55,6 @@ func (f *File) Bytes() []byte {
|
|||||||
type comments struct {
|
type comments struct {
|
||||||
leafNode
|
leafNode
|
||||||
|
|
||||||
parent *node
|
|
||||||
tokens Tokens
|
tokens Tokens
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,8 +71,7 @@ func (c *comments) BuildTokens(to Tokens) Tokens {
|
|||||||
type identifier struct {
|
type identifier struct {
|
||||||
leafNode
|
leafNode
|
||||||
|
|
||||||
parent *node
|
token *Token
|
||||||
token *Token
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newIdentifier(token *Token) *identifier {
|
func newIdentifier(token *Token) *identifier {
|
||||||
@@ -92,8 +91,7 @@ func (i *identifier) hasName(name string) bool {
|
|||||||
type number struct {
|
type number struct {
|
||||||
leafNode
|
leafNode
|
||||||
|
|
||||||
parent *node
|
token *Token
|
||||||
token *Token
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newNumber(token *Token) *number {
|
func newNumber(token *Token) *number {
|
||||||
@@ -109,7 +107,6 @@ func (n *number) BuildTokens(to Tokens) Tokens {
|
|||||||
type quoted struct {
|
type quoted struct {
|
||||||
leafNode
|
leafNode
|
||||||
|
|
||||||
parent *node
|
|
||||||
tokens Tokens
|
tokens Tokens
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
@@ -49,3 +49,9 @@ func (a *Attribute) init(name string, expr *Expression) {
|
|||||||
func (a *Attribute) Expr() *Expression {
|
func (a *Attribute) Expr() *Expression {
|
||||||
return a.expr.content.(*Expression)
|
return a.expr.content.(*Expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setName updates the name of the attribute.
|
||||||
|
func (a *Attribute) setName(name string) {
|
||||||
|
nameObj := newIdentifier(newIdentToken(name))
|
||||||
|
a.name = a.name.ReplaceWith(nameObj)
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -122,7 +122,7 @@ func newBlockLabels(labels []string) *blockLabels {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bl *blockLabels) Replace(newLabels []string) {
|
func (bl *blockLabels) Replace(newLabels []string) {
|
||||||
bl.inTree.children.Clear()
|
bl.children.Clear()
|
||||||
bl.items.Clear()
|
bl.items.Clear()
|
||||||
|
|
||||||
for _, label := range newLabels {
|
for _, label := range newLabels {
|
||||||
|
|||||||
+16
-1
@@ -43,7 +43,7 @@ func (b *Body) Clear() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *Body) AppendUnstructuredTokens(ts Tokens) {
|
func (b *Body) AppendUnstructuredTokens(ts Tokens) {
|
||||||
b.inTree.children.Append(ts)
|
b.children.Append(ts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attributes returns a new map of all of the attributes in the body, with
|
// Attributes returns a new map of all of the attributes in the body, with
|
||||||
@@ -103,6 +103,21 @@ func (b *Body) getAttributeNode(name string) *node {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RenameAttribute changes the attribute named fromName to toName.
|
||||||
|
// Takes no action if fromName is missing or there is already a
|
||||||
|
// conflicting attribute called toName.
|
||||||
|
//
|
||||||
|
// Returns true if the rename succeeded.
|
||||||
|
func (b *Body) RenameAttribute(fromName, toName string) bool {
|
||||||
|
attr := b.GetAttribute(fromName)
|
||||||
|
conflictingAttr := b.GetAttribute(toName)
|
||||||
|
if attr == nil || conflictingAttr != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
attr.setName(toName)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// FirstMatchingBlock returns a first matching block from the body that has the
|
// FirstMatchingBlock returns a first matching block from the body that has the
|
||||||
// given name and labels or returns nil if there is currently no matching
|
// given name and labels or returns nil if there is currently no matching
|
||||||
// block.
|
// block.
|
||||||
|
|||||||
+3
-2
@@ -429,9 +429,10 @@ func linesForFormat(tokens Tokens) []formatLine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func tokenIsNewline(tok *Token) bool {
|
func tokenIsNewline(tok *Token) bool {
|
||||||
if tok.Type == hclsyntax.TokenNewline {
|
switch tok.Type {
|
||||||
|
case hclsyntax.TokenNewline:
|
||||||
return true
|
return true
|
||||||
} else if tok.Type == hclsyntax.TokenComment {
|
case hclsyntax.TokenComment:
|
||||||
// Single line tokens (# and //) consume their terminating newline,
|
// Single line tokens (# and //) consume their terminating newline,
|
||||||
// so we need to treat them as newline tokens as well.
|
// so we need to treat them as newline tokens as well.
|
||||||
if len(tok.Bytes) > 0 && tok.Bytes[len(tok.Bytes)-1] == '\n' {
|
if len(tok.Bytes) > 0 && tok.Bytes[len(tok.Bytes)-1] == '\n' {
|
||||||
|
|||||||
+12
-12
@@ -61,7 +61,7 @@ func parse(src []byte, filename string, start hcl.Pos) (*File, hcl.Diagnostics)
|
|||||||
body: root,
|
body: root,
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes := ret.inTree.children
|
nodes := ret.children
|
||||||
nodes.Append(before.Tokens())
|
nodes.Append(before.Tokens())
|
||||||
nodes.AppendNode(root)
|
nodes.AppendNode(root)
|
||||||
nodes.Append(after.Tokens())
|
nodes.Append(after.Tokens())
|
||||||
@@ -242,7 +242,7 @@ func parseAttribute(nativeAttr *hclsyntax.Attribute, from, leadComments, lineCom
|
|||||||
attr := &Attribute{
|
attr := &Attribute{
|
||||||
inTree: newInTree(),
|
inTree: newInTree(),
|
||||||
}
|
}
|
||||||
children := attr.inTree.children
|
children := attr.children
|
||||||
|
|
||||||
{
|
{
|
||||||
cn := newNode(newComments(leadComments.Tokens()))
|
cn := newNode(newComments(leadComments.Tokens()))
|
||||||
@@ -293,7 +293,7 @@ func parseBlock(nativeBlock *hclsyntax.Block, from, leadComments, lineComments,
|
|||||||
block := &Block{
|
block := &Block{
|
||||||
inTree: newInTree(),
|
inTree: newInTree(),
|
||||||
}
|
}
|
||||||
children := block.inTree.children
|
children := block.children
|
||||||
|
|
||||||
{
|
{
|
||||||
cn := newNode(newComments(leadComments.Tokens()))
|
cn := newNode(newComments(leadComments.Tokens()))
|
||||||
@@ -314,7 +314,7 @@ func parseBlock(nativeBlock *hclsyntax.Block, from, leadComments, lineComments,
|
|||||||
children.AppendNode(in)
|
children.AppendNode(in)
|
||||||
}
|
}
|
||||||
|
|
||||||
before, labelsNode, from := parseBlockLabels(nativeBlock, from)
|
_, labelsNode, from := parseBlockLabels(nativeBlock, from)
|
||||||
block.labels = labelsNode
|
block.labels = labelsNode
|
||||||
children.AppendNode(labelsNode)
|
children.AppendNode(labelsNode)
|
||||||
|
|
||||||
@@ -377,7 +377,7 @@ func parseBlockLabels(nativeBlock *hclsyntax.Block, from inputTokens) (inputToke
|
|||||||
|
|
||||||
func parseExpression(nativeExpr hclsyntax.Expression, from inputTokens) *node {
|
func parseExpression(nativeExpr hclsyntax.Expression, from inputTokens) *node {
|
||||||
expr := newExpression()
|
expr := newExpression()
|
||||||
children := expr.inTree.children
|
children := expr.children
|
||||||
|
|
||||||
nativeVars := nativeExpr.Variables()
|
nativeVars := nativeExpr.Variables()
|
||||||
|
|
||||||
@@ -398,7 +398,7 @@ func parseExpression(nativeExpr hclsyntax.Expression, from inputTokens) *node {
|
|||||||
|
|
||||||
func parseTraversal(nativeTraversal hcl.Traversal, from inputTokens) (before inputTokens, n *node, after inputTokens) {
|
func parseTraversal(nativeTraversal hcl.Traversal, from inputTokens) (before inputTokens, n *node, after inputTokens) {
|
||||||
traversal := newTraversal()
|
traversal := newTraversal()
|
||||||
children := traversal.inTree.children
|
children := traversal.children
|
||||||
before, from, after = from.Partition(nativeTraversal.SourceRange())
|
before, from, after = from.Partition(nativeTraversal.SourceRange())
|
||||||
|
|
||||||
stepAfter := from
|
stepAfter := from
|
||||||
@@ -419,7 +419,7 @@ func parseTraversalStep(nativeStep hcl.Traverser, from inputTokens) (before inpu
|
|||||||
|
|
||||||
case hcl.TraverseRoot, hcl.TraverseAttr:
|
case hcl.TraverseRoot, hcl.TraverseAttr:
|
||||||
step := newTraverseName()
|
step := newTraverseName()
|
||||||
children = step.inTree.children
|
children = step.children
|
||||||
before, from, after = from.Partition(nativeStep.SourceRange())
|
before, from, after = from.Partition(nativeStep.SourceRange())
|
||||||
inBefore, token, inAfter := from.PartitionTypeSingle(hclsyntax.TokenIdent)
|
inBefore, token, inAfter := from.PartitionTypeSingle(hclsyntax.TokenIdent)
|
||||||
name := newIdentifier(token)
|
name := newIdentifier(token)
|
||||||
@@ -430,7 +430,7 @@ func parseTraversalStep(nativeStep hcl.Traverser, from inputTokens) (before inpu
|
|||||||
|
|
||||||
case hcl.TraverseIndex:
|
case hcl.TraverseIndex:
|
||||||
step := newTraverseIndex()
|
step := newTraverseIndex()
|
||||||
children = step.inTree.children
|
children = step.children
|
||||||
before, from, after = from.Partition(nativeStep.SourceRange())
|
before, from, after = from.Partition(nativeStep.SourceRange())
|
||||||
|
|
||||||
if inBefore, dot, from, ok := from.PartitionTypeOk(hclsyntax.TokenDot); ok {
|
if inBefore, dot, from, ok := from.PartitionTypeOk(hclsyntax.TokenDot); ok {
|
||||||
@@ -521,10 +521,10 @@ func writerTokens(nativeTokens hclsyntax.Tokens) Tokens {
|
|||||||
// boundaries, such that the slice operator could be used to produce
|
// boundaries, such that the slice operator could be used to produce
|
||||||
// three token sequences for before, within, and after respectively:
|
// three token sequences for before, within, and after respectively:
|
||||||
//
|
//
|
||||||
// start, end := partitionTokens(toks, rng)
|
// start, end := partitionTokens(toks, rng)
|
||||||
// before := toks[:start]
|
// before := toks[:start]
|
||||||
// within := toks[start:end]
|
// within := toks[start:end]
|
||||||
// after := toks[end:]
|
// after := toks[end:]
|
||||||
//
|
//
|
||||||
// This works best when the range is aligned with token boundaries (e.g.
|
// This works best when the range is aligned with token boundaries (e.g.
|
||||||
// because it was produced in terms of the scanner's result) but if that isn't
|
// because it was produced in terms of the scanner's result) but if that isn't
|
||||||
|
|||||||
+2
-1
@@ -19,7 +19,7 @@ func NewFile() *File {
|
|||||||
file := &File{
|
file := &File{
|
||||||
inTree: newInTree(),
|
inTree: newInTree(),
|
||||||
}
|
}
|
||||||
file.body = file.inTree.children.Append(body)
|
file.body = file.children.Append(body)
|
||||||
return file
|
return file
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,6 +42,7 @@ func Format(src []byte) []byte {
|
|||||||
tokens := lexConfig(src)
|
tokens := lexConfig(src)
|
||||||
format(tokens)
|
format(tokens)
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
|
//nolint:errcheck // FIXME: Propogate errors upward.
|
||||||
tokens.WriteTo(buf)
|
tokens.WriteTo(buf)
|
||||||
return buf.Bytes()
|
return buf.Bytes()
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -48,6 +48,7 @@ type Tokens []*Token
|
|||||||
|
|
||||||
func (ts Tokens) Bytes() []byte {
|
func (ts Tokens) Bytes() []byte {
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
|
//nolint:errcheck // FIXME: Propogate errors upward.
|
||||||
ts.WriteTo(buf)
|
ts.WriteTo(buf)
|
||||||
return buf.Bytes()
|
return buf.Bytes()
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-12
@@ -101,15 +101,6 @@ func parseValue(p *peeker) (node, hcl.Diagnostics) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func tokenCanStartValue(tok token) bool {
|
|
||||||
switch tok.Type {
|
|
||||||
case tokenBraceO, tokenBrackO, tokenNumber, tokenString, tokenKeyword:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseObject(p *peeker) (node, hcl.Diagnostics) {
|
func parseObject(p *peeker) (node, hcl.Diagnostics) {
|
||||||
var diags hcl.Diagnostics
|
var diags hcl.Diagnostics
|
||||||
|
|
||||||
@@ -375,7 +366,7 @@ func parseNumber(p *peeker) (node, hcl.Diagnostics) {
|
|||||||
{
|
{
|
||||||
Severity: hcl.DiagError,
|
Severity: hcl.DiagError,
|
||||||
Summary: "Invalid JSON number",
|
Summary: "Invalid JSON number",
|
||||||
Detail: fmt.Sprintf("There is a syntax error in the given JSON number."),
|
Detail: "There is a syntax error in the given JSON number.",
|
||||||
Subject: &tok.Range,
|
Subject: &tok.Range,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -394,7 +385,7 @@ func parseNumber(p *peeker) (node, hcl.Diagnostics) {
|
|||||||
{
|
{
|
||||||
Severity: hcl.DiagError,
|
Severity: hcl.DiagError,
|
||||||
Summary: "Invalid JSON number",
|
Summary: "Invalid JSON number",
|
||||||
Detail: fmt.Sprintf("There is a syntax error in the given JSON number."),
|
Detail: "There is a syntax error in the given JSON number.",
|
||||||
Subject: &tok.Range,
|
Subject: &tok.Range,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -448,7 +439,7 @@ func parseString(p *peeker) (node, hcl.Diagnostics) {
|
|||||||
{
|
{
|
||||||
Severity: hcl.DiagError,
|
Severity: hcl.DiagError,
|
||||||
Summary: "Invalid JSON string",
|
Summary: "Invalid JSON string",
|
||||||
Detail: fmt.Sprintf("There is a syntax error in the given JSON string."),
|
Detail: "There is a syntax error in the given JSON string.",
|
||||||
Subject: &errRange,
|
Subject: &errRange,
|
||||||
Context: contextRange,
|
Context: contextRange,
|
||||||
},
|
},
|
||||||
|
|||||||
+19
-11
@@ -5,7 +5,7 @@ package json
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/hashicorp/hcl/v2"
|
"github.com/hashicorp/hcl/v2"
|
||||||
@@ -92,20 +92,28 @@ func ParseExpressionWithStartPos(src []byte, filename string, start hcl.Pos) (hc
|
|||||||
// data from the given filename, passing the result to Parse if successful.
|
// data from the given filename, passing the result to Parse if successful.
|
||||||
//
|
//
|
||||||
// If the file cannot be read, an error diagnostic with nil context is returned.
|
// If the file cannot be read, an error diagnostic with nil context is returned.
|
||||||
func ParseFile(filename string) (*hcl.File, hcl.Diagnostics) {
|
func ParseFile(filename string) (rf *hcl.File, diags hcl.Diagnostics) {
|
||||||
f, err := os.Open(filename)
|
f, err := os.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, hcl.Diagnostics{
|
diags = append(diags, &hcl.Diagnostic{
|
||||||
{
|
Severity: hcl.DiagError,
|
||||||
Severity: hcl.DiagError,
|
Summary: "Failed to open file",
|
||||||
Summary: "Failed to open file",
|
Detail: fmt.Sprintf("The file %q could not be opened.", filename),
|
||||||
Detail: fmt.Sprintf("The file %q could not be opened.", filename),
|
})
|
||||||
},
|
return
|
||||||
}
|
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer func() {
|
||||||
|
err := f.Close()
|
||||||
|
if err != nil {
|
||||||
|
diags = append(diags, &hcl.Diagnostic{
|
||||||
|
Severity: hcl.DiagWarning,
|
||||||
|
Summary: "Failed to close file",
|
||||||
|
Detail: fmt.Sprintf("The file %q was opened, but an error occured while closing it.", filename),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
src, err := ioutil.ReadAll(f)
|
src, err := io.ReadAll(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, hcl.Diagnostics{
|
return nil, hcl.Diagnostics{
|
||||||
{
|
{
|
||||||
|
|||||||
+14
-16
@@ -107,23 +107,21 @@ func (mb mergedBodies) JustAttributes() (Attributes, Diagnostics) {
|
|||||||
diags = append(diags, thisDiags...)
|
diags = append(diags, thisDiags...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if thisAttrs != nil {
|
for name, attr := range thisAttrs {
|
||||||
for name, attr := range thisAttrs {
|
if existing := attrs[name]; existing != nil {
|
||||||
if existing := attrs[name]; existing != nil {
|
diags = diags.Append(&Diagnostic{
|
||||||
diags = diags.Append(&Diagnostic{
|
Severity: DiagError,
|
||||||
Severity: DiagError,
|
Summary: "Duplicate argument",
|
||||||
Summary: "Duplicate argument",
|
Detail: fmt.Sprintf(
|
||||||
Detail: fmt.Sprintf(
|
"Argument %q was already set at %s",
|
||||||
"Argument %q was already set at %s",
|
name, existing.NameRange.String(),
|
||||||
name, existing.NameRange.String(),
|
),
|
||||||
),
|
Subject: &attr.NameRange,
|
||||||
Subject: &attr.NameRange,
|
})
|
||||||
})
|
continue
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
attrs[name] = attr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
attrs[name] = attr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-3
@@ -195,9 +195,6 @@ func Index(collection, key cty.Value, srcRange *Range) (cty.Value, Diagnostics)
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !collection.IsKnown() {
|
|
||||||
return cty.DynamicVal.WithSameMarks(collection), nil
|
|
||||||
}
|
|
||||||
if !key.IsKnown() {
|
if !key.IsKnown() {
|
||||||
return cty.DynamicVal.WithSameMarks(collection), nil
|
return cty.DynamicVal.WithSameMarks(collection), nil
|
||||||
}
|
}
|
||||||
@@ -225,6 +222,10 @@ func Index(collection, key cty.Value, srcRange *Range) (cty.Value, Diagnostics)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !collection.IsKnown() {
|
||||||
|
return cty.UnknownVal(ty.AttributeType(attrName)).WithSameMarks(collection), nil
|
||||||
|
}
|
||||||
|
|
||||||
return collection.GetAttr(attrName), nil
|
return collection.GetAttr(attrName), nil
|
||||||
|
|
||||||
case ty.IsSetType():
|
case ty.IsSetType():
|
||||||
|
|||||||
+11
-11
@@ -74,7 +74,7 @@ func RelTraversalForExpr(expr Expression) (Traversal, Diagnostics) {
|
|||||||
// For example, the following attribute has an expression that would produce
|
// For example, the following attribute has an expression that would produce
|
||||||
// the keyword "foo":
|
// the keyword "foo":
|
||||||
//
|
//
|
||||||
// example = foo
|
// example = foo
|
||||||
//
|
//
|
||||||
// This function is a variant of AbsTraversalForExpr, which uses the same
|
// This function is a variant of AbsTraversalForExpr, which uses the same
|
||||||
// interface on the given expression. This helper constrains the result
|
// interface on the given expression. This helper constrains the result
|
||||||
@@ -84,16 +84,16 @@ func RelTraversalForExpr(expr Expression) (Traversal, Diagnostics) {
|
|||||||
// situations where one of a fixed set of keywords is required and arbitrary
|
// situations where one of a fixed set of keywords is required and arbitrary
|
||||||
// expressions are not allowed:
|
// expressions are not allowed:
|
||||||
//
|
//
|
||||||
// switch hcl.ExprAsKeyword(expr) {
|
// switch hcl.ExprAsKeyword(expr) {
|
||||||
// case "allow":
|
// case "allow":
|
||||||
// // (take suitable action for keyword "allow")
|
// // (take suitable action for keyword "allow")
|
||||||
// case "deny":
|
// case "deny":
|
||||||
// // (take suitable action for keyword "deny")
|
// // (take suitable action for keyword "deny")
|
||||||
// default:
|
// default:
|
||||||
// diags = append(diags, &hcl.Diagnostic{
|
// diags = append(diags, &hcl.Diagnostic{
|
||||||
// // ... "invalid keyword" diagnostic message ...
|
// // ... "invalid keyword" diagnostic message ...
|
||||||
// })
|
// })
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// The above approach will generate the same message for both the use of an
|
// The above approach will generate the same message for both the use of an
|
||||||
// unrecognized keyword and for not using a keyword at all, which is usually
|
// unrecognized keyword and for not using a keyword at all, which is usually
|
||||||
|
|||||||
+18
-8
@@ -5,6 +5,8 @@ import (
|
|||||||
"unicode"
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const nbsp = 0xA0
|
||||||
|
|
||||||
// WrapString wraps the given string within lim width in characters.
|
// WrapString wraps the given string within lim width in characters.
|
||||||
//
|
//
|
||||||
// Wrapping is currently naive and only happens at white-space. A future
|
// Wrapping is currently naive and only happens at white-space. A future
|
||||||
@@ -18,50 +20,58 @@ func WrapString(s string, lim uint) string {
|
|||||||
|
|
||||||
var current uint
|
var current uint
|
||||||
var wordBuf, spaceBuf bytes.Buffer
|
var wordBuf, spaceBuf bytes.Buffer
|
||||||
|
var wordBufLen, spaceBufLen uint
|
||||||
|
|
||||||
for _, char := range s {
|
for _, char := range s {
|
||||||
if char == '\n' {
|
if char == '\n' {
|
||||||
if wordBuf.Len() == 0 {
|
if wordBuf.Len() == 0 {
|
||||||
if current+uint(spaceBuf.Len()) > lim {
|
if current+spaceBufLen > lim {
|
||||||
current = 0
|
current = 0
|
||||||
} else {
|
} else {
|
||||||
current += uint(spaceBuf.Len())
|
current += spaceBufLen
|
||||||
spaceBuf.WriteTo(buf)
|
spaceBuf.WriteTo(buf)
|
||||||
}
|
}
|
||||||
spaceBuf.Reset()
|
spaceBuf.Reset()
|
||||||
|
spaceBufLen = 0
|
||||||
} else {
|
} else {
|
||||||
current += uint(spaceBuf.Len() + wordBuf.Len())
|
current += spaceBufLen + wordBufLen
|
||||||
spaceBuf.WriteTo(buf)
|
spaceBuf.WriteTo(buf)
|
||||||
spaceBuf.Reset()
|
spaceBuf.Reset()
|
||||||
|
spaceBufLen = 0
|
||||||
wordBuf.WriteTo(buf)
|
wordBuf.WriteTo(buf)
|
||||||
wordBuf.Reset()
|
wordBuf.Reset()
|
||||||
|
wordBufLen = 0
|
||||||
}
|
}
|
||||||
buf.WriteRune(char)
|
buf.WriteRune(char)
|
||||||
current = 0
|
current = 0
|
||||||
} else if unicode.IsSpace(char) {
|
} else if unicode.IsSpace(char) && char != nbsp {
|
||||||
if spaceBuf.Len() == 0 || wordBuf.Len() > 0 {
|
if spaceBuf.Len() == 0 || wordBuf.Len() > 0 {
|
||||||
current += uint(spaceBuf.Len() + wordBuf.Len())
|
current += spaceBufLen + wordBufLen
|
||||||
spaceBuf.WriteTo(buf)
|
spaceBuf.WriteTo(buf)
|
||||||
spaceBuf.Reset()
|
spaceBuf.Reset()
|
||||||
|
spaceBufLen = 0
|
||||||
wordBuf.WriteTo(buf)
|
wordBuf.WriteTo(buf)
|
||||||
wordBuf.Reset()
|
wordBuf.Reset()
|
||||||
|
wordBufLen = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
spaceBuf.WriteRune(char)
|
spaceBuf.WriteRune(char)
|
||||||
|
spaceBufLen++
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
wordBuf.WriteRune(char)
|
wordBuf.WriteRune(char)
|
||||||
|
wordBufLen++
|
||||||
|
|
||||||
if current+uint(spaceBuf.Len()+wordBuf.Len()) > lim && uint(wordBuf.Len()) < lim {
|
if current+wordBufLen+spaceBufLen > lim && wordBufLen < lim {
|
||||||
buf.WriteRune('\n')
|
buf.WriteRune('\n')
|
||||||
current = 0
|
current = 0
|
||||||
spaceBuf.Reset()
|
spaceBuf.Reset()
|
||||||
|
spaceBufLen = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if wordBuf.Len() == 0 {
|
if wordBuf.Len() == 0 {
|
||||||
if current+uint(spaceBuf.Len()) <= lim {
|
if current+spaceBufLen <= lim {
|
||||||
spaceBuf.WriteTo(buf)
|
spaceBuf.WriteTo(buf)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+8
-8
@@ -14,7 +14,7 @@ type CapsuleOps struct {
|
|||||||
// corresponding type. Conventionally this should return a string
|
// corresponding type. Conventionally this should return a string
|
||||||
// representation of an expression that would produce an equivalent
|
// representation of an expression that would produce an equivalent
|
||||||
// value.
|
// value.
|
||||||
GoString func(val interface{}) string
|
GoString func(val any) string
|
||||||
|
|
||||||
// TypeGoString provides the GoString implementation for the corresponding
|
// TypeGoString provides the GoString implementation for the corresponding
|
||||||
// capsule type itself.
|
// capsule type itself.
|
||||||
@@ -37,7 +37,7 @@ type CapsuleOps struct {
|
|||||||
//
|
//
|
||||||
// If RawEquals is nil then Equals must also be nil, selecting the default
|
// If RawEquals is nil then Equals must also be nil, selecting the default
|
||||||
// pointer-identity comparison instead.
|
// pointer-identity comparison instead.
|
||||||
Equals func(a, b interface{}) Value
|
Equals func(a, b any) Value
|
||||||
|
|
||||||
// RawEquals provides the implementation of the RawEquals operation.
|
// RawEquals provides the implementation of the RawEquals operation.
|
||||||
// This is called only with known, non-null values of the corresponding
|
// This is called only with known, non-null values of the corresponding
|
||||||
@@ -47,7 +47,7 @@ type CapsuleOps struct {
|
|||||||
//
|
//
|
||||||
// If RawEquals is nil, values of the corresponding type are compared by
|
// If RawEquals is nil, values of the corresponding type are compared by
|
||||||
// pointer identity of the encapsulated value.
|
// pointer identity of the encapsulated value.
|
||||||
RawEquals func(a, b interface{}) bool
|
RawEquals func(a, b any) bool
|
||||||
|
|
||||||
// HashKey provides a hashing function for values of the corresponding
|
// HashKey provides a hashing function for values of the corresponding
|
||||||
// capsule type. If defined, cty will use the resulting hashes as part
|
// capsule type. If defined, cty will use the resulting hashes as part
|
||||||
@@ -59,7 +59,7 @@ type CapsuleOps struct {
|
|||||||
// RawEquals to return true when given those values. If a given type
|
// RawEquals to return true when given those values. If a given type
|
||||||
// does not uphold that assumption then sets including this type will
|
// does not uphold that assumption then sets including this type will
|
||||||
// not behave correctly.
|
// not behave correctly.
|
||||||
HashKey func(v interface{}) string
|
HashKey func(v any) string
|
||||||
|
|
||||||
// ConversionFrom can provide conversions from the corresponding type to
|
// ConversionFrom can provide conversions from the corresponding type to
|
||||||
// some other type when values of the corresponding type are used with
|
// some other type when values of the corresponding type are used with
|
||||||
@@ -68,7 +68,7 @@ type CapsuleOps struct {
|
|||||||
// This function itself returns a function, allowing it to switch its
|
// This function itself returns a function, allowing it to switch its
|
||||||
// behavior depending on the given source type. Return nil to indicate
|
// behavior depending on the given source type. Return nil to indicate
|
||||||
// that no such conversion is available.
|
// that no such conversion is available.
|
||||||
ConversionFrom func(src Type) func(interface{}, Path) (Value, error)
|
ConversionFrom func(src Type) func(any, Path) (Value, error)
|
||||||
|
|
||||||
// ConversionTo can provide conversions to the corresponding type from
|
// ConversionTo can provide conversions to the corresponding type from
|
||||||
// some other type when values of the corresponding type are used with
|
// some other type when values of the corresponding type are used with
|
||||||
@@ -77,7 +77,7 @@ type CapsuleOps struct {
|
|||||||
// This function itself returns a function, allowing it to switch its
|
// This function itself returns a function, allowing it to switch its
|
||||||
// behavior depending on the given destination type. Return nil to indicate
|
// behavior depending on the given destination type. Return nil to indicate
|
||||||
// that no such conversion is available.
|
// that no such conversion is available.
|
||||||
ConversionTo func(dst Type) func(Value, Path) (interface{}, error)
|
ConversionTo func(dst Type) func(Value, Path) (any, error)
|
||||||
|
|
||||||
// ExtensionData is an extension point for applications that wish to
|
// ExtensionData is an extension point for applications that wish to
|
||||||
// create their own extension features using capsule types.
|
// create their own extension features using capsule types.
|
||||||
@@ -99,7 +99,7 @@ type CapsuleOps struct {
|
|||||||
// should do so defensively: if the result of ExtensionData is not valid,
|
// should do so defensively: if the result of ExtensionData is not valid,
|
||||||
// prefer to ignore it or gracefully produce an error rather than causing
|
// prefer to ignore it or gracefully produce an error rather than causing
|
||||||
// a panic.
|
// a panic.
|
||||||
ExtensionData func(key interface{}) interface{}
|
ExtensionData func(key any) any
|
||||||
}
|
}
|
||||||
|
|
||||||
// noCapsuleOps is a pointer to a CapsuleOps with no functions set, which
|
// noCapsuleOps is a pointer to a CapsuleOps with no functions set, which
|
||||||
@@ -135,7 +135,7 @@ func (ty Type) CapsuleOps() *CapsuleOps {
|
|||||||
// on the purpose of and usage of this mechanism.
|
// on the purpose of and usage of this mechanism.
|
||||||
//
|
//
|
||||||
// If CapsuleExtensionData is called on a non-capsule type then it will panic.
|
// If CapsuleExtensionData is called on a non-capsule type then it will panic.
|
||||||
func (ty Type) CapsuleExtensionData(key interface{}) interface{} {
|
func (ty Type) CapsuleExtensionData(key any) any {
|
||||||
ops := ty.CapsuleOps()
|
ops := ty.CapsuleOps()
|
||||||
if ops.ExtensionData == nil {
|
if ops.ExtensionData == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
+2
-2
@@ -4,7 +4,7 @@ import (
|
|||||||
"github.com/zclconf/go-cty/cty"
|
"github.com/zclconf/go-cty/cty"
|
||||||
)
|
)
|
||||||
|
|
||||||
func conversionToCapsule(inTy, outTy cty.Type, fn func(inTy cty.Type) func(cty.Value, cty.Path) (interface{}, error)) conversion {
|
func conversionToCapsule(inTy, outTy cty.Type, fn func(inTy cty.Type) func(cty.Value, cty.Path) (any, error)) conversion {
|
||||||
rawConv := fn(inTy)
|
rawConv := fn(inTy)
|
||||||
if rawConv == nil {
|
if rawConv == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -19,7 +19,7 @@ func conversionToCapsule(inTy, outTy cty.Type, fn func(inTy cty.Type) func(cty.V
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func conversionFromCapsule(inTy, outTy cty.Type, fn func(outTy cty.Type) func(interface{}, cty.Path) (cty.Value, error)) conversion {
|
func conversionFromCapsule(inTy, outTy cty.Type, fn func(outTy cty.Type) func(any, cty.Path) (cty.Value, error)) conversion {
|
||||||
rawConv := fn(outTy)
|
rawConv := fn(outTy)
|
||||||
if rawConv == nil {
|
if rawConv == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
+55
@@ -53,6 +53,9 @@ func MismatchMessage(got, want cty.Type) string {
|
|||||||
case got.IsCollectionType() && want.IsCollectionType():
|
case got.IsCollectionType() && want.IsCollectionType():
|
||||||
return mismatchMessageCollectionsFromCollections(got, want)
|
return mismatchMessageCollectionsFromCollections(got, want)
|
||||||
|
|
||||||
|
case !typesAreLikelyToCauseConfusion(got, want):
|
||||||
|
return fmt.Sprintf("%s required, but have %s", want.FriendlyName(), got.FriendlyName())
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// If we have nothing better to say, we'll just state what was required.
|
// If we have nothing better to say, we'll just state what was required.
|
||||||
return want.FriendlyNameForConstraint() + " required"
|
return want.FriendlyNameForConstraint() + " required"
|
||||||
@@ -224,3 +227,55 @@ func mismatchMessageCollectionsFromCollections(got, want cty.Type) string {
|
|||||||
}
|
}
|
||||||
return fmt.Sprintf("incorrect %s: %s", noun, MismatchMessage(gotEty, wantEty))
|
return fmt.Sprintf("incorrect %s: %s", noun, MismatchMessage(gotEty, wantEty))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func typesAreLikelyToCauseConfusion(got, want cty.Type) bool {
|
||||||
|
// NOTE: This function is intended to be used as the penultemate test
|
||||||
|
// in MismatchMessage, and so it intentionally does not address
|
||||||
|
// combinations that are already addressed by the more specific
|
||||||
|
// earlier tests.
|
||||||
|
if gotP, wantP := got.IsPrimitiveType(), want.IsPrimitiveType(); gotP != wantP {
|
||||||
|
// There's never any situation where a primitive type could successfully
|
||||||
|
// convert to a non-primitive one, so this is very unlikely to be
|
||||||
|
// intended as an automatic type conversion. This case is doing most
|
||||||
|
// of the useful work of this function, allowing us to report such
|
||||||
|
// things as "string required, but got tuple".
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if want == cty.String {
|
||||||
|
// All of the primitive types can always successfully convert to
|
||||||
|
// string, regardless of value.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (want == cty.Bool && got == cty.Number) || (got == cty.Bool && want == cty.Number) {
|
||||||
|
// There are no automatic conversions between bool and number, so
|
||||||
|
// describing both directions of this is unlikely to cause confusion.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if got.IsCollectionType() && want.IsCollectionType() && !got.ElementType().HasDynamicTypes() && want.ElementType().HasDynamicTypes() {
|
||||||
|
// Describing mismatches of collection element types is helpful
|
||||||
|
// when we have specific types to report, but confusing when
|
||||||
|
// there are any inexact types involved because readers tend to
|
||||||
|
// then think the inexactness of the element type is the problem,
|
||||||
|
// when it's far more likely to be the collection type kind that's
|
||||||
|
// causing the problem.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if got.IsTupleType() && want.IsObjectType() || got.IsObjectType() && got.IsTupleType() {
|
||||||
|
// The structural type kinds get described just by their bare names,
|
||||||
|
// "tuple" or "object", and so they are not confusing when they
|
||||||
|
// differ. They _are_ potentially confusing when we have matching kinds
|
||||||
|
// but different element types within, but thankfully that's most
|
||||||
|
// often handled by one of the earlier cases in MismatchMessage.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// We are intentionally a little more vague for string-to-number and
|
||||||
|
// string-to-bool conversions because the rules for those are a little
|
||||||
|
// too subtle to be described purely as "need X but want Y", and historical
|
||||||
|
// experience shows that describing it that way causes folks to focus
|
||||||
|
// on the wrong problem (their value is wrong, not their type).
|
||||||
|
|
||||||
|
// If we find that we're getting here in additional specific cases where
|
||||||
|
// it would be more helpful than confusing to report both types then
|
||||||
|
// we'll add additional rules here in a future version.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
// Package ctymarks contains some ancillary types, values, and constants for
|
||||||
|
// use with mark-related parts of the main cty package.
|
||||||
|
package ctymarks
|
||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
package ctymarks
|
||||||
|
|
||||||
|
// WrangleAction values each describe an action to be taken for a particular
|
||||||
|
// mark at a particular path that has been visited by a [WrangleFunc].
|
||||||
|
//
|
||||||
|
// A nil value of this type represents taking no action at all, and thereby
|
||||||
|
// potentially allowing other later functions to decide an action instead.
|
||||||
|
type WrangleAction interface {
|
||||||
|
wrangleAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
type simpleWrangleAction rune
|
||||||
|
|
||||||
|
func (w simpleWrangleAction) wrangleAction() {}
|
||||||
|
|
||||||
|
// WrangleKeep is a [WrangleAction] that indicates that the given mark
|
||||||
|
// should be retained at the given path and that no subsequent wrangle functions
|
||||||
|
// in the same operation should be given an opportunity to visit the same mark.
|
||||||
|
const WrangleKeep = simpleWrangleAction('k')
|
||||||
|
|
||||||
|
// WrangleDrop is a [WrangleAction] that indicates that the given mark
|
||||||
|
// should be dropped at the given path, and that no subsequent wrangle functions
|
||||||
|
// in the same operation should be given an opportunity to visit the same mark.
|
||||||
|
const WrangleDrop = simpleWrangleAction('d')
|
||||||
|
|
||||||
|
// WrangleExpand is a [WrangleAction] that indicates that the given mark
|
||||||
|
// should be transferred to the top-level value that the current mark wrangling
|
||||||
|
// process is operating on.
|
||||||
|
//
|
||||||
|
// This effectively means that the mark then applies to all parts of the
|
||||||
|
// original top-level value, rather than to only a small part of the nested
|
||||||
|
// data structure within it.
|
||||||
|
const WrangleExpand = simpleWrangleAction('e')
|
||||||
|
|
||||||
|
// WrangleReplace returns a [WrangleAction] that indicates that the given
|
||||||
|
// mark should be removed and the given mark inserted in its place.
|
||||||
|
//
|
||||||
|
// This could be useful when values must cross between systems that use
|
||||||
|
// different marks to represent similar concepts, for example.
|
||||||
|
func WrangleReplace(newMark any) WrangleAction {
|
||||||
|
if newMark == nil {
|
||||||
|
panic("ctymarks.WrangleReplace with nil mark")
|
||||||
|
}
|
||||||
|
return wrangleReplaceAction{newMark}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrangleReplaceMark returns the new mark for the given action if it was
|
||||||
|
// created using [WrangleReplace], or nil if it is any other kind of action.
|
||||||
|
func WrangleReplaceMark(action WrangleAction) any {
|
||||||
|
replace, _ := action.(wrangleReplaceAction)
|
||||||
|
return replace.newMark
|
||||||
|
}
|
||||||
|
|
||||||
|
type wrangleReplaceAction struct {
|
||||||
|
newMark any
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w wrangleReplaceAction) wrangleAction() {}
|
||||||
+15
-15
@@ -11,11 +11,11 @@ import (
|
|||||||
//
|
//
|
||||||
// Its usage pattern is as follows:
|
// Its usage pattern is as follows:
|
||||||
//
|
//
|
||||||
// it := val.ElementIterator()
|
// it := val.ElementIterator()
|
||||||
// for it.Next() {
|
// for it.Next() {
|
||||||
// key, val := it.Element()
|
// key, val := it.Element()
|
||||||
// // ...
|
// // ...
|
||||||
// }
|
// }
|
||||||
type ElementIterator interface {
|
type ElementIterator interface {
|
||||||
Next() bool
|
Next() bool
|
||||||
Element() (key Value, value Value)
|
Element() (key Value, value Value)
|
||||||
@@ -46,13 +46,13 @@ func elementIterator(val Value) ElementIterator {
|
|||||||
case val.ty.IsListType():
|
case val.ty.IsListType():
|
||||||
return &listElementIterator{
|
return &listElementIterator{
|
||||||
ety: val.ty.ElementType(),
|
ety: val.ty.ElementType(),
|
||||||
vals: val.v.([]interface{}),
|
vals: val.v.([]any),
|
||||||
idx: -1,
|
idx: -1,
|
||||||
}
|
}
|
||||||
case val.ty.IsMapType():
|
case val.ty.IsMapType():
|
||||||
// We iterate the keys in a predictable lexicographical order so
|
// We iterate the keys in a predictable lexicographical order so
|
||||||
// that results will always be stable given the same input map.
|
// that results will always be stable given the same input map.
|
||||||
rawMap := val.v.(map[string]interface{})
|
rawMap := val.v.(map[string]any)
|
||||||
keys := make([]string, 0, len(rawMap))
|
keys := make([]string, 0, len(rawMap))
|
||||||
for key := range rawMap {
|
for key := range rawMap {
|
||||||
keys = append(keys, key)
|
keys = append(keys, key)
|
||||||
@@ -66,7 +66,7 @@ func elementIterator(val Value) ElementIterator {
|
|||||||
idx: -1,
|
idx: -1,
|
||||||
}
|
}
|
||||||
case val.ty.IsSetType():
|
case val.ty.IsSetType():
|
||||||
rawSet := val.v.(set.Set[interface{}])
|
rawSet := val.v.(set.Set[any])
|
||||||
return &setElementIterator{
|
return &setElementIterator{
|
||||||
ety: val.ty.ElementType(),
|
ety: val.ty.ElementType(),
|
||||||
setIt: rawSet.Iterator(),
|
setIt: rawSet.Iterator(),
|
||||||
@@ -74,7 +74,7 @@ func elementIterator(val Value) ElementIterator {
|
|||||||
case val.ty.IsTupleType():
|
case val.ty.IsTupleType():
|
||||||
return &tupleElementIterator{
|
return &tupleElementIterator{
|
||||||
etys: val.ty.TupleElementTypes(),
|
etys: val.ty.TupleElementTypes(),
|
||||||
vals: val.v.([]interface{}),
|
vals: val.v.([]any),
|
||||||
idx: -1,
|
idx: -1,
|
||||||
}
|
}
|
||||||
case val.ty.IsObjectType():
|
case val.ty.IsObjectType():
|
||||||
@@ -89,7 +89,7 @@ func elementIterator(val Value) ElementIterator {
|
|||||||
|
|
||||||
return &objectElementIterator{
|
return &objectElementIterator{
|
||||||
atys: atys,
|
atys: atys,
|
||||||
vals: val.v.(map[string]interface{}),
|
vals: val.v.(map[string]any),
|
||||||
attrNames: keys,
|
attrNames: keys,
|
||||||
idx: -1,
|
idx: -1,
|
||||||
}
|
}
|
||||||
@@ -100,7 +100,7 @@ func elementIterator(val Value) ElementIterator {
|
|||||||
|
|
||||||
type listElementIterator struct {
|
type listElementIterator struct {
|
||||||
ety Type
|
ety Type
|
||||||
vals []interface{}
|
vals []any
|
||||||
idx int
|
idx int
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +119,7 @@ func (it *listElementIterator) Next() bool {
|
|||||||
|
|
||||||
type mapElementIterator struct {
|
type mapElementIterator struct {
|
||||||
ety Type
|
ety Type
|
||||||
vals map[string]interface{}
|
vals map[string]any
|
||||||
keys []string
|
keys []string
|
||||||
idx int
|
idx int
|
||||||
}
|
}
|
||||||
@@ -139,7 +139,7 @@ func (it *mapElementIterator) Next() bool {
|
|||||||
|
|
||||||
type setElementIterator struct {
|
type setElementIterator struct {
|
||||||
ety Type
|
ety Type
|
||||||
setIt *set.Iterator[interface{}]
|
setIt *set.Iterator[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *setElementIterator) Element() (Value, Value) {
|
func (it *setElementIterator) Element() (Value, Value) {
|
||||||
@@ -156,7 +156,7 @@ func (it *setElementIterator) Next() bool {
|
|||||||
|
|
||||||
type tupleElementIterator struct {
|
type tupleElementIterator struct {
|
||||||
etys []Type
|
etys []Type
|
||||||
vals []interface{}
|
vals []any
|
||||||
idx int
|
idx int
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,7 +175,7 @@ func (it *tupleElementIterator) Next() bool {
|
|||||||
|
|
||||||
type objectElementIterator struct {
|
type objectElementIterator struct {
|
||||||
atys map[string]Type
|
atys map[string]Type
|
||||||
vals map[string]interface{}
|
vals map[string]any
|
||||||
attrNames []string
|
attrNames []string
|
||||||
idx int
|
idx int
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -11,7 +11,7 @@ type PathError struct {
|
|||||||
Path Path
|
Path Path
|
||||||
}
|
}
|
||||||
|
|
||||||
func errorf(path Path, f string, args ...interface{}) error {
|
func errorf(path Path, f string, args ...any) error {
|
||||||
// We need to copy the Path because often our caller builds it by
|
// We need to copy the Path because often our caller builds it by
|
||||||
// continually mutating the same underlying buffer.
|
// continually mutating the same underlying buffer.
|
||||||
sPath := make(Path, len(path))
|
sPath := make(Path, len(path))
|
||||||
@@ -25,7 +25,7 @@ func errorf(path Path, f string, args ...interface{}) error {
|
|||||||
// NewErrorf creates a new PathError for the current path by passing the
|
// NewErrorf creates a new PathError for the current path by passing the
|
||||||
// given format and arguments to fmt.Errorf and then wrapping the result
|
// given format and arguments to fmt.Errorf and then wrapping the result
|
||||||
// similarly to NewError.
|
// similarly to NewError.
|
||||||
func (p Path) NewErrorf(f string, args ...interface{}) error {
|
func (p Path) NewErrorf(f string, args ...any) error {
|
||||||
return errorf(p, f, args...)
|
return errorf(p, f, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -15,7 +15,7 @@ type ArgError struct {
|
|||||||
Index int
|
Index int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewArgErrorf(i int, f string, args ...interface{}) error {
|
func NewArgErrorf(i int, f string, args ...any) error {
|
||||||
return ArgError{
|
return ArgError{
|
||||||
error: fmt.Errorf(f, args...),
|
error: fmt.Errorf(f, args...),
|
||||||
Index: i,
|
Index: i,
|
||||||
@@ -34,11 +34,11 @@ func NewArgError(i int, err error) error {
|
|||||||
// into a normal error so that callers (expected to be language runtimes)
|
// into a normal error so that callers (expected to be language runtimes)
|
||||||
// are freed from having to deal with panics in buggy functions.
|
// are freed from having to deal with panics in buggy functions.
|
||||||
type PanicError struct {
|
type PanicError struct {
|
||||||
Value interface{}
|
Value any
|
||||||
Stack []byte
|
Stack []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func errorForPanic(val interface{}) error {
|
func errorForPanic(val any) error {
|
||||||
return PanicError{
|
return PanicError{
|
||||||
Value: val,
|
Value: val,
|
||||||
Stack: debug.Stack(),
|
Stack: debug.Stack(),
|
||||||
|
|||||||
+2
-2
@@ -11,12 +11,12 @@ import (
|
|||||||
var valueType = reflect.TypeOf(cty.Value{})
|
var valueType = reflect.TypeOf(cty.Value{})
|
||||||
var typeType = reflect.TypeOf(cty.Type{})
|
var typeType = reflect.TypeOf(cty.Type{})
|
||||||
|
|
||||||
var setType = reflect.TypeOf(set.Set[interface{}]{})
|
var setType = reflect.TypeOf(set.Set[any]{})
|
||||||
|
|
||||||
var bigFloatType = reflect.TypeOf(big.Float{})
|
var bigFloatType = reflect.TypeOf(big.Float{})
|
||||||
var bigIntType = reflect.TypeOf(big.Int{})
|
var bigIntType = reflect.TypeOf(big.Int{})
|
||||||
|
|
||||||
var emptyInterfaceType = reflect.TypeOf(interface{}(nil))
|
var emptyInterfaceType = reflect.TypeOf(any(nil))
|
||||||
|
|
||||||
var stringType = reflect.TypeOf("")
|
var stringType = reflect.TypeOf("")
|
||||||
|
|
||||||
|
|||||||
+6
-6
@@ -23,7 +23,7 @@ import (
|
|||||||
// presented from Go's perspective. These messages are thus not appropriate
|
// presented from Go's perspective. These messages are thus not appropriate
|
||||||
// for display to end-users. An error returned from ToCtyValue represents a
|
// for display to end-users. An error returned from ToCtyValue represents a
|
||||||
// bug in the calling program, not user error.
|
// bug in the calling program, not user error.
|
||||||
func ToCtyValue(val interface{}, ty cty.Type) (cty.Value, error) {
|
func ToCtyValue(val any, ty cty.Type) (cty.Value, error) {
|
||||||
// 'path' starts off as empty but will grow for each level of recursive
|
// 'path' starts off as empty but will grow for each level of recursive
|
||||||
// call we make, so by the time toCtyValue returns it is likely to have
|
// call we make, so by the time toCtyValue returns it is likely to have
|
||||||
// unused capacity on the end of it, depending on how deeply-recursive
|
// unused capacity on the end of it, depending on how deeply-recursive
|
||||||
@@ -268,7 +268,7 @@ func toCtySet(val reflect.Value, ety cty.Type, path cty.Path) (cty.Value, error)
|
|||||||
return cty.NilVal, path.NewErrorf("can't convert Go %s to %#v", val.Type(), cty.Set(ety))
|
return cty.NilVal, path.NewErrorf("can't convert Go %s to %#v", val.Type(), cty.Set(ety))
|
||||||
}
|
}
|
||||||
|
|
||||||
rawSet := val.Interface().(set.Set[interface{}])
|
rawSet := val.Interface().(set.Set[any])
|
||||||
inVals := rawSet.Values()
|
inVals := rawSet.Values()
|
||||||
|
|
||||||
if len(inVals) == 0 {
|
if len(inVals) == 0 {
|
||||||
@@ -528,10 +528,10 @@ func toCtyPassthrough(wrappedVal reflect.Value, wantTy cty.Type, path cty.Path)
|
|||||||
// toCtyUnwrapPointer is a helper for dealing with Go pointers. It has three
|
// toCtyUnwrapPointer is a helper for dealing with Go pointers. It has three
|
||||||
// possible outcomes:
|
// possible outcomes:
|
||||||
//
|
//
|
||||||
// - Given value isn't a pointer, so it's just returned as-is.
|
// - Given value isn't a pointer, so it's just returned as-is.
|
||||||
// - Given value is a non-nil pointer, in which case it is dereferenced
|
// - Given value is a non-nil pointer, in which case it is dereferenced
|
||||||
// and the result returned.
|
// and the result returned.
|
||||||
// - Given value is a nil pointer, in which case an invalid value is returned.
|
// - Given value is a nil pointer, in which case an invalid value is returned.
|
||||||
//
|
//
|
||||||
// For nested pointer types, like **int, they are all dereferenced in turn
|
// For nested pointer types, like **int, they are all dereferenced in turn
|
||||||
// until a non-pointer value is found, or until a nil pointer is encountered.
|
// until a non-pointer value is found, or until a nil pointer is encountered.
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@ import (
|
|||||||
//
|
//
|
||||||
// The function will panic if given a non-pointer as the Go value target,
|
// The function will panic if given a non-pointer as the Go value target,
|
||||||
// since that is considered to be a bug in the calling program.
|
// since that is considered to be a bug in the calling program.
|
||||||
func FromCtyValue(val cty.Value, target interface{}) error {
|
func FromCtyValue(val cty.Value, target any) error {
|
||||||
tVal := reflect.ValueOf(target)
|
tVal := reflect.ValueOf(target)
|
||||||
if tVal.Kind() != reflect.Ptr {
|
if tVal.Kind() != reflect.Ptr {
|
||||||
panic("target value is not a pointer")
|
panic("target value is not a pointer")
|
||||||
|
|||||||
+2
-2
@@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/zclconf/go-cty/cty"
|
"github.com/zclconf/go-cty/cty"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ImpliedType takes an arbitrary Go value (as an interface{}) and attempts
|
// ImpliedType takes an arbitrary Go value (as an any) and attempts
|
||||||
// to find a suitable cty.Type instance that could be used for a conversion
|
// to find a suitable cty.Type instance that could be used for a conversion
|
||||||
// with ToCtyValue.
|
// with ToCtyValue.
|
||||||
//
|
//
|
||||||
@@ -21,7 +21,7 @@ import (
|
|||||||
// In particular, ImpliedType will never use capsule types in its returned
|
// In particular, ImpliedType will never use capsule types in its returned
|
||||||
// type, because it cannot know the capsule types supported by the calling
|
// type, because it cannot know the capsule types supported by the calling
|
||||||
// program.
|
// program.
|
||||||
func ImpliedType(gv interface{}) (cty.Type, error) {
|
func ImpliedType(gv any) (cty.Type, error) {
|
||||||
rt := reflect.TypeOf(gv)
|
rt := reflect.TypeOf(gv)
|
||||||
var path cty.Path
|
var path cty.Path
|
||||||
return impliedType(rt, path)
|
return impliedType(rt, path)
|
||||||
|
|||||||
+91
-44
@@ -2,7 +2,10 @@ package cty
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"iter"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/zclconf/go-cty/cty/ctymarks"
|
||||||
)
|
)
|
||||||
|
|
||||||
// marker is an internal wrapper type used to add special "marks" to values.
|
// marker is an internal wrapper type used to add special "marks" to values.
|
||||||
@@ -18,19 +21,19 @@ import (
|
|||||||
// an application that never marks a value does not need to worry about
|
// an application that never marks a value does not need to worry about
|
||||||
// encountering marked values.
|
// encountering marked values.
|
||||||
type marker struct {
|
type marker struct {
|
||||||
realV interface{}
|
realV any
|
||||||
marks ValueMarks
|
marks ValueMarks
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValueMarks is a map, representing a set, of "mark" values associated with
|
// ValueMarks is a map, representing a set, of "mark" values associated with
|
||||||
// a Value. See Value.Mark for more information on the usage of mark values.
|
// a Value. See Value.Mark for more information on the usage of mark values.
|
||||||
type ValueMarks map[interface{}]struct{}
|
type ValueMarks map[any]struct{}
|
||||||
|
|
||||||
// NewValueMarks constructs a new ValueMarks set with the given mark values.
|
// NewValueMarks constructs a new ValueMarks set with the given mark values.
|
||||||
//
|
//
|
||||||
// If any of the arguments are already ValueMarks values then they'll be merged
|
// If any of the arguments are already ValueMarks values then they'll be merged
|
||||||
// into the result, rather than used directly as individual marks.
|
// into the result, rather than used directly as individual marks.
|
||||||
func NewValueMarks(marks ...interface{}) ValueMarks {
|
func NewValueMarks(marks ...any) ValueMarks {
|
||||||
if len(marks) == 0 {
|
if len(marks) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -111,7 +114,7 @@ func (val Value) IsMarked() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HasMark returns true if and only if the receiving value has the given mark.
|
// HasMark returns true if and only if the receiving value has the given mark.
|
||||||
func (val Value) HasMark(mark interface{}) bool {
|
func (val Value) HasMark(mark any) bool {
|
||||||
if mr, ok := val.v.(marker); ok {
|
if mr, ok := val.v.(marker); ok {
|
||||||
_, ok := mr.marks[mark]
|
_, ok := mr.marks[mark]
|
||||||
return ok
|
return ok
|
||||||
@@ -119,21 +122,68 @@ func (val Value) HasMark(mark interface{}) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasMarkDeep is like [HasMark] but also searches any values nested inside
|
||||||
|
// the given value.
|
||||||
|
func (val Value) HasMarkDeep(mark any) bool {
|
||||||
|
for _, v := range DeepValues(val) {
|
||||||
|
if v.HasMark(mark) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueMarksOfType returns an iterable sequence of any marks directly
|
||||||
|
// associated with the given value that can be type-asserted to the given
|
||||||
|
// type.
|
||||||
|
func ValueMarksOfType[T any](v Value) iter.Seq[T] {
|
||||||
|
return func(yield func(T) bool) {
|
||||||
|
yieldValueMarksOfType(v, yield)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueMarksOfTypeDeep is like [ValueMarksOfType] but also visits any values
|
||||||
|
// nested inside the given value.
|
||||||
|
//
|
||||||
|
// The same value may be produced multiple times if multiple nested values are
|
||||||
|
// marked with it.
|
||||||
|
func ValueMarksOfTypeDeep[T any](v Value) iter.Seq[T] {
|
||||||
|
return func(yield func(T) bool) {
|
||||||
|
for _, v := range DeepValues(v) {
|
||||||
|
if !yieldValueMarksOfType(v, yield) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func yieldValueMarksOfType[T any](v Value, yield func(T) bool) bool {
|
||||||
|
mr, ok := v.v.(marker)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for mark := range mr.marks {
|
||||||
|
if v, ok := mark.(T); ok {
|
||||||
|
if !yield(v) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// ContainsMarked returns true if the receiving value or any value within it
|
// ContainsMarked returns true if the receiving value or any value within it
|
||||||
// is marked.
|
// is marked.
|
||||||
//
|
//
|
||||||
// This operation is relatively expensive. If you only need a shallow result,
|
// This operation is relatively expensive. If you only need a shallow result,
|
||||||
// use IsMarked instead.
|
// use IsMarked instead.
|
||||||
func (val Value) ContainsMarked() bool {
|
func (val Value) ContainsMarked() bool {
|
||||||
ret := false
|
for _, v := range DeepValues(val) {
|
||||||
Walk(val, func(_ Path, v Value) (bool, error) {
|
|
||||||
if v.IsMarked() {
|
if v.IsMarked() {
|
||||||
ret = true
|
return true
|
||||||
return false, nil
|
|
||||||
}
|
}
|
||||||
return true, nil
|
}
|
||||||
})
|
return false
|
||||||
return ret
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (val Value) assertUnmarked() {
|
func (val Value) assertUnmarked() {
|
||||||
@@ -189,7 +239,7 @@ func (val Value) HasSameMarks(other Value) bool {
|
|||||||
//
|
//
|
||||||
// An application that never calls this method does not need to worry about
|
// An application that never calls this method does not need to worry about
|
||||||
// handling marked values.
|
// handling marked values.
|
||||||
func (val Value) Mark(mark interface{}) Value {
|
func (val Value) Mark(mark any) Value {
|
||||||
if _, ok := mark.(ValueMarks); ok {
|
if _, ok := mark.(ValueMarks); ok {
|
||||||
panic("cannot call Value.Mark with a ValueMarks value (use WithMarks instead)")
|
panic("cannot call Value.Mark with a ValueMarks value (use WithMarks instead)")
|
||||||
}
|
}
|
||||||
@@ -236,6 +286,11 @@ func (t *applyPathValueMarksTransformer) Exit(p Path, v Value) (Value, error) {
|
|||||||
// markers to particular paths and returns the marked
|
// markers to particular paths and returns the marked
|
||||||
// Value.
|
// Value.
|
||||||
func (val Value) MarkWithPaths(pvm []PathValueMarks) Value {
|
func (val Value) MarkWithPaths(pvm []PathValueMarks) Value {
|
||||||
|
if len(pvm) == 0 {
|
||||||
|
// If we have no marks to apply then there's nothing to do, so we'll
|
||||||
|
// just return the same value rather than wastefully rebuilding it.
|
||||||
|
return val
|
||||||
|
}
|
||||||
ret, _ := TransformWithTransformer(val, &applyPathValueMarksTransformer{pvm})
|
ret, _ := TransformWithTransformer(val, &applyPathValueMarksTransformer{pvm})
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
@@ -257,24 +312,6 @@ func (val Value) Unmark() (Value, ValueMarks) {
|
|||||||
}, marks
|
}, marks
|
||||||
}
|
}
|
||||||
|
|
||||||
type unmarkTransformer struct {
|
|
||||||
pvm []PathValueMarks
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *unmarkTransformer) Enter(p Path, v Value) (Value, error) {
|
|
||||||
unmarkedVal, marks := v.Unmark()
|
|
||||||
if len(marks) > 0 {
|
|
||||||
path := make(Path, len(p), len(p)+1)
|
|
||||||
copy(path, p)
|
|
||||||
t.pvm = append(t.pvm, PathValueMarks{path, marks})
|
|
||||||
}
|
|
||||||
return unmarkedVal, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *unmarkTransformer) Exit(p Path, v Value) (Value, error) {
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarkDeep is similar to Unmark, but it works with an entire nested structure
|
// UnmarkDeep is similar to Unmark, but it works with an entire nested structure
|
||||||
// rather than just the given value directly.
|
// rather than just the given value directly.
|
||||||
//
|
//
|
||||||
@@ -282,17 +319,12 @@ func (t *unmarkTransformer) Exit(p Path, v Value) (Value, error) {
|
|||||||
// the returned marks set includes the superset of all of the marks encountered
|
// the returned marks set includes the superset of all of the marks encountered
|
||||||
// during the operation.
|
// during the operation.
|
||||||
func (val Value) UnmarkDeep() (Value, ValueMarks) {
|
func (val Value) UnmarkDeep() (Value, ValueMarks) {
|
||||||
t := unmarkTransformer{}
|
retMarks := make(ValueMarks)
|
||||||
ret, _ := TransformWithTransformer(val, &t)
|
retVal, _ := val.WrangleMarksDeep(func(mark any, path Path) (ctymarks.WrangleAction, error) {
|
||||||
|
retMarks[mark] = struct{}{}
|
||||||
marks := make(ValueMarks)
|
return ctymarks.WrangleDrop, nil
|
||||||
for _, pvm := range t.pvm {
|
})
|
||||||
for m, s := range pvm.Marks {
|
return retVal, retMarks
|
||||||
marks[m] = s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret, marks
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarkDeepWithPaths is like UnmarkDeep, except it returns a slice
|
// UnmarkDeepWithPaths is like UnmarkDeep, except it returns a slice
|
||||||
@@ -300,9 +332,24 @@ func (val Value) UnmarkDeep() (Value, ValueMarks) {
|
|||||||
// a caller to know which marks are associated with which paths
|
// a caller to know which marks are associated with which paths
|
||||||
// in the Value.
|
// in the Value.
|
||||||
func (val Value) UnmarkDeepWithPaths() (Value, []PathValueMarks) {
|
func (val Value) UnmarkDeepWithPaths() (Value, []PathValueMarks) {
|
||||||
t := unmarkTransformer{}
|
var pvm []PathValueMarks
|
||||||
ret, _ := TransformWithTransformer(val, &t)
|
retVal, _ := val.WrangleMarksDeep(func(mark any, path Path) (ctymarks.WrangleAction, error) {
|
||||||
return ret, t.pvm
|
if len(pvm) != 0 {
|
||||||
|
// We'll try to modify the most recent item instead of adding
|
||||||
|
// a new one, if the path hasn't changed.
|
||||||
|
latest := &pvm[len(pvm)-1]
|
||||||
|
if latest.Path.Equals(path) {
|
||||||
|
latest.Marks[mark] = struct{}{}
|
||||||
|
return ctymarks.WrangleDrop, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pvm = append(pvm, PathValueMarks{
|
||||||
|
Path: path.Copy(),
|
||||||
|
Marks: NewValueMarks(mark),
|
||||||
|
})
|
||||||
|
return ctymarks.WrangleDrop, nil
|
||||||
|
})
|
||||||
|
return retVal, pvm
|
||||||
}
|
}
|
||||||
|
|
||||||
func (val Value) unmarkForce() Value {
|
func (val Value) unmarkForce() Value {
|
||||||
|
|||||||
+289
@@ -0,0 +1,289 @@
|
|||||||
|
package cty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"iter"
|
||||||
|
"maps"
|
||||||
|
|
||||||
|
"github.com/zclconf/go-cty/cty/ctymarks"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WrangleMarksDeep is a specialized variant of [Transform] that is focused
|
||||||
|
// on interrogating and modifying any marks present throughout a data structure,
|
||||||
|
// without modifying anything else about the value.
|
||||||
|
//
|
||||||
|
// Refer to the [WrangleFunc] documentation for more information. Each of
|
||||||
|
// the provided functions is called in turn for each mark at each distinct path,
|
||||||
|
// and the first function that returns a non-nil [ctymarks.WrangleAction] "wins"
|
||||||
|
// and prevents any later ones from running for a particular mark/path pair.
|
||||||
|
//
|
||||||
|
// The implementation makes a best effort to avoid constructing new values
|
||||||
|
// unless marks have actually changed, to keep this operation relatively cheap
|
||||||
|
// in the presumed-common case where no marks are present at all.
|
||||||
|
func (v Value) WrangleMarksDeep(wranglers ...WrangleFunc) (Value, error) {
|
||||||
|
// This function is implemented in this package, rather than in the
|
||||||
|
// separate "ctymarks", so that it can intrude into the unexported
|
||||||
|
// internal details of [Value] to minimize overhead when no marks
|
||||||
|
// are present at all.
|
||||||
|
var path Path
|
||||||
|
if v.IsKnown() && !v.Type().IsPrimitiveType() && !v.IsNull() {
|
||||||
|
// If we have a known, non-null, non-primitive-typed value then we
|
||||||
|
// can assume there will be at least a little nesting we need to
|
||||||
|
// represent using our path, and so we'll preallocate some capacity
|
||||||
|
// which we'll be able to share across all calls that are shallower
|
||||||
|
// than this level of nesting.
|
||||||
|
path = make(Path, 0, 4)
|
||||||
|
}
|
||||||
|
topMarks := make(ValueMarks)
|
||||||
|
var errs []error
|
||||||
|
new := wrangleMarksDeep(v, wranglers, path, topMarks, &errs)
|
||||||
|
if new == NilVal {
|
||||||
|
new = v // completely unchanged
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
switch len(errs) {
|
||||||
|
case 0:
|
||||||
|
// nil err is fine, then
|
||||||
|
case 1:
|
||||||
|
err = errs[0]
|
||||||
|
default:
|
||||||
|
err = errors.Join(errs...)
|
||||||
|
}
|
||||||
|
return new.WithMarks(topMarks), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrangleMarksDeep is the main implementation of [WrangleMarksDeep], which
|
||||||
|
// calls itself recursively to handle nested data structures.
|
||||||
|
//
|
||||||
|
// If the returned value is [NilVal] then that means that no changes were
|
||||||
|
// needed to anything at or beneath that nesting level and so the caller should
|
||||||
|
// just keep the original value exactly.
|
||||||
|
//
|
||||||
|
// Modifies topMarks and errs during traversal to collect (respectively) any
|
||||||
|
// marks that caused [ctymarks.WrangleExpand] and and errors returned by
|
||||||
|
// wrangle functions.
|
||||||
|
func wrangleMarksDeep(v Value, wranglers []WrangleFunc, path Path, topMarks ValueMarks, errs *[]error) Value {
|
||||||
|
var givenMarks, newMarks ValueMarks
|
||||||
|
makeNewValue := false
|
||||||
|
// The following is the same idea as [Value.Unmark], but implemented inline
|
||||||
|
// here so that we can skip copying any existing ValueMarks that might
|
||||||
|
// already be present, since we know we're not going to try to mutate it.
|
||||||
|
if marked, ok := v.v.(marker); ok {
|
||||||
|
v = Value{
|
||||||
|
ty: v.ty,
|
||||||
|
v: marked.realV,
|
||||||
|
}
|
||||||
|
givenMarks = marked.marks
|
||||||
|
}
|
||||||
|
|
||||||
|
// We call this whenever we know we're returning a new value, to perform
|
||||||
|
// a one-time copy of the given marks into a new marks map we can modify
|
||||||
|
// and to set a flag to force us to construct a newly-marked value when
|
||||||
|
// we return below.
|
||||||
|
needNewValue := func() {
|
||||||
|
if newMarks == nil && len(givenMarks) != 0 {
|
||||||
|
newMarks = make(ValueMarks, len(givenMarks))
|
||||||
|
maps.Copy(newMarks, givenMarks)
|
||||||
|
}
|
||||||
|
makeNewValue = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for mark := range givenMarks {
|
||||||
|
Wranglers:
|
||||||
|
for _, wrangler := range wranglers {
|
||||||
|
action, err := wrangler(mark, path)
|
||||||
|
if err != nil {
|
||||||
|
if len(path) != 0 {
|
||||||
|
err = path.NewError(err)
|
||||||
|
}
|
||||||
|
*errs = append(*errs, err)
|
||||||
|
}
|
||||||
|
switch action {
|
||||||
|
case nil:
|
||||||
|
continue Wranglers
|
||||||
|
case ctymarks.WrangleKeep:
|
||||||
|
break Wranglers
|
||||||
|
case ctymarks.WrangleExpand:
|
||||||
|
topMarks[mark] = struct{}{}
|
||||||
|
break Wranglers
|
||||||
|
case ctymarks.WrangleDrop:
|
||||||
|
needNewValue()
|
||||||
|
delete(newMarks, mark)
|
||||||
|
break Wranglers
|
||||||
|
default:
|
||||||
|
newMark := ctymarks.WrangleReplaceMark(action)
|
||||||
|
if newMark == nil {
|
||||||
|
// Should not get here because these cases should be
|
||||||
|
// exhaustive for all possible WrangleAction values.
|
||||||
|
panic(fmt.Sprintf("unhandled WrangleAction %#v", action))
|
||||||
|
}
|
||||||
|
needNewValue()
|
||||||
|
delete(newMarks, mark)
|
||||||
|
newMarks[newMark] = struct{}{}
|
||||||
|
break Wranglers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're not going to make any further changes to our _direct_ marks
|
||||||
|
// after this, so if we didn't already make a copy of the given marks
|
||||||
|
// we can now safely alias our original set to reuse when we return.
|
||||||
|
// (We might still construct a new value though, if we recurse into
|
||||||
|
// a nested value that needs its own changes.)
|
||||||
|
if newMarks == nil {
|
||||||
|
newMarks = givenMarks // might still be nil if we didn't have any marks on entry
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we'll visit nested values recursively, if any.
|
||||||
|
// The cases below intentionally don't cover primitive types, set types,
|
||||||
|
// or capsule types, because none of them can possibly have nested marks
|
||||||
|
// inside. (For set types in particular, any marks on inner values get
|
||||||
|
// aggregated on the top-level set itself during construction.)
|
||||||
|
ty := v.Type()
|
||||||
|
switch {
|
||||||
|
case v.IsNull() || !v.IsKnown():
|
||||||
|
// Can't recurse into null or unknown values, regardless of type,
|
||||||
|
// so nothing to do here.
|
||||||
|
|
||||||
|
case ty.IsListType() || ty.IsTupleType():
|
||||||
|
// These types both have the same internal representation, and we
|
||||||
|
// know we're not going to change anything about the type, so we
|
||||||
|
// can share the same implementation for both.
|
||||||
|
l := v.LengthInt()
|
||||||
|
if l == 0 {
|
||||||
|
break // nothing to do for an empty container
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll avoid allocating a new slice until we know we're going
|
||||||
|
// to make a change.
|
||||||
|
var newElems []any // as would appear in Value.v for all three of these types
|
||||||
|
for i, innerV := range replaceKWithIdx(v.Elements()) {
|
||||||
|
path := append(path, IndexStep{Key: NumberIntVal(int64(i))})
|
||||||
|
newInnerV := wrangleMarksDeep(innerV, wranglers, path, topMarks, errs)
|
||||||
|
if newInnerV != NilVal {
|
||||||
|
needNewValue()
|
||||||
|
if newElems == nil {
|
||||||
|
// If this is the first change we've found then we need to
|
||||||
|
// allocate our new elems array and retroactively copy
|
||||||
|
// anything we previously skipped because it was unchanged.
|
||||||
|
newElems = make([]any, i, l)
|
||||||
|
copy(newElems, v.v.([]any))
|
||||||
|
}
|
||||||
|
newElems = append(newElems, newInnerV.v)
|
||||||
|
} else if newElems != nil {
|
||||||
|
// Once we've started building a new value we need to append
|
||||||
|
// everything to it whether it's changed or not, but we can
|
||||||
|
// reuse the unchanged element's internal value.
|
||||||
|
newElems = append(newElems, innerV.v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if newElems != nil {
|
||||||
|
// if we built a new array of elements then it should replace
|
||||||
|
// the one from our input value.
|
||||||
|
v.v = newElems
|
||||||
|
}
|
||||||
|
|
||||||
|
case ty.IsMapType() || ty.IsObjectType():
|
||||||
|
// These types both have the same internal representation, and we
|
||||||
|
// know we're not going to change anything about the type, so we
|
||||||
|
// can share the same implementation for both.
|
||||||
|
l := v.LengthInt()
|
||||||
|
if l == 0 {
|
||||||
|
break // nothing to do for an empty container
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll avoid allocating a new map until we know we're going to
|
||||||
|
// make a change.
|
||||||
|
var newElems map[string]any
|
||||||
|
for keyV, innerV := range v.Elements() {
|
||||||
|
var pathStep PathStep
|
||||||
|
if ty.IsObjectType() {
|
||||||
|
pathStep = GetAttrStep{Name: keyV.AsString()}
|
||||||
|
} else {
|
||||||
|
pathStep = IndexStep{Key: keyV}
|
||||||
|
}
|
||||||
|
path := append(path, pathStep)
|
||||||
|
newInnerV := wrangleMarksDeep(innerV, wranglers, path, topMarks, errs)
|
||||||
|
if newInnerV != NilVal {
|
||||||
|
needNewValue()
|
||||||
|
if newElems == nil {
|
||||||
|
// If this is the first change we've found then we need to
|
||||||
|
// allocate our new elems map and retroactively copy
|
||||||
|
// everything from the original map before we overwrite
|
||||||
|
// the elements that need to change.
|
||||||
|
newElems = make(map[string]any, l)
|
||||||
|
maps.Copy(newElems, v.v.(map[string]any))
|
||||||
|
}
|
||||||
|
newElems[keyV.AsString()] = newInnerV.v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if newElems != nil {
|
||||||
|
// if we built a new map of elements then it should replace
|
||||||
|
// the one from our input value.
|
||||||
|
v.v = newElems
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !makeNewValue {
|
||||||
|
// We didn't make any changes to the marks, so we don't need to
|
||||||
|
// construct a new value.
|
||||||
|
return NilVal
|
||||||
|
}
|
||||||
|
return v.WithMarks(newMarks)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrangleFunc is the signature of a callback function used to visit a
|
||||||
|
// particular mark associated with a particular path within a value.
|
||||||
|
//
|
||||||
|
// [Path] values passed to successive calls to a [WrangleFunc] may share a
|
||||||
|
// backing array, and so if the function wishes to retain a particular path
|
||||||
|
// after it returns it must use [Path.Copy] to produce a copy in an unaliased
|
||||||
|
// backing array.
|
||||||
|
//
|
||||||
|
// A function of this type must decide what change to make, if any, to the
|
||||||
|
// presence of this mark at this location. Returning nil means to take no
|
||||||
|
// action at all and to potentially allow other later functions of this
|
||||||
|
// type to decide what to do instead.
|
||||||
|
//
|
||||||
|
// If the function returns an error then [Value.WrangleMarksDeep] collects it
|
||||||
|
// and any other errors returned during traversal, automatically wraps in a
|
||||||
|
// [cty.PathError] if not at the root, and returns an [`errors.Join`] of all
|
||||||
|
// of the errors if there are more than one. The indicated action is still taken
|
||||||
|
// and continued "wrangling" occurs as normal to visit other marks and other
|
||||||
|
// paths.
|
||||||
|
//
|
||||||
|
// [cty.Value.WrangleMarksDeep], and this callback signature used with it,
|
||||||
|
// are together designed with some assumptions that don't always hold but
|
||||||
|
// have been common enough to make it seem worth supporting with a first-class
|
||||||
|
// feature:
|
||||||
|
//
|
||||||
|
// - All of the different marks on any specific value are orthogonal to one
|
||||||
|
// another, and so it's possible to decide an action for each one in
|
||||||
|
// isolation.
|
||||||
|
// - Marks within a data structure are orthogonal to the specific values they
|
||||||
|
// are associated with, and so it's possible to decide an action without
|
||||||
|
// knowning the value it's associated with. (Though it's possible in
|
||||||
|
// principle to use the given path to retrieve more information when needed,
|
||||||
|
// at the expense of some additional traversal overhead.)
|
||||||
|
// - Most values have no marks at all and when marks are present there are
|
||||||
|
// relatively few of them, and so it's worth making some extra effort to
|
||||||
|
// handle the no-marks case cheaply even if it makes the marks-present case
|
||||||
|
// a little more expensive.
|
||||||
|
//
|
||||||
|
// If any of these assumptions don't apply to your situation then this may not
|
||||||
|
// be an appropriate solution. [cty.Transform] or [cty.TransformWithTransformer]
|
||||||
|
// might serve as a more general alternative if you need more control.
|
||||||
|
type WrangleFunc func(mark any, path Path) (ctymarks.WrangleAction, error)
|
||||||
|
|
||||||
|
func replaceKWithIdx[K any, V any](in iter.Seq2[K, V]) iter.Seq2[int, V] {
|
||||||
|
return func(yield func(int, V) bool) {
|
||||||
|
i := 0
|
||||||
|
for _, v := range in {
|
||||||
|
if !yield(i, v) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -134,7 +134,7 @@ func init() {
|
|||||||
EmptyObject = Object(map[string]Type{})
|
EmptyObject = Object(map[string]Type{})
|
||||||
EmptyObjectVal = Value{
|
EmptyObjectVal = Value{
|
||||||
ty: EmptyObject,
|
ty: EmptyObject,
|
||||||
v: map[string]interface{}{},
|
v: map[string]any{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -43,5 +43,5 @@ type OrderedRules[T any] interface {
|
|||||||
// Less returns true if and only if the first argument should sort before
|
// Less returns true if and only if the first argument should sort before
|
||||||
// the second argument. If the second argument should sort before the first
|
// the second argument. If the second argument should sort before the first
|
||||||
// or if there is no defined order for the values, return false.
|
// or if there is no defined order for the values, return false.
|
||||||
Less(interface{}, interface{}) bool
|
Less(any, any) bool
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -21,7 +21,7 @@ type ValueSet struct {
|
|||||||
// ValueSet is just a thin wrapper around a set.Set with our value-oriented
|
// ValueSet is just a thin wrapper around a set.Set with our value-oriented
|
||||||
// "rules" applied. We do this so that the caller can work in terms of
|
// "rules" applied. We do this so that the caller can work in terms of
|
||||||
// cty.Value objects even though the set internals use the raw values.
|
// cty.Value objects even though the set internals use the raw values.
|
||||||
s set.Set[interface{}]
|
s set.Set[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewValueSet creates and returns a new ValueSet with the given element type.
|
// NewValueSet creates and returns a new ValueSet with the given element type.
|
||||||
@@ -29,7 +29,7 @@ func NewValueSet(ety Type) ValueSet {
|
|||||||
return newValueSet(set.NewSet(newSetRules(ety)))
|
return newValueSet(set.NewSet(newSetRules(ety)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func newValueSet(s set.Set[interface{}]) ValueSet {
|
func newValueSet(s set.Set[any]) ValueSet {
|
||||||
return ValueSet{
|
return ValueSet{
|
||||||
s: s,
|
s: s,
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-6
@@ -21,9 +21,9 @@ type setRules struct {
|
|||||||
Type Type
|
Type Type
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ set.OrderedRules[interface{}] = setRules{}
|
var _ set.OrderedRules[any] = setRules{}
|
||||||
|
|
||||||
func newSetRules(ety Type) set.Rules[interface{}] {
|
func newSetRules(ety Type) set.Rules[any] {
|
||||||
return setRules{ety}
|
return setRules{ety}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,14 +43,14 @@ func (val Value) Hash() int {
|
|||||||
return int(crc32.ChecksumIEEE(hashBytes))
|
return int(crc32.ChecksumIEEE(hashBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r setRules) Hash(v interface{}) int {
|
func (r setRules) Hash(v any) int {
|
||||||
return Value{
|
return Value{
|
||||||
ty: r.Type,
|
ty: r.Type,
|
||||||
v: v,
|
v: v,
|
||||||
}.Hash()
|
}.Hash()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r setRules) Equivalent(v1 interface{}, v2 interface{}) bool {
|
func (r setRules) Equivalent(v1 any, v2 any) bool {
|
||||||
v1v := Value{
|
v1v := Value{
|
||||||
ty: r.Type,
|
ty: r.Type,
|
||||||
v: v1,
|
v: v1,
|
||||||
@@ -71,7 +71,7 @@ func (r setRules) Equivalent(v1 interface{}, v2 interface{}) bool {
|
|||||||
|
|
||||||
// SameRules is only true if the other Rules instance is also a setRules struct,
|
// SameRules is only true if the other Rules instance is also a setRules struct,
|
||||||
// and the types are considered equal.
|
// and the types are considered equal.
|
||||||
func (r setRules) SameRules(other set.Rules[interface{}]) bool {
|
func (r setRules) SameRules(other set.Rules[any]) bool {
|
||||||
rules, ok := other.(setRules)
|
rules, ok := other.(setRules)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
@@ -82,7 +82,7 @@ func (r setRules) SameRules(other set.Rules[interface{}]) bool {
|
|||||||
|
|
||||||
// Less is an implementation of set.OrderedRules so that we can iterate over
|
// Less is an implementation of set.OrderedRules so that we can iterate over
|
||||||
// set elements in a consistent order, where such an order is possible.
|
// set elements in a consistent order, where such an order is possible.
|
||||||
func (r setRules) Less(v1, v2 interface{}) bool {
|
func (r setRules) Less(v1, v2 any) bool {
|
||||||
v1v := Value{
|
v1v := Value{
|
||||||
ty: r.Type,
|
ty: r.Type,
|
||||||
v: v1,
|
v: v1,
|
||||||
|
|||||||
+1
-1
@@ -74,7 +74,7 @@ func init() {
|
|||||||
EmptyTuple = Tuple([]Type{})
|
EmptyTuple = Tuple([]Type{})
|
||||||
EmptyTupleVal = Value{
|
EmptyTupleVal = Value{
|
||||||
ty: EmptyTuple,
|
ty: EmptyTuple,
|
||||||
v: []interface{}{},
|
v: []any{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ type unknownType struct {
|
|||||||
// on the unknown value, but all unknown values start as totally unknown
|
// on the unknown value, but all unknown values start as totally unknown
|
||||||
// and we will also typically lose all unknown value refinements when
|
// and we will also typically lose all unknown value refinements when
|
||||||
// round-tripping through serialization formats.
|
// round-tripping through serialization formats.
|
||||||
var totallyUnknown interface{} = &unknownType{}
|
var totallyUnknown any = &unknownType{}
|
||||||
|
|
||||||
// UnknownVal returns an Value that represents an unknown value of the given
|
// UnknownVal returns an Value that represents an unknown value of the given
|
||||||
// type. Unknown values can be used to represent a value that is
|
// type. Unknown values can be used to represent a value that is
|
||||||
|
|||||||
+5
@@ -8,6 +8,11 @@ package cty
|
|||||||
// represent unknowns, such as JSON, as long as the caller does not need to
|
// represent unknowns, such as JSON, as long as the caller does not need to
|
||||||
// retain the unknown value information.
|
// retain the unknown value information.
|
||||||
func UnknownAsNull(val Value) Value {
|
func UnknownAsNull(val Value) Value {
|
||||||
|
if val.IsMarked() {
|
||||||
|
val, valMarks := val.Unmark()
|
||||||
|
return UnknownAsNull(val).WithMarks(valMarks)
|
||||||
|
}
|
||||||
|
|
||||||
ty := val.Type()
|
ty := val.Type()
|
||||||
switch {
|
switch {
|
||||||
case val.IsNull():
|
case val.IsNull():
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@ package cty
|
|||||||
// rules than are offered by the built-in converter where necessary.
|
// rules than are offered by the built-in converter where necessary.
|
||||||
type Value struct {
|
type Value struct {
|
||||||
ty Type
|
ty Type
|
||||||
v interface{}
|
v any
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type returns the type of the value.
|
// Type returns the type of the value.
|
||||||
|
|||||||
+10
-10
@@ -113,7 +113,7 @@ func NormalizeString(s string) string {
|
|||||||
// by the key names and value types in the given map.
|
// by the key names and value types in the given map.
|
||||||
func ObjectVal(attrs map[string]Value) Value {
|
func ObjectVal(attrs map[string]Value) Value {
|
||||||
attrTypes := make(map[string]Type, len(attrs))
|
attrTypes := make(map[string]Type, len(attrs))
|
||||||
attrVals := make(map[string]interface{}, len(attrs))
|
attrVals := make(map[string]any, len(attrs))
|
||||||
|
|
||||||
for attr, val := range attrs {
|
for attr, val := range attrs {
|
||||||
attr = NormalizeString(attr)
|
attr = NormalizeString(attr)
|
||||||
@@ -131,7 +131,7 @@ func ObjectVal(attrs map[string]Value) Value {
|
|||||||
// defined by the value types in the given slice.
|
// defined by the value types in the given slice.
|
||||||
func TupleVal(elems []Value) Value {
|
func TupleVal(elems []Value) Value {
|
||||||
elemTypes := make([]Type, len(elems))
|
elemTypes := make([]Type, len(elems))
|
||||||
elemVals := make([]interface{}, len(elems))
|
elemVals := make([]any, len(elems))
|
||||||
|
|
||||||
for i, val := range elems {
|
for i, val := range elems {
|
||||||
elemTypes[i] = val.ty
|
elemTypes[i] = val.ty
|
||||||
@@ -156,7 +156,7 @@ func ListVal(vals []Value) Value {
|
|||||||
panic("must not call ListVal with empty slice")
|
panic("must not call ListVal with empty slice")
|
||||||
}
|
}
|
||||||
elementType := DynamicPseudoType
|
elementType := DynamicPseudoType
|
||||||
rawList := make([]interface{}, len(vals))
|
rawList := make([]any, len(vals))
|
||||||
|
|
||||||
for i, val := range vals {
|
for i, val := range vals {
|
||||||
if elementType == DynamicPseudoType {
|
if elementType == DynamicPseudoType {
|
||||||
@@ -181,7 +181,7 @@ func ListVal(vals []Value) Value {
|
|||||||
func ListValEmpty(element Type) Value {
|
func ListValEmpty(element Type) Value {
|
||||||
return Value{
|
return Value{
|
||||||
ty: List(element),
|
ty: List(element),
|
||||||
v: []interface{}{},
|
v: []any{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,7 +211,7 @@ func MapVal(vals map[string]Value) Value {
|
|||||||
panic("must not call MapVal with empty map")
|
panic("must not call MapVal with empty map")
|
||||||
}
|
}
|
||||||
elementType := DynamicPseudoType
|
elementType := DynamicPseudoType
|
||||||
rawMap := make(map[string]interface{}, len(vals))
|
rawMap := make(map[string]any, len(vals))
|
||||||
|
|
||||||
for key, val := range vals {
|
for key, val := range vals {
|
||||||
if elementType == DynamicPseudoType {
|
if elementType == DynamicPseudoType {
|
||||||
@@ -236,7 +236,7 @@ func MapVal(vals map[string]Value) Value {
|
|||||||
func MapValEmpty(element Type) Value {
|
func MapValEmpty(element Type) Value {
|
||||||
return Value{
|
return Value{
|
||||||
ty: Map(element),
|
ty: Map(element),
|
||||||
v: map[string]interface{}{},
|
v: map[string]any{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,7 +266,7 @@ func SetVal(vals []Value) Value {
|
|||||||
panic("must not call SetVal with empty slice")
|
panic("must not call SetVal with empty slice")
|
||||||
}
|
}
|
||||||
elementType := DynamicPseudoType
|
elementType := DynamicPseudoType
|
||||||
rawList := make([]interface{}, len(vals))
|
rawList := make([]any, len(vals))
|
||||||
var markSets []ValueMarks
|
var markSets []ValueMarks
|
||||||
|
|
||||||
for i, val := range vals {
|
for i, val := range vals {
|
||||||
@@ -286,7 +286,7 @@ func SetVal(vals []Value) Value {
|
|||||||
rawList[i] = val.v
|
rawList[i] = val.v
|
||||||
}
|
}
|
||||||
|
|
||||||
rawVal := set.NewSetFromSlice(set.Rules[interface{}](setRules{elementType}), rawList)
|
rawVal := set.NewSetFromSlice(set.Rules[any](setRules{elementType}), rawList)
|
||||||
|
|
||||||
return Value{
|
return Value{
|
||||||
ty: Set(elementType),
|
ty: Set(elementType),
|
||||||
@@ -333,7 +333,7 @@ func SetValFromValueSet(s ValueSet) Value {
|
|||||||
func SetValEmpty(element Type) Value {
|
func SetValEmpty(element Type) Value {
|
||||||
return Value{
|
return Value{
|
||||||
ty: Set(element),
|
ty: Set(element),
|
||||||
v: set.NewSet(set.Rules[interface{}](setRules{element})),
|
v: set.NewSet(set.Rules[any](setRules{element})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,7 +344,7 @@ func SetValEmpty(element Type) Value {
|
|||||||
// This function will panic if the given type is not a capsule type, if
|
// This function will panic if the given type is not a capsule type, if
|
||||||
// the given wrapVal is not compatible with the given capsule type, or if
|
// the given wrapVal is not compatible with the given capsule type, or if
|
||||||
// wrapVal is not a pointer.
|
// wrapVal is not a pointer.
|
||||||
func CapsuleVal(ty Type, wrapVal interface{}) Value {
|
func CapsuleVal(ty Type, wrapVal any) Value {
|
||||||
if !ty.IsCapsuleType() {
|
if !ty.IsCapsuleType() {
|
||||||
panic("not a capsule type")
|
panic("not a capsule type")
|
||||||
}
|
}
|
||||||
|
|||||||
+85
-63
@@ -2,6 +2,7 @@ package cty
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"iter"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/zclconf/go-cty/cty/set"
|
"github.com/zclconf/go-cty/cty/set"
|
||||||
@@ -13,7 +14,7 @@ func (val Value) GoString() string {
|
|||||||
if val.IsMarked() {
|
if val.IsMarked() {
|
||||||
unVal, marks := val.Unmark()
|
unVal, marks := val.Unmark()
|
||||||
if len(marks) == 1 {
|
if len(marks) == 1 {
|
||||||
var mark interface{}
|
var mark any
|
||||||
for m := range marks {
|
for m := range marks {
|
||||||
mark = m
|
mark = m
|
||||||
}
|
}
|
||||||
@@ -243,11 +244,11 @@ func (val Value) Equals(other Value) Value {
|
|||||||
for attr, aty := range oty.AttrTypes {
|
for attr, aty := range oty.AttrTypes {
|
||||||
lhs := Value{
|
lhs := Value{
|
||||||
ty: aty,
|
ty: aty,
|
||||||
v: val.v.(map[string]interface{})[attr],
|
v: val.v.(map[string]any)[attr],
|
||||||
}
|
}
|
||||||
rhs := Value{
|
rhs := Value{
|
||||||
ty: aty,
|
ty: aty,
|
||||||
v: other.v.(map[string]interface{})[attr],
|
v: other.v.(map[string]any)[attr],
|
||||||
}
|
}
|
||||||
eq := lhs.Equals(rhs)
|
eq := lhs.Equals(rhs)
|
||||||
if !eq.IsKnown() {
|
if !eq.IsKnown() {
|
||||||
@@ -264,11 +265,11 @@ func (val Value) Equals(other Value) Value {
|
|||||||
for i, ety := range tty.ElemTypes {
|
for i, ety := range tty.ElemTypes {
|
||||||
lhs := Value{
|
lhs := Value{
|
||||||
ty: ety,
|
ty: ety,
|
||||||
v: val.v.([]interface{})[i],
|
v: val.v.([]any)[i],
|
||||||
}
|
}
|
||||||
rhs := Value{
|
rhs := Value{
|
||||||
ty: ety,
|
ty: ety,
|
||||||
v: other.v.([]interface{})[i],
|
v: other.v.([]any)[i],
|
||||||
}
|
}
|
||||||
eq := lhs.Equals(rhs)
|
eq := lhs.Equals(rhs)
|
||||||
if !eq.IsKnown() {
|
if !eq.IsKnown() {
|
||||||
@@ -281,16 +282,16 @@ func (val Value) Equals(other Value) Value {
|
|||||||
}
|
}
|
||||||
case ty.IsListType():
|
case ty.IsListType():
|
||||||
ety := ty.typeImpl.(typeList).ElementTypeT
|
ety := ty.typeImpl.(typeList).ElementTypeT
|
||||||
if len(val.v.([]interface{})) == len(other.v.([]interface{})) {
|
if len(val.v.([]any)) == len(other.v.([]any)) {
|
||||||
result = true
|
result = true
|
||||||
for i := range val.v.([]interface{}) {
|
for i := range val.v.([]any) {
|
||||||
lhs := Value{
|
lhs := Value{
|
||||||
ty: ety,
|
ty: ety,
|
||||||
v: val.v.([]interface{})[i],
|
v: val.v.([]any)[i],
|
||||||
}
|
}
|
||||||
rhs := Value{
|
rhs := Value{
|
||||||
ty: ety,
|
ty: ety,
|
||||||
v: other.v.([]interface{})[i],
|
v: other.v.([]any)[i],
|
||||||
}
|
}
|
||||||
eq := lhs.Equals(rhs)
|
eq := lhs.Equals(rhs)
|
||||||
if !eq.IsKnown() {
|
if !eq.IsKnown() {
|
||||||
@@ -303,8 +304,8 @@ func (val Value) Equals(other Value) Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case ty.IsSetType():
|
case ty.IsSetType():
|
||||||
s1 := val.v.(set.Set[interface{}])
|
s1 := val.v.(set.Set[any])
|
||||||
s2 := other.v.(set.Set[interface{}])
|
s2 := other.v.(set.Set[any])
|
||||||
equal := true
|
equal := true
|
||||||
|
|
||||||
// Two sets are equal if all of their values are known and all values
|
// Two sets are equal if all of their values are known and all values
|
||||||
@@ -331,20 +332,20 @@ func (val Value) Equals(other Value) Value {
|
|||||||
result = equal
|
result = equal
|
||||||
case ty.IsMapType():
|
case ty.IsMapType():
|
||||||
ety := ty.typeImpl.(typeMap).ElementTypeT
|
ety := ty.typeImpl.(typeMap).ElementTypeT
|
||||||
if len(val.v.(map[string]interface{})) == len(other.v.(map[string]interface{})) {
|
if len(val.v.(map[string]any)) == len(other.v.(map[string]any)) {
|
||||||
result = true
|
result = true
|
||||||
for k := range val.v.(map[string]interface{}) {
|
for k := range val.v.(map[string]any) {
|
||||||
if _, ok := other.v.(map[string]interface{})[k]; !ok {
|
if _, ok := other.v.(map[string]any)[k]; !ok {
|
||||||
result = false
|
result = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
lhs := Value{
|
lhs := Value{
|
||||||
ty: ety,
|
ty: ety,
|
||||||
v: val.v.(map[string]interface{})[k],
|
v: val.v.(map[string]any)[k],
|
||||||
}
|
}
|
||||||
rhs := Value{
|
rhs := Value{
|
||||||
ty: ety,
|
ty: ety,
|
||||||
v: other.v.(map[string]interface{})[k],
|
v: other.v.(map[string]any)[k],
|
||||||
}
|
}
|
||||||
eq := lhs.Equals(rhs)
|
eq := lhs.Equals(rhs)
|
||||||
if !eq.IsKnown() {
|
if !eq.IsKnown() {
|
||||||
@@ -462,11 +463,11 @@ func (val Value) RawEquals(other Value) bool {
|
|||||||
for attr, aty := range oty.AttrTypes {
|
for attr, aty := range oty.AttrTypes {
|
||||||
lhs := Value{
|
lhs := Value{
|
||||||
ty: aty,
|
ty: aty,
|
||||||
v: val.v.(map[string]interface{})[attr],
|
v: val.v.(map[string]any)[attr],
|
||||||
}
|
}
|
||||||
rhs := Value{
|
rhs := Value{
|
||||||
ty: aty,
|
ty: aty,
|
||||||
v: other.v.(map[string]interface{})[attr],
|
v: other.v.(map[string]any)[attr],
|
||||||
}
|
}
|
||||||
eq := lhs.RawEquals(rhs)
|
eq := lhs.RawEquals(rhs)
|
||||||
if !eq {
|
if !eq {
|
||||||
@@ -479,11 +480,11 @@ func (val Value) RawEquals(other Value) bool {
|
|||||||
for i, ety := range tty.ElemTypes {
|
for i, ety := range tty.ElemTypes {
|
||||||
lhs := Value{
|
lhs := Value{
|
||||||
ty: ety,
|
ty: ety,
|
||||||
v: val.v.([]interface{})[i],
|
v: val.v.([]any)[i],
|
||||||
}
|
}
|
||||||
rhs := Value{
|
rhs := Value{
|
||||||
ty: ety,
|
ty: ety,
|
||||||
v: other.v.([]interface{})[i],
|
v: other.v.([]any)[i],
|
||||||
}
|
}
|
||||||
eq := lhs.RawEquals(rhs)
|
eq := lhs.RawEquals(rhs)
|
||||||
if !eq {
|
if !eq {
|
||||||
@@ -493,15 +494,15 @@ func (val Value) RawEquals(other Value) bool {
|
|||||||
return true
|
return true
|
||||||
case ty.IsListType():
|
case ty.IsListType():
|
||||||
ety := ty.typeImpl.(typeList).ElementTypeT
|
ety := ty.typeImpl.(typeList).ElementTypeT
|
||||||
if len(val.v.([]interface{})) == len(other.v.([]interface{})) {
|
if len(val.v.([]any)) == len(other.v.([]any)) {
|
||||||
for i := range val.v.([]interface{}) {
|
for i := range val.v.([]any) {
|
||||||
lhs := Value{
|
lhs := Value{
|
||||||
ty: ety,
|
ty: ety,
|
||||||
v: val.v.([]interface{})[i],
|
v: val.v.([]any)[i],
|
||||||
}
|
}
|
||||||
rhs := Value{
|
rhs := Value{
|
||||||
ty: ety,
|
ty: ety,
|
||||||
v: other.v.([]interface{})[i],
|
v: other.v.([]any)[i],
|
||||||
}
|
}
|
||||||
eq := lhs.RawEquals(rhs)
|
eq := lhs.RawEquals(rhs)
|
||||||
if !eq {
|
if !eq {
|
||||||
@@ -545,18 +546,18 @@ func (val Value) RawEquals(other Value) bool {
|
|||||||
}
|
}
|
||||||
valUn, _ := val.Unmark()
|
valUn, _ := val.Unmark()
|
||||||
otherUn, _ := other.Unmark()
|
otherUn, _ := other.Unmark()
|
||||||
if len(valUn.v.(map[string]interface{})) == len(otherUn.v.(map[string]interface{})) {
|
if len(valUn.v.(map[string]any)) == len(otherUn.v.(map[string]any)) {
|
||||||
for k := range valUn.v.(map[string]interface{}) {
|
for k := range valUn.v.(map[string]any) {
|
||||||
if _, ok := otherUn.v.(map[string]interface{})[k]; !ok {
|
if _, ok := otherUn.v.(map[string]any)[k]; !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
lhs := Value{
|
lhs := Value{
|
||||||
ty: ety,
|
ty: ety,
|
||||||
v: valUn.v.(map[string]interface{})[k],
|
v: valUn.v.(map[string]any)[k],
|
||||||
}
|
}
|
||||||
rhs := Value{
|
rhs := Value{
|
||||||
ty: ety,
|
ty: ety,
|
||||||
v: otherUn.v.(map[string]interface{})[k],
|
v: otherUn.v.(map[string]any)[k],
|
||||||
}
|
}
|
||||||
eq := lhs.RawEquals(rhs)
|
eq := lhs.RawEquals(rhs)
|
||||||
if !eq {
|
if !eq {
|
||||||
@@ -816,7 +817,7 @@ func (val Value) GetAttr(name string) Value {
|
|||||||
|
|
||||||
return Value{
|
return Value{
|
||||||
ty: attrType,
|
ty: attrType,
|
||||||
v: val.v.(map[string]interface{})[name],
|
v: val.v.(map[string]any)[name],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -872,7 +873,7 @@ func (val Value) Index(key Value) Value {
|
|||||||
|
|
||||||
return Value{
|
return Value{
|
||||||
ty: elty,
|
ty: elty,
|
||||||
v: val.v.([]interface{})[index],
|
v: val.v.([]any)[index],
|
||||||
}
|
}
|
||||||
case val.Type().IsMapType():
|
case val.Type().IsMapType():
|
||||||
elty := val.Type().ElementType()
|
elty := val.Type().ElementType()
|
||||||
@@ -895,7 +896,7 @@ func (val Value) Index(key Value) Value {
|
|||||||
|
|
||||||
return Value{
|
return Value{
|
||||||
ty: elty,
|
ty: elty,
|
||||||
v: val.v.(map[string]interface{})[keyStr],
|
v: val.v.(map[string]any)[keyStr],
|
||||||
}
|
}
|
||||||
case val.Type().IsTupleType():
|
case val.Type().IsTupleType():
|
||||||
if key.Type() == DynamicPseudoType {
|
if key.Type() == DynamicPseudoType {
|
||||||
@@ -922,7 +923,7 @@ func (val Value) Index(key Value) Value {
|
|||||||
|
|
||||||
return Value{
|
return Value{
|
||||||
ty: eltys[index],
|
ty: eltys[index],
|
||||||
v: val.v.([]interface{})[index],
|
v: val.v.([]any)[index],
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
panic("not a list, map, or tuple type")
|
panic("not a list, map, or tuple type")
|
||||||
@@ -969,7 +970,7 @@ func (val Value) HasIndex(key Value) Value {
|
|||||||
return False
|
return False
|
||||||
}
|
}
|
||||||
|
|
||||||
return BoolVal(int(index) < len(val.v.([]interface{})) && index >= 0)
|
return BoolVal(int(index) < len(val.v.([]any)) && index >= 0)
|
||||||
case val.Type().IsMapType():
|
case val.Type().IsMapType():
|
||||||
if key.Type() == DynamicPseudoType {
|
if key.Type() == DynamicPseudoType {
|
||||||
return UnknownVal(Bool).RefineNotNull()
|
return UnknownVal(Bool).RefineNotNull()
|
||||||
@@ -986,7 +987,7 @@ func (val Value) HasIndex(key Value) Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
keyStr := key.v.(string)
|
keyStr := key.v.(string)
|
||||||
_, exists := val.v.(map[string]interface{})[keyStr]
|
_, exists := val.v.(map[string]any)[keyStr]
|
||||||
|
|
||||||
return BoolVal(exists)
|
return BoolVal(exists)
|
||||||
case val.Type().IsTupleType():
|
case val.Type().IsTupleType():
|
||||||
@@ -1062,7 +1063,7 @@ func (val Value) HasElement(elem Value) Value {
|
|||||||
return False
|
return False
|
||||||
}
|
}
|
||||||
|
|
||||||
s := val.v.(set.Set[interface{}])
|
s := val.v.(set.Set[any])
|
||||||
if !s.Has(elem.v) {
|
if !s.Has(elem.v) {
|
||||||
return noMatchResult
|
return noMatchResult
|
||||||
}
|
}
|
||||||
@@ -1102,7 +1103,7 @@ func (val Value) Length() Value {
|
|||||||
// may or may not be equal to other elements in the set, and thus they
|
// may or may not be equal to other elements in the set, and thus they
|
||||||
// may or may not coalesce with other elements and produce fewer
|
// may or may not coalesce with other elements and produce fewer
|
||||||
// items in the resulting set.
|
// items in the resulting set.
|
||||||
storeLength := int64(val.v.(set.Set[interface{}]).Length())
|
storeLength := int64(val.v.(set.Set[any]).Length())
|
||||||
if storeLength == 1 || val.IsWhollyKnown() {
|
if storeLength == 1 || val.IsWhollyKnown() {
|
||||||
// If our set is wholly known then we know its length.
|
// If our set is wholly known then we know its length.
|
||||||
//
|
//
|
||||||
@@ -1164,7 +1165,7 @@ func (val Value) LengthInt() int {
|
|||||||
switch {
|
switch {
|
||||||
|
|
||||||
case val.ty.IsListType():
|
case val.ty.IsListType():
|
||||||
return len(val.v.([]interface{}))
|
return len(val.v.([]any))
|
||||||
|
|
||||||
case val.ty.IsSetType():
|
case val.ty.IsSetType():
|
||||||
// NOTE: This is technically not correct in cases where the set
|
// NOTE: This is technically not correct in cases where the set
|
||||||
@@ -1176,40 +1177,59 @@ func (val Value) LengthInt() int {
|
|||||||
// compatibility with callers that were relying on LengthInt rather
|
// compatibility with callers that were relying on LengthInt rather
|
||||||
// than calling Length. Instead of panicking when a set contains an
|
// than calling Length. Instead of panicking when a set contains an
|
||||||
// unknown value, LengthInt returns the largest possible length.
|
// unknown value, LengthInt returns the largest possible length.
|
||||||
return val.v.(set.Set[interface{}]).Length()
|
return val.v.(set.Set[any]).Length()
|
||||||
|
|
||||||
case val.ty.IsMapType():
|
case val.ty.IsMapType():
|
||||||
return len(val.v.(map[string]interface{}))
|
return len(val.v.(map[string]any))
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic("value is not a collection")
|
panic("value is not a collection")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Elements returns an iterable sequence over the elements of the reciever,
|
||||||
|
// which must be a collection type, a tuple type, or an object type.
|
||||||
|
// If called on a value of any other type, this method will panic.
|
||||||
|
// The value must also be known, non-null, and unmarked, or this method will
|
||||||
|
// panic.
|
||||||
|
//
|
||||||
|
// Use [Value.CanIterateElements] to check dynamically if a particular value
|
||||||
|
// can support this method without panicking.
|
||||||
|
//
|
||||||
|
// The two values in each iteration represent a key and a value respectively.
|
||||||
|
//
|
||||||
|
// If the receiver is of list type then the key is guaranteed to be of type
|
||||||
|
// [Number] and the values are of the list's element type.
|
||||||
|
//
|
||||||
|
// The the reciever is of a map type then the key is guaranteed to be of type
|
||||||
|
// [String] and the values are of the map's element type. Elements are
|
||||||
|
// produced in ascending lexicographical order by key.
|
||||||
|
//
|
||||||
|
// If the receiver is of a set type then each element is returned as both the
|
||||||
|
// key and the value, because set member values are their own identity.
|
||||||
|
//
|
||||||
|
// If the reciever is of a tuple type then the key is guaranteed to be of type
|
||||||
|
// [Number] and the and the value types match the corresponding element types.
|
||||||
|
//
|
||||||
|
// If the reciever is of an object type then the key is guaranteed to be of
|
||||||
|
// type [String] and the value types match the corresponding attribute types.
|
||||||
|
func (val Value) Elements() iter.Seq2[Value, Value] {
|
||||||
|
return func(yield func(Value, Value) bool) {
|
||||||
|
for it := val.ElementIterator(); it.Next(); {
|
||||||
|
if !yield(it.Element()) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ElementIterator returns an ElementIterator for iterating the elements
|
// ElementIterator returns an ElementIterator for iterating the elements
|
||||||
// of the receiver, which must be a collection type, a tuple type, or an object
|
// of the receiver, which must be a collection type, a tuple type, or an object
|
||||||
// type. If called on a method of any other type, this method will panic.
|
// type. If called on a method of any other type, this method will panic.
|
||||||
|
// The value must be known and non-null, or this method will panic.
|
||||||
//
|
//
|
||||||
// The value must be Known and non-Null, or this method will panic.
|
// The element iterator produces keys and values matching what's described
|
||||||
//
|
// for [Value.Elements]. New code should prefer to use [Value.Elements].
|
||||||
// If the receiver is of a list type, the returned keys will be of type Number
|
|
||||||
// and the values will be of the list's element type.
|
|
||||||
//
|
|
||||||
// If the receiver is of a map type, the returned keys will be of type String
|
|
||||||
// and the value will be of the map's element type. Elements are passed in
|
|
||||||
// ascending lexicographical order by key.
|
|
||||||
//
|
|
||||||
// If the receiver is of a set type, each element is returned as both the
|
|
||||||
// key and the value, since set members are their own identity.
|
|
||||||
//
|
|
||||||
// If the receiver is of a tuple type, the returned keys will be of type Number
|
|
||||||
// and the value will be of the corresponding element's type.
|
|
||||||
//
|
|
||||||
// If the receiver is of an object type, the returned keys will be of type
|
|
||||||
// String and the value will be of the corresponding attributes's type.
|
|
||||||
//
|
|
||||||
// ElementIterator is an integration method, so it cannot handle Unknown
|
|
||||||
// values. This method will panic if the receiver is Unknown.
|
|
||||||
func (val Value) ElementIterator() ElementIterator {
|
func (val Value) ElementIterator() ElementIterator {
|
||||||
val.assertUnmarked()
|
val.assertUnmarked()
|
||||||
if !val.IsKnown() {
|
if !val.IsKnown() {
|
||||||
@@ -1222,7 +1242,7 @@ func (val Value) ElementIterator() ElementIterator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CanIterateElements returns true if the receiver can support the
|
// CanIterateElements returns true if the receiver can support the
|
||||||
// ElementIterator method (and by extension, ForEachElement) without panic.
|
// Elements, ElementIterator, and ForEachElement methods without panic.
|
||||||
func (val Value) CanIterateElements() bool {
|
func (val Value) CanIterateElements() bool {
|
||||||
return canElementIterator(val)
|
return canElementIterator(val)
|
||||||
}
|
}
|
||||||
@@ -1232,7 +1252,9 @@ func (val Value) CanIterateElements() bool {
|
|||||||
// will panic.
|
// will panic.
|
||||||
//
|
//
|
||||||
// ForEachElement uses ElementIterator internally, and so the values passed
|
// ForEachElement uses ElementIterator internally, and so the values passed
|
||||||
// to the callback are as described for ElementIterator.
|
// to the callback are as described for [Value.Elements]. New code should
|
||||||
|
// prefer to use [Value.Elements] in a normal for loop instead of using this
|
||||||
|
// method.
|
||||||
//
|
//
|
||||||
// Returns true if the iteration exited early due to the callback function
|
// Returns true if the iteration exited early due to the callback function
|
||||||
// returning true, or false if the loop ran to completion.
|
// returning true, or false if the loop ran to completion.
|
||||||
@@ -1515,7 +1537,7 @@ func (val Value) AsValueSet() ValueSet {
|
|||||||
// The result is the same pointer that was passed to CapsuleVal to create
|
// The result is the same pointer that was passed to CapsuleVal to create
|
||||||
// the value. Since cty considers values to be immutable, it is strongly
|
// the value. Since cty considers values to be immutable, it is strongly
|
||||||
// recommended to treat the encapsulated value itself as immutable too.
|
// recommended to treat the encapsulated value itself as immutable too.
|
||||||
func (val Value) EncapsulatedValue() interface{} {
|
func (val Value) EncapsulatedValue() any {
|
||||||
val.assertUnmarked()
|
val.assertUnmarked()
|
||||||
if !val.Type().IsCapsuleType() {
|
if !val.Type().IsCapsuleType() {
|
||||||
panic("not a capsule-typed value")
|
panic("not a capsule-typed value")
|
||||||
|
|||||||
+28
@@ -1,11 +1,19 @@
|
|||||||
package cty
|
package cty
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"iter"
|
||||||
|
)
|
||||||
|
|
||||||
// Walk visits all of the values in a possibly-complex structure, calling
|
// Walk visits all of the values in a possibly-complex structure, calling
|
||||||
// a given function for each value.
|
// a given function for each value.
|
||||||
//
|
//
|
||||||
// For example, given a list of strings the callback would first be called
|
// For example, given a list of strings the callback would first be called
|
||||||
// with the whole list and then called once for each element of the list.
|
// with the whole list and then called once for each element of the list.
|
||||||
//
|
//
|
||||||
|
// New callers may prefer to use [DeepValues] instead, because that returns
|
||||||
|
// a result usable with a normal for loop.
|
||||||
|
//
|
||||||
// The callback function may prevent recursive visits to child values by
|
// The callback function may prevent recursive visits to child values by
|
||||||
// returning false. The callback function my halt the walk altogether by
|
// returning false. The callback function my halt the walk altogether by
|
||||||
// returning a non-nil error. If the returned error is about the element
|
// returning a non-nil error. If the returned error is about the element
|
||||||
@@ -19,6 +27,26 @@ func Walk(val Value, cb func(Path, Value) (bool, error)) error {
|
|||||||
return walk(path, val, cb)
|
return walk(path, val, cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepValues returns an iterable sequence containing at least the given
|
||||||
|
// value but also, if it is of a collection or structural type, the other values
|
||||||
|
// nested within it recursively.
|
||||||
|
//
|
||||||
|
// The [Path] values in different elements of the sequence may share a backing
|
||||||
|
// array to reduce garbage generated during iteration, so if a caller wishes to
|
||||||
|
// preserve a path outside of a single loop iteration the caller must copy it to
|
||||||
|
// a separate Path value with its own separate backing array, such as by calling
|
||||||
|
// [Path.Copy].
|
||||||
|
func DeepValues(val Value) iter.Seq2[Path, Value] {
|
||||||
|
return func(yield func(Path, Value) bool) {
|
||||||
|
Walk(val, func(p Path, v Value) (bool, error) {
|
||||||
|
if !yield(p, v) {
|
||||||
|
return false, io.EOF // arbitrary error just to get Walk to stop
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func walk(path Path, val Value, cb func(Path, Value) (bool, error)) error {
|
func walk(path Path, val Value, cb func(Path, Value) (bool, error)) error {
|
||||||
deeper, err := cb(path, val)
|
deeper, err := cb(path, val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+187
@@ -0,0 +1,187 @@
|
|||||||
|
// Copyright 2024 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build go1.24
|
||||||
|
|
||||||
|
package ssh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/mlkem"
|
||||||
|
"crypto/sha256"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"runtime"
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/curve25519"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
kexAlgoMLKEM768xCurve25519SHA256 = "mlkem768x25519-sha256"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// After Go 1.24rc1 mlkem swapped the order of return values of Encapsulate.
|
||||||
|
// See #70950.
|
||||||
|
if runtime.Version() == "go1.24rc1" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
supportedKexAlgos = slices.Insert(supportedKexAlgos, 0, kexAlgoMLKEM768xCurve25519SHA256)
|
||||||
|
preferredKexAlgos = slices.Insert(preferredKexAlgos, 0, kexAlgoMLKEM768xCurve25519SHA256)
|
||||||
|
kexAlgoMap[kexAlgoMLKEM768xCurve25519SHA256] = &mlkem768WithCurve25519sha256{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mlkem768WithCurve25519sha256 implements the hybrid ML-KEM768 with
|
||||||
|
// curve25519-sha256 key exchange method, as described by
|
||||||
|
// draft-kampanakis-curdle-ssh-pq-ke-05 section 2.3.3.
|
||||||
|
type mlkem768WithCurve25519sha256 struct{}
|
||||||
|
|
||||||
|
func (kex *mlkem768WithCurve25519sha256) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) {
|
||||||
|
var c25519kp curve25519KeyPair
|
||||||
|
if err := c25519kp.generate(rand); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
seed := make([]byte, mlkem.SeedSize)
|
||||||
|
if _, err := io.ReadFull(rand, seed); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mlkemDk, err := mlkem.NewDecapsulationKey768(seed)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
hybridKey := append(mlkemDk.EncapsulationKey().Bytes(), c25519kp.pub[:]...)
|
||||||
|
if err := c.writePacket(Marshal(&kexECDHInitMsg{hybridKey})); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
packet, err := c.readPacket()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var reply kexECDHReplyMsg
|
||||||
|
if err = Unmarshal(packet, &reply); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(reply.EphemeralPubKey) != mlkem.CiphertextSize768+32 {
|
||||||
|
return nil, errors.New("ssh: peer's mlkem768x25519 public value has wrong length")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform KEM decapsulate operation to obtain shared key from ML-KEM.
|
||||||
|
mlkem768Secret, err := mlkemDk.Decapsulate(reply.EphemeralPubKey[:mlkem.CiphertextSize768])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Complete Curve25519 ECDH to obtain its shared key.
|
||||||
|
c25519Secret, err := curve25519.X25519(c25519kp.priv[:], reply.EphemeralPubKey[mlkem.CiphertextSize768:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("ssh: peer's mlkem768x25519 public value is not valid: %w", err)
|
||||||
|
}
|
||||||
|
// Compute actual shared key.
|
||||||
|
h := sha256.New()
|
||||||
|
h.Write(mlkem768Secret)
|
||||||
|
h.Write(c25519Secret)
|
||||||
|
secret := h.Sum(nil)
|
||||||
|
|
||||||
|
h.Reset()
|
||||||
|
magics.write(h)
|
||||||
|
writeString(h, reply.HostKey)
|
||||||
|
writeString(h, hybridKey)
|
||||||
|
writeString(h, reply.EphemeralPubKey)
|
||||||
|
|
||||||
|
K := make([]byte, stringLength(len(secret)))
|
||||||
|
marshalString(K, secret)
|
||||||
|
h.Write(K)
|
||||||
|
|
||||||
|
return &kexResult{
|
||||||
|
H: h.Sum(nil),
|
||||||
|
K: K,
|
||||||
|
HostKey: reply.HostKey,
|
||||||
|
Signature: reply.Signature,
|
||||||
|
Hash: crypto.SHA256,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kex *mlkem768WithCurve25519sha256) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv AlgorithmSigner, algo string) (*kexResult, error) {
|
||||||
|
packet, err := c.readPacket()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var kexInit kexECDHInitMsg
|
||||||
|
if err = Unmarshal(packet, &kexInit); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(kexInit.ClientPubKey) != mlkem.EncapsulationKeySize768+32 {
|
||||||
|
return nil, errors.New("ssh: peer's ML-KEM768/curve25519 public value has wrong length")
|
||||||
|
}
|
||||||
|
|
||||||
|
encapsulationKey, err := mlkem.NewEncapsulationKey768(kexInit.ClientPubKey[:mlkem.EncapsulationKeySize768])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("ssh: peer's ML-KEM768 encapsulation key is not valid: %w", err)
|
||||||
|
}
|
||||||
|
// Perform KEM encapsulate operation to obtain ciphertext and shared key.
|
||||||
|
mlkem768Secret, mlkem768Ciphertext := encapsulationKey.Encapsulate()
|
||||||
|
|
||||||
|
// Perform server side of Curve25519 ECDH to obtain server public value and
|
||||||
|
// shared key.
|
||||||
|
var c25519kp curve25519KeyPair
|
||||||
|
if err := c25519kp.generate(rand); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c25519Secret, err := curve25519.X25519(c25519kp.priv[:], kexInit.ClientPubKey[mlkem.EncapsulationKeySize768:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("ssh: peer's ML-KEM768/curve25519 public value is not valid: %w", err)
|
||||||
|
}
|
||||||
|
hybridKey := append(mlkem768Ciphertext, c25519kp.pub[:]...)
|
||||||
|
|
||||||
|
// Compute actual shared key.
|
||||||
|
h := sha256.New()
|
||||||
|
h.Write(mlkem768Secret)
|
||||||
|
h.Write(c25519Secret)
|
||||||
|
secret := h.Sum(nil)
|
||||||
|
|
||||||
|
hostKeyBytes := priv.PublicKey().Marshal()
|
||||||
|
|
||||||
|
h.Reset()
|
||||||
|
magics.write(h)
|
||||||
|
writeString(h, hostKeyBytes)
|
||||||
|
writeString(h, kexInit.ClientPubKey)
|
||||||
|
writeString(h, hybridKey)
|
||||||
|
|
||||||
|
K := make([]byte, stringLength(len(secret)))
|
||||||
|
marshalString(K, secret)
|
||||||
|
h.Write(K)
|
||||||
|
|
||||||
|
H := h.Sum(nil)
|
||||||
|
|
||||||
|
sig, err := signAndMarshal(priv, rand, H, algo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
reply := kexECDHReplyMsg{
|
||||||
|
EphemeralPubKey: hybridKey,
|
||||||
|
HostKey: hostKeyBytes,
|
||||||
|
Signature: sig,
|
||||||
|
}
|
||||||
|
if err := c.writePacket(Marshal(&reply)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &kexResult{
|
||||||
|
H: H,
|
||||||
|
K: K,
|
||||||
|
HostKey: hostKeyBytes,
|
||||||
|
Signature: sig,
|
||||||
|
Hash: crypto.SHA256,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
+58
-10
@@ -6,6 +6,7 @@ package term
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -36,6 +37,26 @@ var vt100EscapeCodes = EscapeCodes{
|
|||||||
Reset: []byte{keyEscape, '[', '0', 'm'},
|
Reset: []byte{keyEscape, '[', '0', 'm'},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A History provides a (possibly bounded) queue of input lines read by [Terminal.ReadLine].
|
||||||
|
type History interface {
|
||||||
|
// Add will be called by [Terminal.ReadLine] to add
|
||||||
|
// a new, most recent entry to the history.
|
||||||
|
// It is allowed to drop any entry, including
|
||||||
|
// the entry being added (e.g., if it's deemed an invalid entry),
|
||||||
|
// the least-recent entry (e.g., to keep the history bounded),
|
||||||
|
// or any other entry.
|
||||||
|
Add(entry string)
|
||||||
|
|
||||||
|
// Len returns the number of entries in the history.
|
||||||
|
Len() int
|
||||||
|
|
||||||
|
// At returns an entry from the history.
|
||||||
|
// Index 0 is the most-recently added entry and
|
||||||
|
// index Len()-1 is the least-recently added entry.
|
||||||
|
// If index is < 0 or >= Len(), it panics.
|
||||||
|
At(idx int) string
|
||||||
|
}
|
||||||
|
|
||||||
// Terminal contains the state for running a VT100 terminal that is capable of
|
// Terminal contains the state for running a VT100 terminal that is capable of
|
||||||
// reading lines of input.
|
// reading lines of input.
|
||||||
type Terminal struct {
|
type Terminal struct {
|
||||||
@@ -86,9 +107,14 @@ type Terminal struct {
|
|||||||
remainder []byte
|
remainder []byte
|
||||||
inBuf [256]byte
|
inBuf [256]byte
|
||||||
|
|
||||||
// history contains previously entered commands so that they can be
|
// History records and retrieves lines of input read by [ReadLine] which
|
||||||
// accessed with the up and down keys.
|
// a user can retrieve and navigate using the up and down arrow keys.
|
||||||
history stRingBuffer
|
//
|
||||||
|
// It is not safe to call ReadLine concurrently with any methods on History.
|
||||||
|
//
|
||||||
|
// [NewTerminal] sets this to a default implementation that records the
|
||||||
|
// last 100 lines of input.
|
||||||
|
History History
|
||||||
// historyIndex stores the currently accessed history entry, where zero
|
// historyIndex stores the currently accessed history entry, where zero
|
||||||
// means the immediately previous entry.
|
// means the immediately previous entry.
|
||||||
historyIndex int
|
historyIndex int
|
||||||
@@ -111,6 +137,7 @@ func NewTerminal(c io.ReadWriter, prompt string) *Terminal {
|
|||||||
termHeight: 24,
|
termHeight: 24,
|
||||||
echo: true,
|
echo: true,
|
||||||
historyIndex: -1,
|
historyIndex: -1,
|
||||||
|
History: &stRingBuffer{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,6 +477,23 @@ func visualLength(runes []rune) int {
|
|||||||
return length
|
return length
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// histroryAt unlocks the terminal and relocks it while calling History.At.
|
||||||
|
func (t *Terminal) historyAt(idx int) (string, bool) {
|
||||||
|
t.lock.Unlock() // Unlock to avoid deadlock if History methods use the output writer.
|
||||||
|
defer t.lock.Lock() // panic in At (or Len) protection.
|
||||||
|
if idx < 0 || idx >= t.History.Len() {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return t.History.At(idx), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// historyAdd unlocks the terminal and relocks it while calling History.Add.
|
||||||
|
func (t *Terminal) historyAdd(entry string) {
|
||||||
|
t.lock.Unlock() // Unlock to avoid deadlock if History methods use the output writer.
|
||||||
|
defer t.lock.Lock() // panic in Add protection.
|
||||||
|
t.History.Add(entry)
|
||||||
|
}
|
||||||
|
|
||||||
// handleKey processes the given key and, optionally, returns a line of text
|
// handleKey processes the given key and, optionally, returns a line of text
|
||||||
// that the user has entered.
|
// that the user has entered.
|
||||||
func (t *Terminal) handleKey(key rune) (line string, ok bool) {
|
func (t *Terminal) handleKey(key rune) (line string, ok bool) {
|
||||||
@@ -497,7 +541,7 @@ func (t *Terminal) handleKey(key rune) (line string, ok bool) {
|
|||||||
t.pos = len(t.line)
|
t.pos = len(t.line)
|
||||||
t.moveCursorToPos(t.pos)
|
t.moveCursorToPos(t.pos)
|
||||||
case keyUp:
|
case keyUp:
|
||||||
entry, ok := t.history.NthPreviousEntry(t.historyIndex + 1)
|
entry, ok := t.historyAt(t.historyIndex + 1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
@@ -516,7 +560,7 @@ func (t *Terminal) handleKey(key rune) (line string, ok bool) {
|
|||||||
t.setLine(runes, len(runes))
|
t.setLine(runes, len(runes))
|
||||||
t.historyIndex--
|
t.historyIndex--
|
||||||
default:
|
default:
|
||||||
entry, ok := t.history.NthPreviousEntry(t.historyIndex - 1)
|
entry, ok := t.historyAt(t.historyIndex - 1)
|
||||||
if ok {
|
if ok {
|
||||||
t.historyIndex--
|
t.historyIndex--
|
||||||
runes := []rune(entry)
|
runes := []rune(entry)
|
||||||
@@ -781,7 +825,7 @@ func (t *Terminal) readLine() (line string, err error) {
|
|||||||
if lineOk {
|
if lineOk {
|
||||||
if t.echo {
|
if t.echo {
|
||||||
t.historyIndex = -1
|
t.historyIndex = -1
|
||||||
t.history.Add(line)
|
t.historyAdd(line)
|
||||||
}
|
}
|
||||||
if lineIsPasted {
|
if lineIsPasted {
|
||||||
err = ErrPasteIndicator
|
err = ErrPasteIndicator
|
||||||
@@ -938,19 +982,23 @@ func (s *stRingBuffer) Add(a string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NthPreviousEntry returns the value passed to the nth previous call to Add.
|
func (s *stRingBuffer) Len() int {
|
||||||
|
return s.size
|
||||||
|
}
|
||||||
|
|
||||||
|
// At returns the value passed to the nth previous call to Add.
|
||||||
// If n is zero then the immediately prior value is returned, if one, then the
|
// If n is zero then the immediately prior value is returned, if one, then the
|
||||||
// next most recent, and so on. If such an element doesn't exist then ok is
|
// next most recent, and so on. If such an element doesn't exist then ok is
|
||||||
// false.
|
// false.
|
||||||
func (s *stRingBuffer) NthPreviousEntry(n int) (value string, ok bool) {
|
func (s *stRingBuffer) At(n int) string {
|
||||||
if n < 0 || n >= s.size {
|
if n < 0 || n >= s.size {
|
||||||
return "", false
|
panic(fmt.Sprintf("term: history index [%d] out of range [0,%d)", n, s.size))
|
||||||
}
|
}
|
||||||
index := s.head - n
|
index := s.head - n
|
||||||
if index < 0 {
|
if index < 0 {
|
||||||
index += s.max
|
index += s.max
|
||||||
}
|
}
|
||||||
return s.entries[index], true
|
return s.entries[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
// readPasswordLine reads from reader until it finds \n or io.EOF.
|
// readPasswordLine reads from reader until it finds \n or io.EOF.
|
||||||
|
|||||||
Vendored
+12
-11
@@ -385,8 +385,8 @@ github.com/hashicorp/errwrap
|
|||||||
# github.com/hashicorp/go-cleanhttp v0.5.2
|
# github.com/hashicorp/go-cleanhttp v0.5.2
|
||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
github.com/hashicorp/go-cleanhttp
|
github.com/hashicorp/go-cleanhttp
|
||||||
# github.com/hashicorp/go-cty-funcs v0.0.0-20250210171435-dda779884a9f
|
# github.com/hashicorp/go-cty-funcs v0.0.0-20250818135842-6aab67130928
|
||||||
## explicit; go 1.23
|
## explicit; go 1.24
|
||||||
github.com/hashicorp/go-cty-funcs/cidr
|
github.com/hashicorp/go-cty-funcs/cidr
|
||||||
github.com/hashicorp/go-cty-funcs/crypto
|
github.com/hashicorp/go-cty-funcs/crypto
|
||||||
github.com/hashicorp/go-cty-funcs/encoding
|
github.com/hashicorp/go-cty-funcs/encoding
|
||||||
@@ -394,8 +394,8 @@ github.com/hashicorp/go-cty-funcs/uuid
|
|||||||
# github.com/hashicorp/go-multierror v1.1.1
|
# github.com/hashicorp/go-multierror v1.1.1
|
||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
github.com/hashicorp/go-multierror
|
github.com/hashicorp/go-multierror
|
||||||
# github.com/hashicorp/hcl/v2 v2.23.0
|
# github.com/hashicorp/hcl/v2 v2.24.0
|
||||||
## explicit; go 1.18
|
## explicit; go 1.23.0
|
||||||
github.com/hashicorp/hcl/v2
|
github.com/hashicorp/hcl/v2
|
||||||
github.com/hashicorp/hcl/v2/ext/customdecode
|
github.com/hashicorp/hcl/v2/ext/customdecode
|
||||||
github.com/hashicorp/hcl/v2/ext/tryfunc
|
github.com/hashicorp/hcl/v2/ext/tryfunc
|
||||||
@@ -442,8 +442,8 @@ github.com/mattn/go-runewidth
|
|||||||
# github.com/mattn/go-shellwords v1.0.12
|
# github.com/mattn/go-shellwords v1.0.12
|
||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
github.com/mattn/go-shellwords
|
github.com/mattn/go-shellwords
|
||||||
# github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7
|
# github.com/mitchellh/go-wordwrap v1.0.1
|
||||||
## explicit
|
## explicit; go 1.14
|
||||||
github.com/mitchellh/go-wordwrap
|
github.com/mitchellh/go-wordwrap
|
||||||
# github.com/mitchellh/hashstructure/v2 v2.0.2
|
# github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||||
## explicit; go 1.14
|
## explicit; go 1.14
|
||||||
@@ -691,10 +691,11 @@ github.com/x448/float16
|
|||||||
# github.com/xhit/go-str2duration/v2 v2.1.0
|
# github.com/xhit/go-str2duration/v2 v2.1.0
|
||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
github.com/xhit/go-str2duration/v2
|
github.com/xhit/go-str2duration/v2
|
||||||
# github.com/zclconf/go-cty v1.16.2
|
# github.com/zclconf/go-cty v1.17.0
|
||||||
## explicit; go 1.18
|
## explicit; go 1.23
|
||||||
github.com/zclconf/go-cty/cty
|
github.com/zclconf/go-cty/cty
|
||||||
github.com/zclconf/go-cty/cty/convert
|
github.com/zclconf/go-cty/cty/convert
|
||||||
|
github.com/zclconf/go-cty/cty/ctymarks
|
||||||
github.com/zclconf/go-cty/cty/ctystrings
|
github.com/zclconf/go-cty/cty/ctystrings
|
||||||
github.com/zclconf/go-cty/cty/function
|
github.com/zclconf/go-cty/cty/function
|
||||||
github.com/zclconf/go-cty/cty/function/stdlib
|
github.com/zclconf/go-cty/cty/function/stdlib
|
||||||
@@ -811,7 +812,7 @@ go.opentelemetry.io/proto/otlp/trace/v1
|
|||||||
# go.yaml.in/yaml/v3 v3.0.4
|
# go.yaml.in/yaml/v3 v3.0.4
|
||||||
## explicit; go 1.16
|
## explicit; go 1.16
|
||||||
go.yaml.in/yaml/v3
|
go.yaml.in/yaml/v3
|
||||||
# golang.org/x/crypto v0.37.0
|
# golang.org/x/crypto v0.38.0
|
||||||
## explicit; go 1.23.0
|
## explicit; go 1.23.0
|
||||||
golang.org/x/crypto/bcrypt
|
golang.org/x/crypto/bcrypt
|
||||||
golang.org/x/crypto/blowfish
|
golang.org/x/crypto/blowfish
|
||||||
@@ -857,10 +858,10 @@ golang.org/x/sys/plan9
|
|||||||
golang.org/x/sys/unix
|
golang.org/x/sys/unix
|
||||||
golang.org/x/sys/windows
|
golang.org/x/sys/windows
|
||||||
golang.org/x/sys/windows/registry
|
golang.org/x/sys/windows/registry
|
||||||
# golang.org/x/term v0.31.0
|
# golang.org/x/term v0.32.0
|
||||||
## explicit; go 1.23.0
|
## explicit; go 1.23.0
|
||||||
golang.org/x/term
|
golang.org/x/term
|
||||||
# golang.org/x/text v0.24.0
|
# golang.org/x/text v0.25.0
|
||||||
## explicit; go 1.23.0
|
## explicit; go 1.23.0
|
||||||
golang.org/x/text/cases
|
golang.org/x/text/cases
|
||||||
golang.org/x/text/feature/plural
|
golang.org/x/text/feature/plural
|
||||||
|
|||||||
Reference in New Issue
Block a user